I felt it was time to update and clarify some previous thoughts on the use of the Repository pattern.
Previously, I had indicated that a Repository did not provide much benefit, as ORMs act as a Repository. Although true, I had a very specific context in mind when saying this – anemic domain models. When you’re working with anemic domain models, a Repository does add another layer of unnecessary complexity.
So, when can adding a Repository add value?
When you have complex business rules and logic, and more specifically within Domain-Driven Design. In DDD, the concepts of a bounded context describe segregating related entities and their associated business logic into a collection of related classes containing rich object-oriented behavior. The class hierarchy and public methods of your entities allow you to contain and precisely dictate how others interact with the objects. These controlled interactions with the domain can then ensure all invariants have been resolved.
In the above code, a Person could be created without a First or Last Name. In fact, a Person can even be created without a Name. The second example below shows an alternate approach, where the creation and and update of a Person is controlled, preventing a Person from being created or updated without both a First and Last Name.
So what does this have to do with Repositories?
In DDD, bounded contexts are charged with maintaining complex states with completely resolved invariants. Because your bounded context is really just a collection of richly-interacting classes, you need a way to ensure any changes in your bounded context are persisted to your database.
Enter the Repository.
Within the concept of a bounded context, you can have separate groups build your bounded contexts and your object persistence layers. This is a great opportunity for the Repository to be responsible for detecting changes in your bounded context and persisting them properly (and at the same time, within the same transaction). The Repository also enables your domain architects not need to be ORM persistence experts (they should probably know about ORM/object persistence; however, they don’t have to be the expert).
Unlike the Repository<T> that has FindById() and a generic Update() method for every entity within your bounded context, I prefer my bounded context Repository to retrieve and persist object graphs.
This should provide more clarity around previous comments, but I’m sure I’d still love to hear from you.