CQRS Design : best pratices for bounded contexts

Hi,

I’m currently developing a stock management software and that’s my first
try with Axon framework, and CQRS design in general.

So far, I have two aggregates root :

  • WarehouseItem
  • TransferOrder

For both, I’m using a Event Sourcing repository.

I’ve implemented some commands to handle some common business processes
like checkIn, and some other that are specific to my business field (grade
item, and so on…).

These command are applied to the WarehouseItem only, but now, I have to
implement a new use case about TransferOrder.

Basically (and obviously), we can add warehouse items to the TransferOrder.
But these items can only be selected if the item is available according to
certain business conditions.

So, I have a command AddWarehouseItemToTransferOrder with the warehouse
item id.

I can see two solutions to check if the warehouse item is available :

  1. Perform the check in the CommandHandler of this command by loading the
    WarehouseItem aggregate id from there.
  2. Use a Saga to perform the check later on using the
    WarehouseItemAddedEvent, and implement a LockItemCommand on WarehouseItem
    aggregate that would fail if the item is not available.

What is twisting my mind about 1) is that it would imply that from the same
CommandHandler, I would load the TransferOrder and WarehouseItem
aggregates. I never saw such a case in all examples I’ve seen so far, so is
it supposed to be a bad practice ? Should I have another service that would
rely on a specific “View” data generated from the WarehouseItem Events to
know if it’s available ?

  1. seems to be cumbersome for my use case but has the advantage not to mix
    the concerns.

Thank you for your help, ideas, suggestions…

Best Regards,
Jerome

Hi Jerome,

Aggregates are used to process commands, and shouldn’t be loaded or queried by other parts of the application, so both options are out :).

If I understand your problem correctly, a WarehouseItem can be transferred elsewhere when it is placed (possibly with other items) on a TransferOrder.

If the fear is that the item is no longer available for transfer it may be best to start with a command to the item to initiate the transfer. If the item is already being transferred or is no longer in the warehouse the aggregate will reject the command. Otherwise an event like TransferringItemEvent is published.

An event handler outside the item aggregate can then simply add the item to the transfer order using eg AddItemToTransferOrderCommand.

Depending on your demands you may have to put an automatic rollback mechanism in place in case the second command fails, or handle these rare cases manually.

An alternative to the event handler is to use the return value of the first command to determine whether or not to send the second command.

Regards,
Rene

Hi René,

Thank you very much for your answer.

You got my point, and after reading some other posts on this group, I came to the same conclusion that loading the aggregate is excluded for this case.

Another option that I was considering was to perform this check before issuing any commands, with a dedicated service that gets the state from some dedicated read model (and then display some notification on UI side). But again, I’m not convinced, it looks like a workaround more than anything else as the state should be exclusively handled within the aggregate.

I will go with our suggestion then, I’m just wondering if this EventHandler that fires the AddWarehouseItemToTransferOrder shouldn’t be a Saga, as the business flow behind this use case is more complex and contains other steps.

Concerning the return value, I’m not sure it fits my design as I’m using a REST API in front of the CQRS infrastructure.

As you can see, I still don’t have the clear picture :wink: !

Thanks again,

Jerome

Hi Jerome,

It is also a valid option to read the warehouse item state from a read model. If you can go that route it is often the best way because it puts less burden on your aggregates.

However, depending on how likely it is the warehouse item is no longer available you may have to roll back more frequently. That’s why I originally suggested the alternative route.

It is perfectly valid to make decisions based on read state. It may be that the read state is stale but that’s ok as long as you can handle rollbacks either manually or automatically.

Regarding the use of sagas, if you can use a regular event handler it is often better as sagas introduce complexity and limit scalability. One way this can often be achieved is by adding relevant data for the second command to the event published by the warehouse item.

Regards,
Rene

Hi René,

I just wanted to thank you again for enlightening me about this issue. I will go for the easiest solution (two separate commands : reserve on the item itself, and then add on the Transfer ) first as I noticed that checking from the read model would imply to create dedicated data that would become cumbersome.

Have a nice day,
Best Regards,
Jerome