Extracting Data Access Out of Active Record

Posted on by in Web

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.


  Comments: 14

  1. I completely agree with breaking out the methods of fatty classes into modules. The question I have is would you consider these classes as other domain models? Or services? I can see an argument for both.

    I would also do the same for instance behavior that goes beyond query parameters. Some would say that this leads to class bloat, but my arguments against that lie in the speed of testing that could be gained, and maybe the bloat is in having a codebase that is doing too much in the first place and what you want is a whole other application!

    • I wouldn’t have a problem considering them domain models or services. I prefer to not use pattern names and just consider them objects.

  2. Paweł Gościcki

    Interesting idea.

    On a side note – including gists as js snippets results in posts without code in Google Reader :/ I.e. I need to visit the site to read the code.

  3. I fail to see the benefit in this. I feel like the approach advocated here simply pulls database access into two objects instead of one. Why not leave this logic in the ActiveRecord objects and pull the domain logic into another object of some sort (which also provides the potential to test domain logic without needing the database)?

    • I’m only advocating moving class behavior i.e., query methods. I don’t think it’s confusing to look for queries in one well-named object and domain logic in another.

      If I left all data access in an ActiveRecord subclass e.g., Account, then what would I name the new class that would contain all the domain logic?

      • Ok, I can see how moving the class methods out of the model could help clean things up, especially if you move all custom class methods. I would be interested to see how that works.

  4. Michael Wynholds

    Why don’t you extract these queries to modules and mix them in to the model? That way you get to say Loan.delinquent instead of Lender.delinquent. Doesn’t that make more sense, and still give you non-bloated models? I don’t actually understand how Lender.delinquent is an appropriate name in the domain, especially as it is returning Loan objects. (I feel the same way about Account / Bank, and PhoneCall / TelephonyProvider).

    On a second note, regarding the tradeoff of Lender being dependent on Loan’s properties… I wouldn’t consider calling out those properties in an AR query as being “dependent”. I think dependent means you have access to those properties directly. In this case you are requiring knowledge of the properties of Loan within Lender, but not depending on any of its internals.

    • Extracting ActiveRecord behavior into modules and then mixing them back into the model misses out on an opportunity to introduce a new domain object. The class will still be monolithic and doing too much; its responsibilities will just be in separate files.

      I do agree that it’s a bit strange for these new objects to return other types of objects (e.g., Lender returning Loans). I don’t have a solution for that.

      I consider an object to be dependent on all of its collaborators. Being dependent on a public interface is a safer dependency than knowing about a collaborator’s internals.

      • Michael Wynholds

        So what you are really creating is factories, or finders or something like that, right? I don’t understand how Lender is the domain object for searching for Loans.

        • You could call these modules finders, but not factories because they’re loading existing objects and not creating new objects. In the domain, a lender is someone who lends money, i.e., provides loans.

  5. I feel naming these modules will be difficult and confusing. Difficult because you’d have to figure out two domain names from one. Confusing because the behavior is not explicit in the file or module name. How do you feel about name-spacing the modules (i.e. data)?

    • Naming is hard but domains are filled with useful jargon. A lender sells loans, a bank provides accounts, a telephony provider can provide call history. Introducing these objects allows you to more closely model the domain, instead of having a “dao” or “repositories” directory. Sure those concepts are well-known and standard, but I’d rather have a codebase filled with domain language instead of implementation details.

  6. I do this myself too. and as Rudy mentioned: I name this refactored code to be a service. In this case, I would call it a LoanService.

    In fact, I usually add a folder called:
    to my rails project : and I would put a LoanService right in there. Not only does it offload the loan class, it also helps to keep the app/models directory clean.

    The LoanService knows about Loans, and A Loan can also know about its service.
    The LoanService could also know about other related objects – like accounts, people or banks, (this offloads the Loan object from being overloaded).

    As a design principle – I try to keep all my services either as singletons (if it needs to be configured) or all the methods just end up being static (e.g.: self.my_method) – so there are not a lot of instances of services floating around.

    But maybe that’s just remnants of Java thinking..?

Your feedback