Repository and unit of work
The known repository pattern combined with a unit of work pattern are a great idea if you have to abstract your persistence layer away. I saw more than one implementation so far. Some I would use as well and some I’m happy I don’t have to maintain.
Currently I work on a project that builds up some reusable framework to support some often used customer needs in our business. Some data are managed by this framework, that also provides a standard UI (asp.net mvc) for administration. These information have to be stored to a database. We do not want to couple the whole framework to entity framework because looking to the future, we see a lot more than that. CosomosDB is coming and we do want to support EF 6, EF core, mongodb and possibly some more. So looking back to some previous experience I see the repository and unit of work approach as a solution among others. And to be clear, it is not a one size fits all solution. These kind of solutions simply do not exist.
For simplicity I reduce the code to the essential and provide only a example.
The definition of Martin Fowler
A short quote of Martin Fowler’s definition of a repository take from his official page
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
So given this definition the following code will be completely valid.
This does work with entity framework without any problems and matches the definition. Everything is fine, right? For default settings this wil work well. But what are the drawbacks here?
- You can not cache the entities, because they are context bound
- In disconnected scenarios (like webapps) you have to load the entities for all actions again.
- It simply does not work out of the box with e.g. mongodb driver
The pragmatic solution
The pragmatic version looks similar to this:
With this solution, we have a explicit update call and can handle all special logic within this call. Yes, it does break the original definition but it works. So if you have a situation where you do not have to do anything, you just do nothing within this implementation. Our implementation checks if the entity is attached and does nothing if it is there. If it is not attached, it attaches the entity and marks it as modified so EF will update the database. For other dbms, this method will be different or empty at all.
Hint: Do validate these update calls with unit tests so that they can’t get lost.
What entities do deserve a repository?
This question is tricky. The answer to this question has direct impact of the number of repositories you have to build or how bloated your repository interface will become.
every entity gets a repository
The easy way is to say that every entity gets an own repository. Looking at the example above we would also have a repository for DetailA. Does this make sense? Probably yes and probably no. If you follow the rule strictly, DetailA does get a repository. But does it make sense? In this case it wouldn’t make sense because DetailA can not exist without EntityA. An what about the hidden entities that are used to build n:m relations? Should they be implemented as a class and have it’s own repository even if they have only the two ids as fields? I’d say no, they shouldn’t. So in my opinion this is not a good rule to build your repositories.
only aggregate roots get a repository
Another approach is to use the aggregate roots to build repositories. So every aggregate root entity gets a repository implementation. This may sound easy and logical but it isn’t. In the example above there will only be the EntityARepository and no DetailA repository. Simple because DetailA depends on EntityA and can not stand for it’s own. This is an easy example and everyone may follow here. But what about the following situation?
So which entity does get a repository here? User? Role? Both? and if both, where will be the code to assign a role to a user or assign a user to a role? Or are these redundant implementations allowed? I can’t give you a recommendation what solution you should implement here. If your requirements have a higher weight on the user than on the role, I’d implement the assignment code at the user repository. But at the end of the day, the decision is up to you. There is no magic solution for these problems. If you have a good hint to a better solution for this problem in relation to the repository pattern, I’d be glad to get the information :-)
Should I use this pattern?
As always, it depends :-) If you just have a tiny little application that is not supposed to grow or live long, I would not use this pattern. If it is a quick and dirty prototype, I would not use it either. It would be a overkill and the chance that you change your database background is quite low. So use your db and implement the code within the application service classes.
We currently use it in a library used to support building asp.net mvc apps with a often used set of basic functionality of our customers. In this case we think it is worth the effort to implement this pattern. Just because we let the app developer decide where the data is stored. Some like to have it in a sql database, some in cosmosdb and some in a local mongodb. So we have implemented EF6 support and are planning to provide mongodb as well. But it is nut sure if we keep the repository pattern for the next major version.
After implementing the repository pattern once more and fighting once more these topics and potential issues with the mapping, I think I’m not going to implement it in the next solution, library or project. I think I’m going to use a implementation of the CQRS-Pattern. This does not mean I’m going to use event sourcing. CQRS with event sourcing is a whole different story ;-) But luckily we can use CQRS without event sourcing.