The trail we blaze
hOLA!
hOLA!
A little bit about me
I'm a full stack developer
I'm from argentina
I work for
NO, not
@moove_it
/moove-it
You can find me on the internet
@aragno157
/lucas-aragno
An introduction
I was a freelance developer
I worked in ^10 apps in the same year
Train derailment and subsequent unstoppable fireball
laragno$ cat app/controllers/application_controller.rb | wc -l
569
laragno$
fat models thin controllers!
laragno$ cat app/models/user.rb | wc -l
571
laragno$
Why???
So we need to fix this
Service objects to the rescue!
be careful...
SERVICE OBJECTS SHOULD
- Show what your app does
- Be easy/fast to test
- Model domain behavior
- Perform actions with side effects
- Don't respond anything ( tell don't ask )
- Be rails independent
MVC IS DEAD, IT'S TIME TO MOVE ON
@conradirwin
MVC
-
You have models, which are nice self-contained bits of state
-
You have Views which are nice self-contained bits of UI
-
You have Controllers which are nice self-contained bits of …
Model
Operation
View
Event
oPERATION
They are responsible for making changes to your models, for showing the right views at the right time, and for responding to events triggered by user interactions. In a well factored application, each sub-operation can be run independently of its parent.
EVENT
Listening on events is what gives MOVE (and MVC) the inversion of control that you need to allow models to update views without the models being directly aware of which views they are updating. This is a powerful abstraction technique, allowing components to be coupled together without interfering with each other.
Trailblazer
created by @apotonick
└── app
├── concepts
└── speaker
├── cell
│ ├── index.rb
│ └── show.rb
├── contract
│ ├── create.rb
│ └── update.rb
├── operation
│ ├── create.rb
│ └── update.rb
└── view
├── index.haml
└── show.haml
Controllers
- Controllers are lean endpoints for HTTP
- Actions are meant only to dispatch an operation
Controllers
class SpeakersController < ApplicationController
def create
Speakers::Create.(params)
end
end
Controllers
class SpeakersController < ApplicationController
def create
run Speaker::Create do |op|
return redirect_to(speaker_path op.model)
end
render :new
end
end
Operations
- Operations encapsulate business logic
-
It's a simple orchestrator between the form object, models and your business code.
Operations
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
def process(params)
Speaker.create(params)
end
end
end
Operations
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
contract do
property :name, validates: {presence: true}
end
def process(params)
@model = Speaker.new
validate(params, @model) do |f|
f.save!
end
end
end
end
Operations
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
contract Contract::Create
def process(params)
@model = Speaker.new
validate(params, @model) do |f|
f.save!
end
end
end
end
Reform
module Contract
class Create
properties :name
validates :name, presence: true
end
end
Policies
class Speaker::Policy
def initialize(user, speaker)
@user, @speaker = user, speaker
end
def create?
@user.admin?
end
end
Policies
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
include Policy
policy Speaker::Policy, :create?
end
end
callbacks
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
include Callback
# ...
callback(:after_save) do
on_change :markdownize_description!
end
# ...
end
end
callbacks
class Speaker < ActiveRecord::Base
class Create < Trailblazer::Operation
include Callback
# ...
def process(params)
validate(params) do
contract.save
callback!(:after_save)
end
end
def markdownize_description!(speaker)
speaker.description = Markdownize.(speaker.description)
end
end
end
Models
class Speaker < ActiveRecord::Base
belongs_to :conference
scope :first_day, lambda { where(speaks_on_the_first_day: true) }
end
Testing!
describe Speaker::Update do
let(:speaker) { Speaker::Create.(...) }
# do you thing
end
Helpers are not helpful
`The way helpers are being used in Rails encourages users to write “script-like” code using global variables and procedural functions in an environment that calls itself “object-oriented”.` @apotonick
Cells
class SpeakerCell < Cell::ViewModel
property :name
property :description
property :avatar_url
def show
render
end
private
def avatar
image_tag avatar_url
end
end
Cells
<!-- wow so logic less -->
<div class="speaker-box">
<%= avatar %>
<h3><%= name %></h3>
<p>
<%= description %>
</p>
</div>
Cells
<!-- ... -->
<% @speakers.each do |speaker| >
<%= cell(:speaker, speaker) %>
<% end %>
You might not need microservices
Switching to microservices it's not a thing that you can decide from one day to another, maybe a good object design it's all what you need
so why trailblazer?
- Adds new layers where we can store our business logic
- You don't need to re-write everything
- You don't need to use the whole trailblazer stack
- Growing community
- Awesome people
- Kick ass name
final thougths
I
I
(Not as much as Jerry does)
Trailblazer makes me
Refactoring makes me
"Developers have fear of changing the code"
Nick Sutterer
"Don't fear the class"
Bryan Helmkamp
obrigado!
- Gracias!
- Thanks!
thetrailweblaze
By Lucas Aragno
thetrailweblaze
- 1,992