Extracting Data Access Out of Active Record

Posted on by in Development

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.

Extract Query

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 ILoanRepositoryImpl or 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, expires_on and 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 Loan.

Here’s another example (again, for simplicity, this example only contains one query):

Let’s move this query to a new Bank object:

And one last example:

We can move this into a TelephonyProvider:

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.