We want to model a warehouse application. Let us assume we identified the following real world objects:
- Articles(the things stored in the warehouse)
- Palettes (where the Articles are on)
- Compartments (the places in the racks where the palettes are stored in)
There are the following constraints:
- A palette is in exactly one compartment
- A compartment can hold zero or one palette
For the start we have one operation:
- Move (moves a palette from its current compartment to another).
Of course this is very simplified.
How should this be modelled?
Stockitem could be a value object I think. One solution would be to model the whole warehouse as an aggregate with palette and compartment entities. The move operation could be implemented without any problems in this case regarding its constraints (invariants). But this approach has obvious drawbacks. The Event log for this aggregate will grow infinitely. Two move operations could not be executed in parallel because of the aggregate versioning and so on. And from a ddd point of view, it feels not right for me.
Another approach would be to make each palette and each compartment its own aggregate. But how would the move operation be implemented then?
Problem 1 : Who loads referenced aggregates?
I think it should stick to the palette. The palette could reference the compartment it is in (its an aggregat). But how is this reference implemented (CQRS/ES)? The Comandhandler of the move command obviously will load the palette aggregate from the palette repository and call the move method on it. Who loads the referenced compartment? And who loads the compartment it should be moved to? I read that aggregates should not access repositories. Should the commandhandler load both compartments? Should the compartments be given to the move method as parameters? Or should the commandhandler set the current compartment to the palette and give the target compartment as a parameter?
Problem 2 & 3 : Constraints and bidirectional association between aggregates
What about the constraints? To check if the target compartment is empty, the compartment needs to know the palette that is stored in it. This would be a bidirectional association that should be avoided. And because they are different aggregates, they could not be updated in the same transaction. Has the palette to fire a domain event to inform the compartment that it will move to it? Has it to be implemented as a saga with undo action? What if two moves conflict, one wins, but the loosing palette could not be moved back, because the old compartment is filled up in the meantime?
This all seems very complicated to me regarding the really simple problem.
In the books and the examples it seems all so clear. But if I try to use it, I seem to do it wrong.
Could somebody guide me in the right direction please?