There a tendency in software developers to want to follow a specific formula. Whether it is adhering to a methodology like agile or using a specific design pattern. This happens so often that we have a specific term for it in our field called Cargo cult programming. In these situation a programmer don’t ask themselves what are we trying to solve here? What is the core of our problem that specific pattern going to solve for us? Are we actually have these kind of problems? Or maybe this problem existed before, but doesn’t exist anymore?
In this post I’m going to list some reasons suggested for using repository pattern. Then we’re going to see if these reasons really needs these kind of patterns to be solved. Then I’m going to propose other alternatives for solving these problems.
What Repository Pattern Trying To Solve and My Arguments Against Using It With ORM
Repository Pattern Helps Hide SQL Complexity
This reason hold water if we use the SQL and ADO.NET directly. But it is not the case when we use an ORM like Entity Framework or NHibernate. Now when using ORM the complexity of querying the database is hidden behind these kind of frameworks. So unless you’re using SQL directly and you want in memory representation of your database objects, using repository doesn’t make any sense.
Repository Pattern Makes Our Code More Testable
No it doesn’t, not at least when we use an ORM. Isaac Abraham has a very good article explaining why this simply is not the case. Also if you use newer frameworks such as entity framework core, this even got easier. There are in memory database that you can used through something called InMemory Provider, more on that here. We can even use LINQ and entity framework directly in our code and our code will still be testable. Although I don’t advise this since it make your code dependent to an specific ORM.
Repository Pattern Solve the DDD’s One Repository Per Aggregate Root
In Domain Driven Design there is a concept called aggregate root. The aggregate root is the entity that act as a parent or root for other set of related entities. So these associated entities only make sense if they are attached to the aggregate root. Imagine we have a table called Book and another table called BookReview. The existence of BookReview table does not make sense without the Book table. What repository pattern supposedly solve is that we can gather all of the database logic related to the whole aggregate into one repository.
So any operation on a BookReview should be done through a Book. Since the existence of BookReview is entirely dependent on a Book. But when we use an ORM, this problem simply doesn’t exist. We can navigate and change an entity which is a root and all the entities that are connected to that entity. We do that simply by using navigation properties in most ORMs.
Repository Pattern Helps Organize Our Database Code
One of the reason repository pattern supposedly benefit us is that it helps organize our database related code. When we have a repository we organize everything related to database and our CRUD into repositories. But do we really need to have repositories to do that? We can organize our persistence logic in a query object if we have a lot of queries. Or for our commands we can have difference service and command objects.
Repository Pattern Helps Us Easily Switch out ORM
Once again this is not something that only repository pattern can solve. This can be solved simply by another level of indirection. I argue that repository pattern make this aspect even harder. Because when we have a repository and we want to switch to another ORM or source of data, we need to do it for all of the queries at once. But if we use something like query object, and each query object are independent of each other, then we are not pressured to do the switch all at once. We can do it separately for each query whenever we need.
Most ORMs Already Implement Repository and Unit of Work Pattern
Another important reason for not using repository pattern with an ORM such as entity framework is that they already implement these patterns. So it would be unnecessary abstraction over an already working abstraction. What we might want to do however is to isolate the ORM specific code into another layer. That can help us to switch out the ORM with another persistence/retrieval strategy.
If Repository Pattern Is Unnecessary, What Can be Used Instead?
Use Mediator Pattern Combined with CQRS
One approach is to create query and command objects. There’s an article by Jimmy Bogard that he suggest preferring query objects over repositories. He also created a library called MediatR which is a Mediator implementation for .Net.
Here I give you an example from my FreeLancerBlog project. MediatR used in this project for reads through query objects and commands for create, update and delete. I try to bring a simple example here, but you can find a lot of more in depth articles on how to best use it such as here and here.
First we create the parameters for our query object.
Then we create a handler that accept those arguments and returns the desired result.
Then we simply take a dependency on IMediator and use the query object like so on line 21.
Note that we can handle all kind of things in our controller though the IMediator interface. This cause our constructors to be lean and make our code more easy to test.
Use Generic Services By Jon P Smith
There’s another library that I like called GenericServices by the author of Entity Framework Core In Action book. There are GenericService that only works with EF6 and EfCore.GenericServices that works with entity framework core. Here’s the more in depth explanation about how this library works.
Repositories On Top UnitOfWork Are Not a Good Idea
Do we need the repository pattern?
Why shouldn’t I use the repository pattern with Entity Framework?
Is the Repository pattern useful with Entity Framework?
Is the repository pattern useful with Entity Framework Core?
Favor query objects over repositories
In this post I discussed why using repository pattern with an ORM might not be a good idea. First we saw some reasons that is brought up for using this pattern. Then I explained why these reasons simply doesn’t not hold when we’re using an ORM. Hopefully I could convince you that this pattern is unnecessary when we use an ORM. I also provided links to other articles about this in case you needed more information.