Design models provide proven solutions to real-world problems encountered in software designs. The repository model is used to decouple business logic and data access layers in your application.
The data access layer typically contains storage-specific code and methods for operating on data to and from data storage. The data access layer that the repository summarizes can be an ORM (i.e. Entity Framework or NHibernate), XML file, web service, etc. It can even be a collection of SQL statements.
By using the repository design model, your application’s business logic layer doesn’t need to know how data persistence occurs underneath. Essentially, a repository acts as an intermediary between the domain and the data mapping layers of your application. It is supposed to provide you with an encapsulation on how data is actually held in the data storage layer.
The repository model can be beneficial when you have many entities and you have many complex queries to work with those entities. An extra layer of abstraction in this case can help you eliminate the duplication of query logic.
The generic repository
A generic repository is a type that includes a set of generic methods for performing CRUD operations. However, this is just another anti-model and is frequently used with the Entity Framework to ignore calls to the data access layer. In my opinion, using a generic repository is too deep a generalization. It is a bad idea to abstract Entity Framework calls using a generic repository.
Let me explain this with an example.
The following code list illustrates a generic repository – it contains generic methods for performing basic CRUD operations.
public interface IRepository
T GetByID(int id);
void Add(T item);
void Update(T item);
void Delete(T item);
To create a specific repository, you will then need to implement the generic interface as shown in the code list below.
public class AuthorRepository : IRepository
//Implemented methods of the IRepository interface
As you can see, to create a specific repository class, you need to implement each of the generic repository interface methods. The major downside to this approach is that you would have to create a new repository for each entity.
Here’s another downside to this approach: The basic intention of the repository model is to decouple your domain layer from how data is actually held by the data access layer. Here is an updated version of the repository class we just created.
public class AuthorRepository : IRepository
private AuthorContext dbContext;
//Methods of the IRepository interface
As you can see from the code list given earlier, AuthorRepository needs the AuthorContext instance to perform the CRUD operations for which it is intended. So where is the decoupling? Ideally, the domain layer should have no knowledge of persistence logic.
An additional layer of abstraction
The domain model and the persistence model in an application have markedly different responsibilities. While the first models behavior, that is, models real problems and the solutions to those problems, the second is used to model how application data is actually stored in the data store.
The intention of the repository model should be to abstract persistence logic and hide internal implementations of how data is persisted. The operations of the repository must be sufficiently expressive and not be generic. You cannot have a generic repository that can contain operations that can adapt to any scenario. This becomes an unnecessary abstraction and therefore makes the generic repository model an anti-model. You can model all of your domain objects in the same way. A generic repository does not define a meaningful contract, and again you would need a specific repository that extends your generic repository and provides the specific set of significant transactions for that particular entity.
Now that you have a few mature data persistence technologies (NHibernate, Entity Framework, etc.), why do you need that extra layer of abstraction anyway? Most of the mature ORM technologies available today have the same capabilities. When trying to use a repository, you are simply adding an extra layer of abstraction for no reason. For example, you might need methods like the following for your AuthorRepository.
It gets worse as you have more and more complex methods and research – you would end up with a repository that closely matches the persistent storage layer used below.