4

Quoting from Vaughn Vernon:

When two or more Aggregates have at least some dependencies on updates, use eventual consistency.

enter image description here

He further goes on to suggest that one could make use of Domain Events to publish actions to the other Aggregate Roots that need to be updated.

He further proceeds to explain that Eventual Consistency might be a necessary evil.

What makes updating multiple Aggregate Roots in one request/transaction such a bad practice?

  • 2
    It sounds like he's talking about a distributed system, where each aggregate root might live in a different location. – Robert Harvey Aug 22 '17 at 17:36
  • Database contention. Segregation of responsibility. Tighter coupling. Also, what the guy above said. – Machado Aug 22 '17 at 17:37
  • 2
    The Aggregate is by definition the transactional boundary. – Constantin Galbenu Aug 22 '17 at 18:17
  • 1
    @NicholasKyriakides you can do it in the same request but you should be prepared for failure so you need to have a mechanism to detect failure. – Constantin Galbenu Aug 22 '17 at 20:42
  • 1
    This rule helps you think again about your design when you need a multiple-aggregate transaction, as this is a strong indication that you have not correctly identify the aggregates boundaries. If you have already thought again and again, have gained suficient knowledge about your domain, domain experts cry when they see you again and run in all directions and still need multiple aggregates in the same transaction and you don't need scalability then do it. – Constantin Galbenu Aug 23 '17 at 5:20
4

From everything I've read, here and elsewhere, the reason seems to be that changing multiple aggregates in one transaction creates the requirement that they are stored on the same database host. (Let's not even consider two-phase commit.)

This introduces a trade-off, one that makes this a consideration rather than a rule of thumb.

Is your bounded context in question ever going to be divided among multiple database hosts? Not multiple databases - that is no problem. Database hosts.

Unless you process incredible volumes or you are bad at choosing table indexes, most contexts are only ever going to use a single database host. This lets you have database transactions around multiple aggregate changes (within one bounded context). This makes consistency guarantees so much easier.

All things considered, I prefer the decision making process like this:

  1. Is it enough for the aggregates to be eventually consistent? If so, use eventual consistency.
  2. Can we reasonably expect the aggregates to always live on a single database host? If so, allow them to share the same database transaction. (Note that if the aggregates are in different bounded contexts, the answer here is probably 'no'.)
  3. We need multiple hosts and guaranteed consistency. Only now do we have to jump through hoops, because our requirements are heavy. Solve the problem through design.

To give an example of #3:

  • The Balance context tracks the balance of each tenant.
  • A tenant's balance must not be negative.
  • The Payment context wants to spend some of a tenant's balance. The deduction must be immediately consistent (to guarantee the previous rule). Should the payment fail, then the balance must eventually go back up.
  • The Balance context exposes in its API a method that returns a new Reservation, reducing the balance by a requested amount, or returning a failure if that amount is not available.
  • The Balance context consumes events from the Payment context.
  • Certain events increase a tenant's balance.
  • Other events pertain to a decrease in the tenant's balance, and are always linked to a prior Reservation. They confirm that Reservation.
  • Reservations are valid for a short time, e.g. 5 minutes. Reservations not confirmed within that time are reversed, increasing the balance to compensate.

Note that this example requires the guarantee that every event is handled exactly once. Particularly, no event must ever be skipped! That is doable, but it is challenging to get right. Most tools are not airtight in this regard. The most easily identified point of failure is if the application server crashes after updating its database, but before publishing its event(s). Guaranteeing exactly-once delivery of events is a worthwhile discussion in its own right.

  • ...creates the requirement that they are stored on the same database host. This is the reason since each microservice needs to have it's own database host. I came to this realisation later on. I'll mark this as accepted as this is the correct answer. – Nik Kyriakides Aug 29 '18 at 15:26
  • @NikKyriakides I disagree with "each microservice needs to have [its] own database host". One context may contain multiple applications that are separate microservices (e.g. an API and a job service). Being in the same context, these could certainly access the same database (so by definition the same host). And even with separate contexts, we just should have separate databases. If the company chooses to host these on the same server, that is fine. I hope this is clear. – Timo Mar 19 at 10:14
12

What makes updating multiple Aggregate Roots in one request/transaction such a bad practice?

The problem is the other way around - trying to modify multiple aggregates in a single transaction is an indication that you haven't modeled your aggregate boundaries correctly.

Put another way: modifying two different aggregates in the same transaction introduces a constraint on their storage (the aggregates need to be stored in the same database), and that constraint is not reflected in the model. It's effectively implicit.

  • 1
    Thanks, I've just finished this video which effectively goes in detail about the points you've made above. – Nik Kyriakides Aug 23 '17 at 2:57
  • 1
    An Interview with Vaughn Vernon on Implementing Domain-Driven Design. Here Vernon comes to say what VoiceOfUnreasonn is answering. – Laiv Aug 24 '17 at 7:31
  • One question: where/how should the domain event be stored? It has to be stored somewhere as part of the same transaction (otherwise, you could lose some events) in order to be processed later. But then, persisting an event could be considered as persisting a second aggregate root which violates the 1! aggregate/transaction rule? What am I missing? – Xavier Dury Jul 3 '18 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.