Commands affecting multiple aggregates

Hi,

before we start with the obvious rethink your aggregate boundaries I feel there are use cases where this is actually happening.
Let’s take an eshop web app as an example - we have the catalog and the inventory management contexts.
But for the user convenience when creating a new product there is an option to immediately set the stock quantities.
So one action now represents a CreateProductCommand as well as a CreateInventoryItemCommand.

For obvious reasons those 2 live in separate bounded contexts one focusing on the catalog and one the actual inventory for a product thus product and inventory are 2 AR.
What is the correct way to handle this scenario ?

We have the following options

  1. create the 2 aggregate roots in one command handler - but this is not a good practice
  2. create a saga. here I wonder how to deliver the required information to the saga - it would start on the CreateProductCommand but in order to issue the CreateInventoryItemCommand it has to recieve the data for that.
    what means we would need to apply an ProductInventoryCreated event to pass the required data further to the saga.
  3. create an API service between the UI and the command handlers that will take the incoming request and submit 2 commands. it has to cope with errors when one command fails etc.

david

Hi,

the aggregate boudaries sound fine :wink:

I’d go for option 3. The commands don’t seem to be related directly, so a Saga feels like forcing things the wrong way.
Invoking 2 aggregates from a single command is possible in Axon, but not considered good practice.

Cheers,

Allard

Hi Allard,

thanks for the response. I would like to hear more opinions on this issue - e.g. mapping UI -> domain functionality.
Because often the GUI groups together things that do belong to different domains but they simply “belong” on one screen.

Another option that comes to my mind is an extension of the previous model -

Let’s say we have a more complicated model in Product -> ProductVariant -> Inventory.

And in another domain we have a Cart.
The ProductVariant has it’s own SKU and price thus inventory is tracked for the variant not for the product.
that means you are adding ProductVariants to a Cart not Products. In that case the ProductVariant has to be an AR so we can reference it in the Cart.

Then we have an UI screen that allows as to create the following on one click

  1. a product
  2. multuple variants
  3. stock for those variants

that would map to the following commands

  1. CreateProduct
  2. multiple AddProductVariant
  3. and multiple AddInventory

while in this scenario a Saga for managing the product -> variant relations seems to have a logical reason there is again a need for further orchestration between the GUI and the domain.

d.

Yesterday another approach crossed my mind - not sure if it makes any sense but we will see :wink:

What about using an EventMessage to start a saga that would coordinate those actions ?
The incoming composite command could be wrapped into an EventMessage and directly dispatched into the event bus.
This would start a ProductionCreationManagerSaga that would receive all the required data for its subsequent actions in the starting event and then issue the commands to create the required aggregates.
Then it will listen for the domain messages originating from the aggregates and act accordingly.

d.

Hi David,

it’s not a conventional approach, but if it works, it works. Often, these kinds of solutions are an indication that you’re missing a domain concept. The key question here is: ‘what is the key action the user triggers? What are side effects of that action?’

Note that you may have to manage transaction boundaries around your first event. Normally, events are published at points where a transaction was already started.

Cheers,

Allard

It’s more like two domain concepts at once all to simplify the UI.

the first concept is to create the product with its optional variants.
the second is to set inventory for those variants.
both being separate aggregates with different logic behind them.

the best solution for me seems to handle this as an application service that will talk to 2 domain services and coordinate their action.