Active Record verses Repository
This post compares the Active Record and Repository patterns; probably the two most popular data access patterns in use today. Let me put my cards on the table. Every project I've worked on has used the Active Record pattern. Some of these projects have been designed by me, and some by other folks. But either way, we've really struggled to unit test the object which were mapped to the database. We just could not substitute the data access layer during unit test. This is because the Active Record pattern is fundamentally untestable.
Fowler defines the Active Record pattern in Patterns of Enterprise Application Architecture:
An object that wraps a row in a database table or view, encapsulates the data access, and adds domain logic on that data.
The picture shows a fairly typical active record style class. Its got some business logic methods which aren't shown. It's got instance methods for insert, update and delete. And its got some static "get" methods for creating objects from database records. The Get(id) method returns a single object with primary key id. GetAll() returns a list of all the objects. And GetByFK() makes a list of objects based on a foreign key id.
So what's the problem when it comes to testing?
- The database Insert, Update and Delete methods are on the business object. The only way to test, without hitting the database, is to sub-class and override these methods in a test class. It works, but its ugly. You can easily be screwed if the methods start picking up extra responsibilities.
- The Get methods, which create object based on database records, are all static. There is just no easy way for you to replace the data access with in-memory objects during test. The only way round this problem I can think of is to use a reflection based mocking framework, like TypeMock, to replace these methods. Having attempted this sort of thing, it's not much fun. The tests were not easy to understand at all.
- You just seem to get a general mixing of the business and database logic. Got something which needs to happen when the object gets saved? Stick it in the update method. Maybe this doesn't stop testing in itself, it's just that pernicious complexification of the code.
So, Active Record, by its nature it does not support testing. With a herculean effort, you might get some tests in - but you won't have much fun doing it.
The previous couple of posts have looked at a simple implemetation of the repository pattern, and how do genuine unit testing with the repository pattern. The repository pattern looks something like this:
In the Repository pattern all of the data access is put in a separate class and is accessed via instance methods. To me, just doing this is beneficial, since data access is now encapsulated in a separate class, leaving the business object to get on with business. This should stop the unfortunate mixing of data access and business logic you tend to get with Active Record.
The big benefit comes though when you test. It's simple to put an interface on the repository, and then for testing, replace with a fake repository. The fake repository can be primed, during the arrange stage of the unit test, with objects for use in the test. The update, insert and delete methods just need to record what happened during the test.
So the big advantage of the repository, is its inherent testability. The unit test are easy to write when you use a repository interface with dependency injection. It's inherent goodness, does not however translate into popularity. In a googlefight, Active Record beats Repository hands down. Clearly the battle for testable architecture has not yet been won.