Event sourcing, one event, state of two aggregates changed

domain-driven-designevent-sourcing

I am trying to learn ways of DDD and related subjects. I came up with an idea of simple bounded context to implement "bank": there are accounts, money can be deposited, withdrawn and transferred between them. It is also important to keep history of changes.

I identified Account entity and that event sourcing would be good to keep track of changes in it. Other entities or value objects are irrelevant to the problem, so I won't mention them.

When considering deposits and withdrawals – it's relatively simple, because there is only one aggregate modified.

When transferring it's different – two aggregates must be modified by one MoneyTransferred event. DDD deprecates modifying multiple aggregates in one transaction. On the other hand event sourcing's rule is to apply events to entities and modify state based on them. If event could be stored simply in database, there would be no problem. But to prevent concurrent modification of event sourced entities we must implement something versioning the event stream of each aggregate (to keep their transaction bounds). With versioning comes another problem – I cannot use simple structures to store events and read them back to apply them to aggregate.

My question is – how can I bring together those three principles: "one aggregate one transaction", "event->change in aggregate" and "concurrent modification prevention"?

Best Answer

When transferring it's different - two aggregates must be modified by one MoneyTransferred event.

Transferring money is a separate act from updating the ledgers.

MoneyTransferred
AccountCredited
AccountDebited

The exercise that finally broke this loose for me was realizing that AccountOverdrawn is an event, it describes the state of the account without regard to the other participants in this exchange, so there must be a command run against an account that produces it.

You can't reasonably derive state like AccountOverdrawn from the read model, because you can't possibly know if you have seen all of the events yet -- only the aggregate itself has a full view of the history at any given moment.

The answer, of course, is right there in the ubiquitous language -- accounts are credited or debited to reflect the bank's obligations to its customers.

Allright, but it means I should use AccountCredited and AccountDebited events for deposits and withdrawals as well, so I only register not the cause of change, but the change caused by some other action. If I would like to reverse the action I couldn't, because not all of the events are registered.

I'm not entirely certain that follows, because you do have (for cases like this one) a natural correlation identifier, which is the transaction id itself.

Second thing - it means i need to use something like saga.

Slightly different spelling: you need something like a human being dispatching the right commands.

There are at least two ways you could do it. One would be to have a subscriber listening for MoneyTransferred, and dispatching the two commands to the ledgers.

Another alternative would be to track the processing of the transaction as a separate aggregate -- think of it as a checklist of all the things that need to get done since a transaction occurred. So a MoneyTransferred event handler dispatches ProcessTransaction, which schedules work to be done and checks off what work has been completed.

Related Topic