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

(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