cult3

Trailblazer - A New Architecture For Rails [Review]

Apr 27, 2016

Table of contents:

  1. What is Trailblazer?
  2. Thin Models and Thin Controllers
  3. Operations encapsulate actions
  4. Forms deal with validation
  5. Say goodbye to Callback Hell
  6. Policies for Authorisation
  7. View Models add a widget architecture
  8. Parse and render documents with Representers
  9. Conclusion

I really love the foundational philosophy of Ruby on Rails. The framework provides a wealth of amazing tools to help you build better applications, quicker, all whilst making the process incredibly fun and rewarding.

When you create a new Rails project, you can have a fully functional Minimal Viable Product in a very short amount of time. This is an incredible enabler when you want to quickly go from idea to a working prototype.

Rails provides you with tools and conventions, and aims to avoid unnecessary abstraction or indirection. Once you are familiar with “The Rails Way” it makes understanding a Rails project very easy.

However, I find once an application reaches a certain level of complexity, a higher layer of abstraction is inevitable. Rails does not provide tools to deal with this abstraction and so you will often see large Rails projects spiral into a ball of mud.

Trailblazer is a “framework” for dealing with the inevitable complexity of a Rails project. By providing a set of tools for adding abstraction, Trailblazer sets a common standard and a methodology for dealing with complexity, without you having to reinvent the wheel for yourself.

Trailblazer is created by Nick Sutterer. Nick is a long time Ruby developer who has been working on Trailblazer for over 10 years! Nick has also written a book to accompany Trailblazer, which will be the subject of this review.

What is Trailblazer?

Trailblazer is a set of Ruby gems that aim to deal with the inevitable abstraction that will be required once a Rails project moves beyond a simple application.

The gems form a thin layer that sit on top of Rails, but are completely decoupled from the framework and from each other. You are free to use one or more gems from Trailblazer, without having to pull in a large amount of extra overhead. Each of the gems provides an opinionated way of dealing with the common actions of a typical web application.

Trailblazer guides you to move your business logic from the Models and Controllers and into specific objects that should be responsible for that concern. These objects use encapsulation and Object-oriented Programming to maximize reusability.

Trailblazer also aims to solve some of the problems of Rails’ lack of abstraction when dealing with common requirements such as dealing with different request types, authorisation policies, and view logic.

The following is an overview of the major components of Trailblazer.

Thin Models and Thin Controllers

The first big thing to note in Trailblazer is that both your Models and Controllers should be very thin. This is instantly a big departure from the typical Rails project where either the Model, the Controller, or both end up being very fat with business logic.

A Controller in Trailblazer is a very lean HTTP endpoint that dispatches a request to an Operation. Controllers do not have any business logic. The Controller’s responsibility is restricted to HTTP responsibilities only.

A Model in Trailblazer holds the details of the associations between other models and provides a way to query the database. Active Record is very good at modeling associations and querying the database, and so those are the Model’s only responsibilities. Models should not contain any validations, callbacks, or business logic.

Operations encapsulate actions

Your main interaction with Trailblazer is through Operations. An Operation encapsulates the process of an action of your application. For example, a blogging application would have an Article::Create Operation.

At first glance Operations in Trailblazer seem to have a lot of responsibility. Operations deal with validation, callbacks, authorisation, and business logic. However, the Operation’s job is simply to coordinate the underlying collaborators and provide you with a simple API, it does not actually have all of these responsibilities.

Typically when you have a create and an update action, the business rules will be similar, but not quite the same. Trailblazer heavily uses inheritance to reduce duplication whilst making it easy to create specific rules for authorisation or validation for that specific use-case.

You will also probably have different business rules for certain actions. For example, perhaps any user can create an article, but only the author of the article or an admin can edit an article once it has been published. Trailblazer promotes polymorphism to deal with these different contexts using subclasses, rather than messy if``else statements.

Forms deal with validation

