Domain-Driven Design – Inter-Aggregate Commands/Transactions in Eventual Consistency

aggregatedomain-driven-designtransaction

I am trying to model a business transaction that operates on two aggregates. Let's say we have a Bag aggregate type that contains Items (entities). I would like to have a command TransferItemToBag(itemName: String, fromBag: BagId, toBag: BagId) which extracts an item from the first Bag and puts it to the second one. When this command is received, I want to send a command AddItemToBag(…) to the second bag and when it responds with ItemAddedToBag(…) send RemoveItemFromBag(…).

In other words, I would like to create a command in the system that does what user can do with the two other commands, sending them one by one. Pseudocode:

on(FirstSecondCombinedCommand cmd) {
   eventBus.register(eventType=FirstEvent, {
       aggregate = repository.ofId(cmd.id2)
       aggregate.handle(SecondCommand(...))
   })

   aggregate = repository.ofId(cmd.id1)
   aggregate.handle(FirstCommand(...))   // produces FirstEvent
}

Where does this logic belongs to? Please notice I don't want to have a strong consistency, rather eventual one. I am also ok if the transaction is interrupted because there was a different competing command in between that came to one of the aggregates. I considered several possibilities:

  • Aggregate – the command is only about a single aggregate type, so the rule of thumb for choosing a place for a business logic (aggregate/entity vs domain service) suggests me to go with that. On the other hand it means the command handler in the aggregate needs to be able to produce commands. It may violate a rule of single aggregate mutation within a transaction/command but what if it done asynchronously to avoid it? I mean posting the command on some kind of Command Bus and let it be dispatched later, maybe by a different thread.
  • Saga – the situation is in fact a kind of a (long) running process and that made me think about Sagas for the implementation. On the other hand I was always considering Sagas as something stateful. I don't see an explicit state in my situation.
  • Application Service – I have seen in IDDD that it is a place for setting up listeners and invoking various commands but still the situation I am describing is a part of a business logic for me and I would like to keep it in the domain.
  • Domain Service – the last possibility I can think of. Seems to be a reasonable place for inter-aggregatetype stuff, but what if we have the same aggregate type?

Please do not focus on this concrete example of aggregates (it is devised). I am interested in a generic answer where to put such "transaction" code.

Best Answer

You're not going to like the answer. It depends. In the case of your simple transaction, you might as well let the Aggregates interact together.

But if it's possible that the interactions involve a number of aggregates (and hence a number of commands and events), your best bet would be to use a Saga. This lets you keep the process in one location, making it easier to update the workflow when the need arises. This would also make it easier to follow the workflow instead of having to trace it across several aggregates. Microsoft worked with a lot of luminaries within the DDD/CQRS community to create a nice blueprint of the approach, their discussion on sagas (which they rename process managers) is worth reading.

Related Topic