IRepository: one size does not fit all

I’ve been spending a lot of time putting TDD/DDD into practice lately, and obsessing over all sorts of small-yet-important details as I try concepts and patterns for myself.

One pattern that has recently become popular in mainstream .NET is IRepository<T>. Originally documented in PoEAA, a repository is an adapter that can read and write objects from the database as if it were an in-memory collection like an array. This is called persistence ignorance (PI), and lets you forget about underlying database/ORM semantics.

Somewhere along the line however, someone realized that with generics you can create a single, shared interface that will support pretty much any operation you could ever want to do with any sort of object. They usually look something like this:

public interface IRepository<T>
    T GetById(int id);
    IEnumerable<T> GetAll();
    IEnumerable<T> FindAll(IDictionary<string, object> propertyValuePairs);
    T FindOne(IDictionary<string, object> propertyValuePairs);
    T SaveOrUpdate(T entity);
    void Delete(T entity);

If you need to add any custom behaviour, you can simply subclass it — e.g. IProjectRepository : IRepository — and add new methods there. That’s pretty handy for a quick forms-over-LINQ-to-SQL application.

However, I don’t believe this is satisfactory for applications using domain-driven design (DDD), as repositories can vary greatly in their capabilities. For example, some repositories will allow aggregates to be added, but not deleted (like legal records). Others might be completely read-only. Trying to shoehorn them all into a one-size-fits-all IRepository<T> interface will simple result in a lot of leaky abstractions: you could end up with a Remove() method that is available but always throws InvalidOperationException, or developer team rules like “do not never call Save() on RepositoryX”. That would be pretty bad, so what else can we do instead?

Possibility #2: no common repository interface at all

The first alternative is simply dropping the IRepository<T> base, and make a totally custom repository for each aggregate.

public interface IProjectRepository
    Project GetById(int id);
    void Delete(Project entity);
    // ... etc

This is good, because we won’t inherit a bunch of crap we don’t want, but similar repositories will have a lot of duplicate code. Making method names consistent is now left to the developer — we could end up with one repository having Load(), another Get(), another Retrieve() etc. This isn’t very good.

Thinking about it, a lot of repositories are going to fit into one or two broad categories that share a few common methods — those that support reading and writing, and those that only support reading. What if we extracted these categories out into semi-specialized base interfaces?

Possibility #3: IReadOnlyRepository and IMutableRepository

What if we provided base interfaces for common repository types like this:

IReadOnlyRepository and IMutableRepository

This is better, but still doesn’t satisfy all needs. Providing a GetAll() method might be helpful for a small collection of aggregates that we enumerate over often, but it wouldn’t make so much sense for a database of a million customers. We still need to be able to include/exclude standard capabilities at a more granular level.

Possibility #4: Repository Traits

Let’s create a whole load of little fine-grained interfaces — one for each standard method a repository might have.

public interface ICanGetAll<T>
    IEnumerable<T> GetAll();

public interface ICanGetById<TEntity, TKey>
    TEntity GetById(TKey id);

public interface ICanRemove<T>
    void Remove(T entity);

public interface ICanSave<T>
    void Save(T entity);

public interface ICanGetCount
    int GetCount();

// ...etc

I am calling these Repository Traits. When defining a repository interface for a particular aggregate, you can explicitly pick and choose which methods it should have, as well as adding your own custom ones:

public interface IProjectRepository :
    ICanGetById<Project, int>,
    IEnumerable<Project> GetProjectsForUser(User user);

This lets you define repositories that can do as little or as much as they need to, and no more. If you recognize a new trait that may be shared by several repositories — e.g., ICanDeleteAll — all you need to do is define and implement a new interface.

Side note: concrete repositories can still have generic bases

Out of interest, here’s what my concrete PersonRepository looks like:

Concrete NHibernateRepository and PersonRepository

There’s very little new code in it, because all of the common repository traits are already satisfied by a generic, one-size-fits-all NHibernateRepository base class (which must be completely hidden from external callers!). The IPersonRepository just defines which subset of its methods are available to the domain layer.

14 thoughts on “IRepository: one size does not fit all

  1. I’m definitely leaning towards a generic DAO style inner repository, and concrete implementation of a domain repository containing this.

    Part of the domain would be IPersonRepository exposing clear methods like GetPersonByFirstName(string firstName) etc… This would be implemented by a concrete repository that lives outside your domain that contains a nested concrete generic DAO style repository exposing Load FindAll etc…

  2. It seems like we can almost clearly separate the concepts of “Domain Repositories” (and Domain Repositories Traits) and “Data Access Repositories”.

    Should we?

  3. 2Richard

    And also I’d better tend to instead (of inheritance) delegate data-access functions to NHibernateRepository by using it inside ProjectRepository.

    That’s just because I can’t clearly see any reason to inherit from it. Is there one?

  4. Very good post. I definitely like way you split the repository interface into little self-contained traits.
    Combining your approach with a few extension methods creates traits that carry implementation with them. That implementation is easily reused between concrete repositories. I show this approach in a blog post at …and hey, I think it turns out pretty nicely :-)

  5. @Richard do you have a sample solution and project structure on GitHub or something so I can see a bigger picture? How do you usually structure your projects with this kind of architecture? Do you put these interfaces in the Domain project, or in the same project as the implementation of the repositories? (and yes, i’ve read Jimmy’s recent blog post on project structures, but I am working on a fairly large and complex system that warrants a structured approach). Thanks

  6. @Thiago: I would recommend looking at Ayende’s Macto, a sample “prison management application” written using DDD.

    Background and design notes:

    And I wouldn’t use split code into separate projects (DLLs) unless they are being packaged and deployed separately. If you are just producing one .exe, or one web app, then it will be much faster to put everything in the same project/assembly and use namespaces.

  7. @Richard I dug through Ayende’s app, which hasn’t been updated in 2 yrs. It doesn’t even resemble the problem and requirements of my app and architecture, so it’s not much help to me.

    Also, I do need things separated because it’s a cloud app with web and worker roles that access same domain models, EF entities, etc.

  8. I create an interface per repository, no inheritance at all. It’s only the implementation I use inheritance for, I create a generic Repository and then subclass + override where needed. So I get common behaviour but only expose via the interface what is logical for the currecn object.

Comments are closed.