The Active Record pattern combines data access and domain logic. Over time, an Active Record class can accumulate a large amount of query methods. Moving these methods into a separate object can result in smaller, more cohesive domain objects.
Here’s an Active Record class with a few queries (for simplicity, there’s only two):
Let’s move these queries to another object:
Instead of naming this object something like
RdbLoanDAO, we used a word from the domain. Words like “repository” and “DAO” are implementation details. By including vocabulary from the domain, we create a richer domain model.
I used a module because there’s no state; only behavior. Without state, you only need one instance. Using a class would communicate that you could instantiate a
Lender, which would be useless. When you only have behavior, a module is all you need.
One tradeoff from moving these queries out of
Loan is that
Lender is now dependent on some of
Loan‘s attributes (specifically,
repaid). However, this dependency is ok because these attributes are part of
Loan‘s public interface.
Test this module using mini-integration tests. Instead of mocking out
Loan, rely on the real
Loan object and its database. Test it as if this method was on
Here’s another example (again, for simplicity, this example only contains one query):
Let’s move this query to a new
And one last example:
We can move this into a
When Should I Do This?
Use this refactoring when the majority of an Active Record class consists of queries. The key to this refactoring is finding a good name for the new object. Take your time and don’t fall back on old habits. Avoiding pattern names in class names is a good way to get fellow developers on board with this refactoring.
This refactoring only addresses persistence concerns at the class-level. Active Record objects still have instance-level persistence methods (e.g., #save, #update_attributes, #destroy). By only refactoring class behavior, you can gradually move away from Active Record.