DDD: Repository Implementation Patterns
One of the major structural patterns encountered in DDD (and one of the most argued about) is the Repository pattern. You’ve created a persistent domain model, and now you need to be able to retrieve these objects from an encapsulated store. In Fowler’s PoEAA, the Repository pattern is described as:
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
In many DDD implementations, the use of a Repository is widened to go beyond retrieval, but the other pieces in the CRUD acronym. This is natural, as a Repository provides a centralized facade over some backing store, whether that backing store is a database, XML, SOAP, REST and so on. But what about the actual interface of the Repository? What kinds of options do we have for designing the actual Repository itself? There are quite a few different schools of thought here, each with their own benefits and drawbacks.
The Generic Repository Interface
In the generic repository interface, the interface definition itself is generic, with implementations for individual entity types. The generic type parameter denotes the entity type of the repository. One example is in S#arp Architecture:
public interface IRepositoryWithTypedId<T, IdT> { T Get(IdT id); IList<T> GetAll(); IList<T> FindAll(IDictionary<string, object> propertyValuePairs); T FindOne(IDictionary<string, object> propertyValuePairs); T SaveOrUpdate(T entity); void Delete(T entity); IDbContext DbContext { get; } }
If you want a repository for a specific type of entity, you create a derived type, closing the interface type with the entity:
public interface ICustomerRepository : INHibernateRepositoryWithTypedId<Customer, string> { List<Customer> FindByCountry(string countryName); }
Note that this repository supplies an additional, specialized query method for a specific find method. The nice thing about a generic repository interface is that a corresponding base repository implementation can be designed so that all of the data access code that doesn’t change from repository to repository can have a place to live. The actual implementation can then rely on a common base implementation for the non-changing behavior:
public class CustomerRepository : NHibernateRepositoryWithTypedId<Customer, string>, ICustomerRepository { public List<Customer> FindByCountry(string countryName) { ICriteria criteria = Session.CreateCriteria(typeof(Customer)) .Add(Expression.Eq("Country", countryName)); return criteria.List<Customer>() as List<Customer>; } }
But, we still get the type safety of generics. However, we tend to muddy the contract of the ICustomerRepository a little if our entity does not support all of the operations of the base repository interface. It’s basically a sacrifice of a common base implementation that you violate the Interface Segregation Principle for some simplification on the implementation side. For example, in my current domain, deleting an entity has legal ramifications, so we basically don’t allow it for quite a few entities. Instead, things are marked as various stages of “soft” deletes.
One big benefit is that most modern IoC containers allow configuring the container such that the most derived type of IRepository
The Generic Method Repository
In the generic method repository, our repository implementations do not expose methods for specific queries. Additionally, no specific repository for an entity is defined. Instead, only general-purpose methods are exposed for querying, persisting and retrieval. The methods are generic, providing type safety, but there is only one implementation. Consider the Alt.Oxite repository implementation from FubuMVC Contrib:
public interface IRepository { void Save<ENTITY>(ENTITY entity) where ENTITY : DomainEntity; ENTITY Load<ENTITY>(Guid id) where ENTITY : DomainEntity; IQueryable<ENTITY> Query<ENTITY>() where ENTITY : DomainEntity; IQueryable<ENTITY> Query<ENTITY>(IDomainQuery<ENTITY> whereQuery) where ENTITY : DomainEntity; }
In this implementation, the repository itself is not generic. A single repository dependency can perform all needed CRU (not D) operations against any well-known entity. Because most ORMs nowadays are generic, the implementation of a generic repository is very straightforward, it simply wraps the ORM. The actual definition of queries is left to something else. In the above case, LINQ queries utilize the Query method to retrieve persistent objects.
The advantage to this pattern is that we do not need to create an interface for each type of entity we create. Instead, we remove the responsibility of forming specific queries to the callers, including worrying about fetching, caching, projection and so on. Since this logic does not change, we don’t have to pull in a repository for every entity we want to retrieve. Instead, operations flow through one general-purpose interface.
The disadvantage to this pattern is we lose named query methods on our repository, as Greg pointed out a while back. The ICustomerRepository from the previous pattern exposed (and encapsulated) a well-known query with a intention-revealing name. With a generic method repository, there is only one implementation. Creating a derived ICustomerRepository of a generic method repository would be rather strange, as the base interface allows any entity under the sun.
The Encapsulated Role-Specific Repository
Classes that use a repository rarely use every method inside of it. A class that does a query against Customers probably isn’t going to then go delete them. Instead, we’re likely querying for some other purpose, whether it’s to display information, look up information for business rules, and so on. Instead of a catch-all repository that exposes every method under the sun, we could alternatively apply the Interface Segregation Principle for role-based interfaces, and define interfaces that expose only what one class needs. With these repositories, you’ll much more likely find encapsulated query methods, that do not expose the ability to arbitrarily query a backing data store:
public interface IProductRepositoryForNewOrder { Product[] FindDiscontinuedProducts(); }
A single repository implementation implements all Product repository interfaces, but only the single method needed is exposed and used by the caller. This way, you do not see Save, Delete, and other query operations that you do not care about. The down side is that this implementation tends to be more difficult to combine with IoC containers, as auto-wiring does not work as expected. At some point, it starts to look like these types of repositories are glorified query objects, a single class that represents a query.
###
Wrapping it up
In our projects, we’ve tended to use the generic repository interface, as it allows us to override implementations for custom behavior. We still will include a default implementation that gets wired in, a non-abstract RepositoryBase
In the end, it really depends on the situation of each project. I’ve tended to stick with a generic interface, as it’s the easiest to scale complexity. However, quite a few smart folks use the generic method repository, and rely on external queries for the hard stuff. Complexity with saves and deletes is then handled by the cascading behavior of the underlying ORM. It’s quite intriguing, but I’ve been reticent to give up a tried-and-true pattern. I’m curious to see what other repository patterns people use out there.