The advanced business logic framework

Trailblazer is an architectural style that provides a modern approach to implementing business logic.

It makes complex application workflows simple, reliable and self-documenting.

OUR TOOLS Give me Code!

Structure

Finally know where to put your code

Trailblazer extends the basic MVC pattern of Rails with new abstractions. We call that a high-level architecture. Rock-solid conventions that go far beyond database table naming or route paths let you focus on your application code, minimize bugs and improve the maintainability across large teams.

Directory Structure
Directory Structure
Trailblazer

The pattern

Let Us Take Care of the Flow Control!

Each function of your application is encapsulated into an activity. This activity structures and orchestrates the necessary steps using a modern workflow approach. Depending on the complexity of your function, a step can range from an ad-hoc Ruby method to another nested activity entirely.

Lifecycles and long-running processes can be implemented by chaining activities in workflows.

Features

Framework agnostic

The Trailblazer gems work with any Ruby framework. We provide glue code for Rails and Hanami, but there are TRB-powered apps in Roda, Grape, Sinatra and many more out there.

Legacy ready

You can start using Trailblazer in existing, massive applications today. Refactorings can be applied step-wise, legacy code can be minimized as you go. Remember: Rome wasn’t build in one day, either.

LTS: LONG-TERM-SUPPORT

Trailblazer is in use in thousands of production applications. That’s why we promise: Trailblazer 2.1 is long-term supported. Easy upgrades and stable internal APIs allow us to innovate and keep you stable.

Build to Refactor

Our patterns are developed to be used in highly complex, existing, messy legacy applications. Trailblazer is designed to refactor old code - you do not have to rewrite the entire system to get a better architecture.

Test first

By restructuring business code, application behavior can be tested more efficiently with more unit and less integration tests. Trailblazer engineers do enjoy the simplicity of testing and the speedup of the test suites.

It’s real!

Trailblazer is in use in thousands of production applications. Our patterns have evolved over a decade of engineering, our gems are mature and battle-tested. And: we will never stop innovating.

Trailblazer Trailblazer

Our tools

A new developer experience

Newly added developer tools like tracing or step-debugging improve your team’s developer experience. Our new tools reduce frustration to a minimum and will blow your mind - in a good way.

Our new visual editor allows modelling complex long-running processes or visualizes existing activities.

Code

Want some code?

CONTROLLER They end up as lean HTTP endpoints. No business logic is to be found in the controller, they instantly delegate to their respective operation.

Oh, and did we say there won’t be controller tests anymore? That’s right. Only unit and integration tests.


class SongsController < ApplicationController
  def create
    run Song::Create do |ctx|
      redirect_to songs_path(ctx[:model])
    end
  end
end

MODEL Models contain associations, scopes and finders. Only persistence logic, no callbacks, no validations, no business logic here.

Instead, business code and callbacks are moved to operations.


class Song < ActiveRecord::Base
  has_many :albums
  belongs_to :composer
end

The OPERATION is the heart of the Trailblazer architecture. It orchestrates validations, policies, models, callback and business logic by leveraging a functional pipeline with built-in error handling.

Designed to be a stateless object, the operation passes around one mutable options hash and makes heavy use of Ruby keyword arguments - if you want it.


class Song::Create < Trailblazer::Operation
  step Model( Song, :new )
  step Policy::Pundit( Application::Policy, :create? )
  step Contract::Build( constant: Song::Contract::Create )
  step Contract::Validate()
  step Contract::Persist()
  fail Notifier::DBError
  pass :update_song_count!

  def update_song_count!(ctx, current_user:, **)
    current_user.increment_song_counter
  end
end

Any number of POLICYs can be used in an operation to grant or deny access to functionality.

Also, use your choice of authorization framework.


class Application::Policy < Pundit::Policy
  def create?
    user.can_create?(model)
  end
end

Validations are implemented with CONTRACT.

Trailblazer supports Reform and Dry::Schema validations in any number.


class Song::Contract::Create < Reform::Form
  property :title
  property :length

  validates :title, presence: true
end

And the best there’s only one way to run an operation. Any dependency, such as the current_user, must be injected from the outside.

The concept of global state does not exist in Trailblazer, which leads to simplified, mock-free testability and concurrent-ready code.


Song::Create.(
  { title: "Roxanne", length: 300 }, # params
  current_user: current_user         # dependencies
)

Clumsy, slow controller tests are history. Now that all your business logic is controlled by the operation, SIMPLE UNIT TESTS can test any edge-case scenario or avoid regressions.

Trailblazer’s encapsulation makes a programmer’s life better.


describe Song::Create do
  it "prohibits empty params" do
    result = Song::Create.({})

    expect(result).to be_failure
    expect(result[:model]).to be_new
  end
end

HAPPY PEOPLE WHO USE TRAILBLAZER

BRYAN "HEC" HEENAN

FOUNDER/DIRECTOR at BRANDSCOPE, INC.

Trailblazer saved our butt! As is typical of a fast growing startup, the demand for quick turnaround of additional features had lead to our code-base becoming unstructured and buggy. Implementing new features was becoming both difficult and dangerous, and performance was starting to suffer. While investigating ways to refactor/rewrite I chanced across Nick and Trailblazer. After extensive research and comparison, we decided to adopt Trailblazer. What a fantastic decision this was.