A Post Entitled Qu’ils mangent de la brioche
The phrase attributed to Marie Antoinette often mistranslated into English as “Let them eat cake” seems particularly appropriate in light of the great post by the folks over at Engine Yard. The “cake” that the French noblewoman referred to was really an enriched bread that the upper classes could dine upon but was generally out of the reach of the starving peasants (all the more during a famine!). For the Ruby community today, let’s take it as a challenge to raise our collective game.
In Clean Code, “Uncle Bob” Martin from Object Mentor made these statements about the Rails community’s dominant ORM ActiveRecord.
Active Records are special forms of DTOs. They are data structures with public (or bean-accessed) variables; but they typically have navigational methods like save and find. Typically these Active Records are direct translations from database tables, or other data sources.
Unfortunately we often find that developers try to treat these data structures as though they were objects by putting business rule methods in them. This is awkward because it creates a hybrid between a data structure and an object.
The solution, of course, is to treat the Active Record as a data structure and to create separate objects that contain the business rules and that hide their internal data (which are probably just instances of the Active Record).
Ouch. Martin is generally a pretty sharp guy and when he calls out a specific implementation pattern we should take notice. The statements made me a bit uncomfortable because I am just as addicted to rolling business logic into ARec models as everyone else.
The EY post could be the game changer. The technique they describe for model composition was one they created primarily for speeding up tests. If you haven’t already taken the time to ready the article, they suggest extracting a certain amount of business logic into a namespaced module and then including that into your ARec-based model. The advantage is that you can create mock objects for testing, extend them with your module just like their ARec counterpart, and test them without hitting the database. It’s a simple but brilliant suggestion.
But now consider EY’s approach in light of Martin’s objection to the abuse of the Active Record pattern in general. Extracting the business logic into a separate, testable module achieves the goal of decoupling the DTO (Active Record class) from the business logic that acts upon it. Couldn’t the EY recommendation become the norm for building Rails applications?
One approach along these lines has already been encapsulated into the RailsConcerns plugin. The idea is fairly simple. Organize your Rails project similar to most gems: include both a things.rb and things folder in your app/models. The things.rb will be responsible for including/extending itself using the modules and classes in the things folder. The plugin provides a simple API for the latter. Let’s assume that you have an Employee model and that you break your business logic for the employee down into modules called Employee::Vacation and Employee::Pay, organizing them into appropriately named files in the app/models/employee folder. In that case you could have an ARec model that deals primarily with data (validations, etc) and brings in other classes/modules for the business logic like so:
class Employee < ActiveRecord::Base
validates_presence_of :first_name, :last_name, :start_date
concerned_with :pay, :vacation
end
I didn’t care for the initial justification of RailsConcerns (breaking down a large model) but it now makes a lot of sense based on Martin’s and EngineYard’s comments. The one place that I’ll still disagree with RailsConcerns is its example of extracting validations to a module; since that seems to be the essence of what DTO is, it makes sense to stay in the model.