Best way to implement Repository Pattern?

Michael Cook picture Michael Cook · Sep 11, 2009 · Viewed 10.7k times · Source

I've been exploring BDD/DDD and as a consequence trying to come up with a proper implementation of the Repository pattern. So far, it's been hard to find a consensus over the best way to implement this. I've tried to boil it down to the following variations, but I'm unsure which is the best approach.

For reference I'm building an ASP.MVC application with NHibernate as a back-end.

public interface IRepository<T> {
        // 1) Thin facade over LINQ
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IQueryable<T> Find();
        // or possibly even
        T Get(Expression<Func<T, bool>> query);
        List<T> Find(Expression<Func<T, bool>> query);
}

public interface IRepository<T> {
        // 2) Custom methods for each query
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySku(string sku);
        IList<T> FindByName(string name);
        IList<T> FindByPrice(decimal price);
        // ... and so on
}

public interface IRepository<T> {
        // 3) Wrap NHibernate Criteria in Spec pattern
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}


public interface IRepository<T> {
        // 4) Expose NHibernate Criteria directly
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Remove(T entity);
        IList<T> FindAll();
        IList<T> Find(ICriteria criteria);
        // .. or possibly
        IList<T> Find(HQL stuff);
}

My initial thoughts are that

1) is great from an efficiency point of view, but I may get into trouble as things get more complicated.

2) seems very tedious and could end up with a very crowded class, but otherwise offers a high degree of separation between my domain logic and data layer which I like.

3) seems difficult up front and more work to write queries, but limits cross contamination to just the Specs layer.

4) My least favorite, but possibly most direct implementation and possibly most database efficient for complex queries, though it puts a lot of responsibility on the calling code.

Answer

dthrasher picture dthrasher · Sep 12, 2009

There's also a good argument for a "none of the above" approach.

The problem with generic repositories is that you're making the assumption that all objects in your system will support all four CRUD operations: Create, Read, Update, Delete. But in complex systems, you'll likely have objects that support only a few of the operations. For instance, you might have objects that are read-only, or objects that are created but never updated.

You could break the IRepository interface into small interfaces, for Read, Delete, etc. but that gets messy pretty quickly.

Gregory Young makes a good argument (from a DDD / software layering perspective) that each repository ought to support only the operations that are specific to the domain object or aggregate you're working with. Here's his article on generic repositories.

And for an alternate view, see this Ayende blog post.