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
Transferring money is a separate act from updating the ledgers.
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.
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.
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.