18

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.

Then he goes on to a suggestion to use BASE/Eventual Consistency, which I know as ACIDs weird-cousin.

Here's my problem. Eventual Consistency is not something you just say "oh yeah let me just go on and use a BASE consistency model here real quick".

Compared to transactional-consistency, eventual-consistency is an exotic consistency model with esoteric theoretical caveats, some of them active research subjects; it requires significant exposure to both it's theoretical concepts and practical limitations to avoid foot-gunning yourself - a run-of-the-mill engineer is unlikely to have adequate experience with it or possibly even having heard of it. By definition, it's harder to reason about it, since it lacks the consistency-guarantee of ACID.

My point being is that choosing it as a consistency model is going to have repercussions that reach through the entire system and I can't reasonably take such a fundamental architectural decision without knowing exactly what the trade-off is that is being implied here.

What's Vernon suggesting in that paper? What is it exactly that makes updating multiple Aggregate Roots in one request/transaction such a bad practice, that giving up the ACID-guarantees is justified?

24
  • 3
    It sounds like he's talking about a distributed system, where each aggregate root might live in a different location. Commented Aug 22, 2017 at 17:36
  • 4
    The Aggregate is by definition the transactional boundary. Commented Aug 22, 2017 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. Commented Aug 22, 2017 at 20:42
  • 1
    Thanks - I already did - Scalability in Vernon's PDF while not explicit, is meant in the context of distributed systems. Otherwise the transaction rule doesn't make any sense. The words "cohesion"/"loose coupling" mean nothing in the context of a transaction rule unless there are underlying concerns, which exist in a distributed system. Commented Aug 23, 2017 at 2:43
  • 2
    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. Commented Aug 23, 2017 at 5:20

3 Answers 3

17

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.

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.

4
  • 1
    ...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. Commented Aug 29, 2018 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
    Commented Mar 19, 2019 at 10:14
  • "This is the reason since each microservice needs to have it's own database host." One aggregate in this rule refers to one aggregate Instance, not just one type of aggregate. So e.g. two balances of two separate tenants are two separate aggregates and according to the rule should not have to be updated in one transaction.
    – bdsl
    Commented Aug 12 at 12:11
  • @bdsl Yes, certainly, that's clear Commented Aug 16 at 7:58
16

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.

7
  • 1
    Thanks, I've just finished this video which effectively goes in detail about the points you've made above. Commented Aug 23, 2017 at 2:57
  • 1
    An Interview with Vaughn Vernon on Implementing Domain-Driven Design. Here Vernon comes to say what VoiceOfUnreasonn is answering.
    – Laiv
    Commented Aug 24, 2017 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? Commented Jul 3, 2018 at 10:40
  • However, why should a technical requirement related to an infrastructure concern influence the decision taken in the domain ? Commented Sep 13, 2019 at 22:52
  • By definition a storage constraint is not a domain constraint. These are orthogonal concerns. Our domain is a logical construct that exists in-memory. What happens when we invoke "Save Domain" is of no concern to the domain itself, and the number of database transactions chosen to persist the domain is subject to a very different set of considerations. Commented Jul 13, 2020 at 15:51
1

Further quoting Vaughn Vernon:

Limiting the modification of one aggregate instance per transaction may sound overly strict. However, it is a rule of thumb and should be the goal in most cases. It addresses the very reason to use aggregates.

Since aggregates must be designed with a consistency focus, it implies that the user interface should concentrate each request to execute a single command on just one aggregate instance. If user requests try to accomplish too much, it will force the application to modify multiple instances at once.

Therefore, aggregates are chiefly about consistency boundaries and not driven by a desire to design object graphs. Some real-world invariants will be more complex than this. Even so, typically invariants will be less demanding on our modeling efforts, making it possible to design small aggregates

https://www.dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_1.pdf

If you need transactional consistency across two or more aggregate instances then effectively they become a single aggregate by definition.

As Vernon discusses under the heading "Don't Trust Every Use Case", I think this is not only about allowing for different instances to be stored on different DB hosts, as the answer from Timo suggests, but also about supporting processing concurrent requests from different users. The wider the consistency rules enforced at the database/aggregate level are, the bigger the chance there is for conflicts between requests.

2
  • the last part is an especially good point - I do remember getting the same impression. That these guidelines were addressing update conflicts. I gotta say that I found the theoretical musings of Evans et al a little annoying. I'm under the impression that in their attempts to extricate the high-level architectural concepts from their technical considerations, at any cost, they ended up creating an impractical and dangerously foggy view of their whys and the hows. These considerations should have been stated clearly and unambigously - I despise that we have to go digging for them. Commented Aug 12 at 14:12
  • > cont. That these guidelines were addressing update conflicts... I wonder if this is consequence of the aggregate guidelines themselves, creating this problem. A big update consisting of a lot of composite items would be a prime suspect of update conflicts. Now, I do understand that DDD doesn't care since by definition it maps the domain, not the technicalities. Commented Aug 12 at 14:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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