The domain:
I have an InventoryItem
aggregate and a Kit
aggregate. An InventoryItem
may be added to one (and only one) Kit
. An InventoryItem
has a Type
property, which is an enum of various item types the business uses. A Kit
will have multiple InventoryItems
.
I am using event sourcing. Each InventoryItem
aggregate is stored in its own event stream, and each Kit
aggregate is stored in its own event stream.
The problem:
An InventoryItem
must know whether it is part of a Kit
in order to enforce invariants such as blocking deletion if it is, and not allowing it to be added to another Kit
.
A Kit
must know how many and what type of InventoryItems
have been added to it in order to enforce invariants such as not allowing shipment until the Kit
requirements are completely satisfied.
However, one transaction is supposed to affect only one aggregate. So, when an InventoryItem
is added to a Kit
, I'm unable to use one transaction to update both the Kit
and the InventoryItem
with the required information. I've been thinking through several solutions, and each one seems to present several issues.
Using a process manager/saga to model the operation as a long-running process.
This feels wrong to me, as the operation is really not a long-running process. It also runs the risk of a stage failing and requiring rollback of the previous stage. All in all, it feels like a lot of overhead for something that should be a simple operation.
Using domain events to update either the Kit
or InventoryItem
.
I could update one of the aggregates with the initial transaction, then dispatch a domain event to update the second aggregate. However, this runs the risk of an out-of-sync domain model if for some reason the dispatched event fails. Such a case would also introduce the possibility of another user adding the InventoryItem
to another Kit
before the out-of-sync model is noticed and fixed. Once again, it feels like there are a lot of potential problems and complexity for what is a very simple operation.
Creating another aggregate.
I've considered introducing another aggregate such as KitItem
. This aggregate would essentially store the Kit
/Inventory Item
relationship, and the Kit
and InventoryItem
aggregates would hold references to it. This seems to me to be the most reasonable approach, and yet it once again introduces the possibility of an out-of-sync model. For example, what if a Kit
is deleted? We could use a domain event to delete all KitItems
with a relationship to the Kit
, but what if that fails for some reason? Additionally, if the Kit
is only allowed to hold a reference to the KitItem
, how would the Kit
know what types of InventoryItems
have been added to it?
Does anyone have any advice on what approach to take here, or if there's another approach I haven't considered?