I was reading the comments on Udi Dahan’s blog post about Domain Driven Design when I ran into a frequently asked question regarding domain classes and repository access. Usually, the questions are similar to the following:
- While invoking behaviour on one aggregate root, how do I load another aggregate from a repository?
- How do I inject a repository instance into a domain object?
I generally prefer to tackle this problem using an approach that keeps the domain model free of storage concerns.
The Obligatory Customer/Order Example
Let me put forth a simple scenario to demonstrate what you might do when a domain class seems to require access to information that would typically be in the repository:
A Customer class has a CreateOrder method that enforces the invariant "a customer can only create orders if they have no more than two unpaid orders."
In this scenario, the CreateOrder method needs to access the presumably short list of unpaid orders belonging to the customer. Rather than polluting the Customer class with calls to a repository, the method should simply access an UnpaidOrders field of the Customer class. This brings up the big question: how do the unpaid orders get into the Customer instance?
Repositories Return Ready to Use Aggregates
As suspected, the unpaid orders are loaded from a repository - the big difference is which repository and how. In this example, the Customer repository would handle the loading of UnpaidOrders collection. Whether this collection is fetched immediately when the Customer is loaded (eager loading) or loaded when you access the UnpaidOrders member (lazy loading) is a separate concern. In both cases, the Customer class is oblivious of this and simply accesses the UnpaidOrders collection as if it were pre-populated.
Fancy object relation mapping (ORM) tools, like NHibernate, make it easy to build domain models in this way because they provide separation of concerns. How the UnpaidOrders collection is loaded is part of the ORM configuration. If you are concerned with database performance issues, Udi has a great post on using intention revealing interfaces to solve the conflict between eager and lazy loading.
Closing
In short, the ideal solution is to make domain classes oblivious of repositories by letting the repository for the aggregate root handle the fetching of related entities and aggregates.