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.
Developers building applications with ASP.Net Core and Entity Framework Core should not use UoW and Repository pattern anymore. EF Core supports unit testing and mock contexts. EF Core is 100% test-friendly, one can even mock what e.g SaveChanges method (returns the count of records that were affected) returns.
The Repository pattern allows you to easily test your application with unit tests. Remember that unit tests only test your code, not infrastructure, so the repository abstractions make it easier to achieve that goal.
Create the Asp.Net MVC Project with the name of (Repository Design Pattern) screenshots are below. Create an Entity Framework to give the model name DataContext. Select Entity framework designer from the database. Create a Folder in the model folder with the name (DAL) where we will implement our repository.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With