Question

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.

Was it helpful?

Solution

I think they are all good options (except maybe 4 if you don't want to tie yourself to nhibernate), and you seem to have pros and cons well analyzed to make a decision on your own based on your current endeavor. Don't beat yourself too hard on this.

I'm currently working on a mixture between 2 and 3 I guess:

public interface IRepository<T> 
{
        ...
        IList<T> FindAll();
        IList<T> FindBySpec(ISpecification<T> specification);
        T GetById(int id);
}

public interface ISpecificRepository : IRepository<Specific> 
{
        ...
        IList<Specific> FindBySku(string sku);
        IList<Specific> FindByName(string name);
        IList<Specific> FindByPrice(decimal price);
}

And there's also a Repository (of T) base class.

OTHER TIPS

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.

One of the things we are doing is that all of our repositories have different needs so we are creating a collection of interfaces:

public interface IReadOnlyRepository<T,V>
{
   V Find(T);
}

In this example the read only repository just does gets from the database. The reason for the T,V is the V represents what is returned by the repository and the T represents what is passed in, so you could do something like this:

public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer>
{
    public Customer Find(int customerId)
    {
    }

    public Customer Find(string customerName)
    {
    }
}

I can also create seperate interfaces for the Add, Update and the Delete. This way if my repository does not need the behavior then it just does not implement the interface.

I'm a bif fan of 1 because I can create filters and paging extension methods than I can apply to the IQueryable<> return values for the Find method. I keep the extension methods in the data layer and then construct on the fly in the business layer. (Not totally pure, admittedly.)

Of course, when the system stabilises I have the option to make specific Find methods using the same extension methods and optimise using Func<>.

When using NH with Linq your repository can be:

session.Linq<Entity>()

The specifications are things that deal with:

IQueryable<Entity>

You can facade it all away if you want, but that is a lot of mundane work to abstract an abstraction.

Simple is good. Yah, NH does databases, but it provides so many more patterns. Having other than the DAL depend on NH is far from a sin.

I believe it depends on your needs. It is important to think about repository in conjunction with other design patterns you consider to use. Finally, it very depends on what do you expect from repository (what are the main reasons for using it).

Do you need to create strict layer (for example you will need to replace NHibernate with Entity Framework in the future)? Do you want to write test especially to the repository methods?

There is no best way how to create repository. There is just a few ways and it is definitely up to you, what is the most practical for your needs.

Wither the Repository

A relevant article by Jimmy Bogard of LosTechies

http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/10/wither-the-repository.aspx

Also, another quick article with some comments suggesting version #2 is really a DOA pattern and not a repository.

http://fabiomaulo.blogspot.com/2009/06/linq-and-repository.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top