Joydip Kanjilal
Contributor

Design patterns that I often avoid: Repository pattern

opinion
Sep 08, 20164 mins
Software Development

Learn the potential pitfalls of using the repository pattern, including adding an extra layer of abstraction when it's not needed in your software designs

Design patterns provide proven solutions to real world problems faced in software designs. The Repository pattern is used to decouple the business logic and the data access layers in your application.

The data access layer typically contains storage specific code and methods to operate on the data to and from the data storage. The data access layer that the repository abstracts can be an ORM (i.e., Entity Framework or NHibernate), XML file, a web service, etc. It can even be a collection of SQL statements.

In using the Repository design pattern, the business logic layer of your application need not have any knowledge on how data persistence happens beneath. Essentially, a repository mediates between the domain and the data mapping layers of your application. It’s supposed to provide you an encapsulation on the way that data is actually persisted in the data storage layer.

The Repository pattern may be beneficial where you have many entities and have many complex queries to work with those entities. An extra layer of abstraction in this case can help you to eliminate duplication of query logic.

The generic repository

A generic repository is a type that comprises of a set of generic methods for performing CRUD operations. However, it’s just another anti pattern and is used frequently with Entity Framework to abstract calls to the data access layer. In my opinion, using a generic repository is generalization too far. It’s a bad idea to abstract calls to Entity Framework using a generic repository.

Let me explain this with an example.

The following code listing illustrates a generic repository — it contains generic methods for performing the basic CRUD operations.

public interface IRepository<T>

   {

       IEnumerable<T> GetAll();

       T GetByID(int id);

       void Add(T item);

       void Update(T item);

       void Delete(T item);

   }

To create a specific repository, you would then need to implement the generic interface as shown in the code listing below.

public class AuthorRepository : IRepository<Author>

   {

       //Implemented methods of the IRepository interface

   }

As you can see, to create any specific repository class, you would need to implement each of the methods of the generic repository interface. The major drawback of this approach is that you would have to create a new repository for each entity.

Here’s another drawback of this approach: The basic intent of the repository pattern is to decouple your domain layer from how the data is actually persisted by the data access layer. Here’s an updated version of the repository class we just created.

public class AuthorRepository : IRepository<Author>

   {

       private AuthorContext dbContext;

       //Methods of the IRepository interface

   }

As you can see in the code listing given earlier, the AuthorRepository needs the AuthorContext instance to perform the CRUD operations it is intended for. So, where is the decoupling, then? Ideally, the domain layer should not have any knowledge of the persistence logic.

An extra layer of abstraction

The domain model and the persistence model in an application have distinctly different responsibilities. While the former models behavior, i.e., models the real-life problems and the solutions to those problems, the latter is used to model how the application’s data is actually stored in the data store.

The intent of the repository pattern should be to abstract the persistence logic and hide the internal implementations of how the data is persisted. The operations of the repository should be expressive enough and not be generic. You cannot have a repository that is generic and one that can contain operations that can fit in any scenario. This becomes an unnecessary abstraction and hence makes the generic repository pattern an anti-pattern. You can model all your domain objects the same way. A generic repository doesn’t define a meaningful contract and you would again need a specific repository that extends your generic repository and provides the specific set of operations that are meaningful to that particular entity.

Now that you have quite a few mature data persistence technologies (NHibernate, Entity Framework, etc.) around, why do you need this extra layer of abstraction anyway? Most of the mature ORM technologies available today have the same capabilities. In trying to use a repository, you just add an additional layer of abstraction without any reason. As an example, you might need methods like the following for your AuthorRepository.

FindAuthorById()

FindAuthorByCountry()

This gets worse as you have more and more methods and complex searches – you would end up with having a repository that would closely map with the persistent storage layer in use underneath.

Joydip Kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author