For example, consider a store having multiple menus. Menus list items and one item can be listed in multiple menus.
Imagining a Menu aggregate root and Item aggregate root. A Menu would have a collection of MenuItem's who reference an Item AR along with ordering information within the particular menu.
My question is, how would you access the Item's name, price, description from the MenuItem. Say, for instance, the Menu AR handles a command to re-order itself by price (I know that sounds UI related, but I'm strictly talking domain model here, idk maybe it's a business rule that a menu must be sorted in a particular way? )
Would you obtain a Value Object for the Item AR inside the MenuItem? If so, would the Menu AR hold a reference to a domain service to lookup the value object for the Item, or would the MenuItem use the domain service.
I guess, the Menu AR should be consistent at all times and that could mean that when an Item is added to a Menu, the MenuItem holds a reference to a value object for the Item.
Sounds like that would break the 'reference entities by identity' rule, so the MenuItem would hold a reference to ItemId. Considering the use of event sourcing, whenever you'd want to apply a command to the Menu AR, it would replay all of it's events bringing it into consistency and then you issue a command to re-order the menu's items.
The MenuItem would only have a ItemId and not the details of that item, would this be the time to load those items? The Menu could loop over it's MenuItems then use a service to lookup a Item value object by ItemId for each MenuItem and then perform the sorting.
Thanks for any input, greatly appreciated.
Like you said, this should probably be done in the query side. I'm not sure that I see how keeping the ordering consistent in the domain would be of any use? Perhaps I would if menus had different ordering strategies, but even then. Anyway...
If the data used by an AR is not within it's boundary then it may only be made eventually consistent, unless you modify more than a single AR per transaction which is usually a bad practice.
You may do something like that:
menu = menuRepository.findById(menuId);
menu.reorder(itemPricingService);
Some also prefer to resolve dependencies in the application service/command handler:
menu = menuRepository.findById(menuId);
itemIdList = menu.items().map(extractItemId);
itemPriceList = itemPricingService.pricesOf(itemIdList);
menu.reorder(itemPriceList);
However, you will also need to listen to an event such as ItemPriceChanged
in order to keep the menu ordering consistent with price changes.
There's also another strategy where you could copy over pricing information to the MenuItem
. The price would be kept eventually consistent relying on events and a reorder would occur from the item of which the price changed.
You may use a similar reordering strategy as implemented here. Have a look at the reorderFrom
implementation of Product
and ProductBacklogItem
.