I’m building a repository for a large CRM schema that has a high number of relations between entities.
Some of the entities are referenced by almost all entities, e.g. Person and Company.
Where I have an aggregate root such as Order, that addresses Order Header, Order Lines etc. In the case where the Order is from a new Customer, I need to insert a Company and a Person record at the same time… so far so good.
The difficulty is that this is the situation with very many other aggregate roots.
I don’t want to have a private method insertPerson(IPerson)
in every single repository implementation.
I have a Customer repository that already has public InsertCustomer(ICustomer)
method, which persists a Company and a person record. But my reading indicates that repositories shouldn’t depend on each other.
If this is a case where it is okay for repositories to depend on each other, what is the best way to go about it? Should I inject the Customer Repository into the Order repository constuctor, pass it as a parameter to the methods that need it, or never commit this evil and duplicate the identical code in all repositories, in case those repositories need to specialise that code for their specific contexts?
2
This sounds like an area that Service Layer might come in handy. You could inject the repositories in the service layer and that way each repo won’t depend on the others, and the service can coordinate all of the inserts for a given operation across aggregates.
Details of your implementation might also guide you depending on the extent to which you’re relying on an ORM and need to take into account the atomicity of the repository operations. Without knowing more about that this advice may be less than useful.
1
So the process of inserting an Order for a new Customer requires the corresponding new Person to be stored. That sounds like domain logic alright.
The process of which the Order insertion is a part is probably in some domain service. I would expect part of that service to look like this:
Begin transaction
Call to PersonRepo interface
Call to OrderRepo interface
Complete transaction
I realize that transactions are often regarded a database implementation detail. However, and particularly in the form of a TransactionScope around a block of code, they make perfect sense as part of the domain logic, clustering a bunch of operations that are considered one whole, inseparable modification.
Should you be hell-bent on keeping this out of the domain logic and/or consider this an implementation detail of storing the Order, you could always require the Person as a parameter to InsertOrder()
, and have the Order repo depend on the Person repo. I have never heard of this being a bad practice, and one infrastructure detail being dependent on another (in the same infrastructure, no less!) seems perfectly sensible to me. Regardless, I would not recommend this option, because, looking at the resulting domain logic, one wonders why it seems that everything is being stored (calls to repo interfaces) except for the Person!
Composition and the Facade pattern. I would split your repository into two parts: the public interface, and the actual implementation. That way you can implement it however it makes sense for the DB, and still present a nice API to the repository’s consumer. Composition gets you the ability to reuse your customer repository implementation in the other repositories that need access to customer data. For Specialization I would have the customer repository implement multiple interfaces to make special case access explicit and minimal, or if the changes are more in depth go with composition again (or inheritance depending on what you need) to reuse the base implementation and provide the extra functionality.