As I mentioned above, you define the validation rules for an action in an Operation, but it’s not the Operation’s responsibility to enforce those rules.

Under the hood the Operation is using Reform. We’ve previously been using Reform in Using Form Objects in Ruby on Rails with Reform and Creating a Sign Up Form flow in Ruby on Rails Part 2 without using any other components of Trailblazer, which shows how decoupled the Trailblazer gems are from Rails and from each other!

Say goodbye to Callback Hell

One of the most common problems with growing Rails projects is falling into Callback Hell. Active Record executes callbacks during the lifecycle of an object. These callbacks can be really useful to hook into to perform a given action, but it’s often a slippery slope to Callback Hell.

Trailblazer provides the functionality to run callbacks inside an Operation. However, callbacks in Operations are simply method calls, there really isn’t any magic.

And the fact that you are defining the callback in the Operation, rather than the Model itself will prevent any callbacks from running in the wrong context!

Policies for Authorisation

Authorisation rules are an important part of just about any application. Trailblazer gives you a simple way to define authorisation policies in much the same way that the very popular Pundit gem deals with it.

However, Trailblazer also leverages polymorphism to encapsulate specific actions in a subclass of the Operation. The class that is executing the Operation (typically the Controller) will not have to deal with any messy if statements to run a particular action for a given authorisation context.

View Models add a widget architecture

View Models in Trailblazer are called Cells and provide encapsulation for your User Interface. Views no longer have logic or are swimming in an ocean of helper functions as Trailblazer introduces a widget architecture.

This greatly simplifies the views of the application.

Parse and render documents with Representers

Finally, it is often the case that you need to accept or respond with JSON or XML documents, not just an HTML user interface.

Representers provide a way to specify the response document structure to conform to certain standards such as HAL or JSON API.

Representers also parse documents into an object graph, rather than the naive hash structure that Rails provides you with by default.

Conclusion

I actually heard about Trailblazer quite a while ago, but I initially dismissed it as unnecessary abstraction and indirection. A lot of web frameworks sell you on the idea that you don’t need abstraction and indirection with simple examples and well documented use-cases.

However, it is often the case that you do need a higher layer of abstraction in order to deal with the complexity of non-trivial applications. If you don’t deal with that abstraction explicitly, one day you will wake up and find that you’ve created a mess of abstraction and indirection through a thousand cuts.

In previous web applications that I’ve worked on, I’ve created abstractions that deal with a lot of the same things that Trailblazer is looking to solve. I found myself nodding along and thinking back to situations where I’ve required the exact abstraction Nick was describing.

However, none of my solutions have ever been as elegant or as intricate as Trailblazer. There has clearly been a lot of hard work and considered design decisions over the years to make Trailblazer what it is today!

Once I had cast away my initial skepticism and I really started to dig into Trailblazer to understand the problems that it was solving I was almost immediately sold on the idea. Typically I will always try to use an Open Source version of a tool because the author has inevitably already stumbled across the edge-cases that I’m yet to suffer through.

I also have to really commend Nick on his book. A lot of ebooks are half-baked, particular programming books. I often find that programming ebooks lack structure and have seemingly unrelated chapters that I suspect are included to boost the page count and justify the price.

Trailblazer (the book) follows a clear structure of Nick developing an application using Trailblazer (the framework) and Ruby on Rails. Nick walks you through the main concepts of Trailblazer and then shows you how to implement them. He also covers a number of common problems that you will stumble across when developing a web application and he shows you how Trailblazer can deal with them for you.

If you are looking to build web applications in Ruby, but you are fatigued with dealing with the inevitable complexity, I would highly recommend taking a look at Trailblazer. Rails deals with the underlying infrastructure, whilst Trailblazer provide you with the next step for going beyond a simple tutorial.

And if you want to skip ahead and accelerate your learning, I would highly recommend buying the book. It’s a great read and it clearly demonstrates the power of Trailblazer.

Buy Trailblazer on Learnpub

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.