39

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.

Jim G.
  • 15,141
  • 22
  • 103
  • 166
Michael Cook
  • 1,676
  • 2
  • 26
  • 47
  • +1 for a timely question. I've also struggled with the Repository pattern. – TrueWill Sep 11 '09 at 01:49
  • 3
    Probably not what you're looking for, but I found this implementation of the Spec pattern really cool: http://mathgeekcoder.blogspot.com/2008/07/advanced-domain-model-queries-using.html . Mainly because it lets you use Linq2Sql/Linq2NHibernate/etc. – R. Martinho Fernandes Sep 11 '09 at 01:52
  • Thanks TrueWill and thanks Martinho, that actually relates very well to another topic I posted recently http://stackoverflow.com/questions/1408553/specification-pattern-vs-spec-in-bdd – Michael Cook Sep 11 '09 at 03:56

7 Answers7

23

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.

dthrasher
  • 40,656
  • 34
  • 113
  • 139
  • 1
    I agree. I would just add that doesn't mean you can't have a generic implementation, you just shouldn't have a generic interface. You can have an abstract Repository (and no IRepository interface) with all methods protected. Each specific repository can then extend it and make desired methods public to implement the ISpecificRepository interface. – Saulo Vallory Jun 25 '13 at 20:08
  • Indeed! In my experience the best way to implement the Repository pattern is to *not* implement it at all. Instead, use a good ORM API (such as JPA in Java) and a single "ApplicationDatabase" class to make it easier to use. That's my choice for a Java EE app, anyway; I find it keeps the application code simpler, shorter, and more cohesive than having a bunch of extra "EntityAbcRepository" classes. – Rogério Mar 06 '15 at 20:57
11

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.

Fredy Treboux
  • 3,167
  • 2
  • 26
  • 32
6

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.

Michael Mann
  • 777
  • 3
  • 9
  • This is interesting. I need to think about the potential benefits of using IReadonlyRepository, but at first glance, I kind of like it. Thanks for sharing. – Kevin Swiber Sep 11 '09 at 13:21
1

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<>.

Scott McKenzie
  • 16,052
  • 8
  • 45
  • 70
1

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.

anonymous
  • 6,825
  • 8
  • 47
  • 60
  • But using session directly in logic, while simple, would violate Inversion of Control and make it really hard to test, would it not? – Michael Cook Sep 11 '09 at 16:23
  • Depends on how you structure your code. If you use the Unit of Work pattern, then ISession is your UoW. You can put a thin facade over ISession if that makes things better (I don't). So you then you are left with repositories. I view repositories as very basic -- get object, save object, expose objects as a collection (ISession does all of this). Queries/Specs can be handled by IQueryable. If you have domain logic that ends up depending on ISession it would have instead ended up depending on a Repository. Both of these are equally bad situations. – anonymous Sep 11 '09 at 17:11
  • So ISession can end up being a bit of a 'do everything' kind of interface. That is why putting it behind another interface can be a bit more explicit on how it is being used (IUnitOfWork, IRepository, etc). I think that part is personal preference, and I wouldn't spend a lot of time on engineering the perfect abstraction. – anonymous Sep 11 '09 at 17:15
1

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.

Miroslav Holec
  • 3,139
  • 25
  • 22
0

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

Michael Cook
  • 1,676
  • 2
  • 26
  • 47