One aggregate per transaction, with “one” or “multiple” bounded contexts

628 views
Skip to first unread message

Murilo Carlos Cardoso

unread,
Aug 2, 2020, 6:37:17 PM8/2/20
to ddd...@googlegroups.com

In the chapter 8 of the Red Book Vaughn Vernon demonstrated how two aggregates can "talk" to each other with domain events. In chapter 13 how different aggregates in two different bounded contexts can "talk" to each other with notifications.

My question is, why should I technically deal with these situations differently once both of them happen in different transactions? If is it just one or multiple bounded contexts the possible problems wouldn't be the same?

For example, if the application crashes between two domain events in the same bounded context I'll end up with inconsistency as with two bounded contexts. So this is not a safe way to deal with it, right? 

Thanks!

Greg Young

unread,
Aug 2, 2020, 6:45:25 PM8/2/20
to ddd...@googlegroups.com
Integration through events is *almost always* eventually consistent it's one of the main benefits of the model!

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/CAH7D42QH3gKXNowiPNMOz8mWYMiHgzp3LMae2uheNuc7A8Vpcg%40mail.gmail.com.


--
Studying for the Turing test

Murilo Carlos Cardoso

unread,
Aug 2, 2020, 7:53:05 PM8/2/20
to ddd...@googlegroups.com
I could be wrong but "eventually consistent" is different from "real inconsistency".

I mentioned chapter 8 of the Red Book because in the beginning of the chapter, domain events are demonstrated without any kind of persistence for the events and no transitory status in the aggregates (when in the same BC). If anything bad happens between the "conversation" how can I recover? I consider this a real inconsistency. 

My question is about getting a confirmation that, to avoid real inconsistency the steps below are necessary even in the same BC:

(i) Transitional status in the aggregate;
(ii) Persist events before send them (to avoid lose events);
(iii) In the receiving side execute the "domain operation" in an idempotent way when possible. When not possible, deduplicate the event to check if the event had been received before process the domain operation.

Thanks!

(I'm not a english native speaker so I hope you can understand)



reda laanait

unread,
Aug 2, 2020, 8:25:29 PM8/2/20
to ddd...@googlegroups.com
domain events are demonstrated without any kind of persistence for the events and no transitory status in the aggregates (when in the same BC). If anything bad happens between the "conversation" how can I recover?

Chapter 8 Domain events, spreading the news to remote Bounded contexts (pp 303-304) handle these points. 

You can find more details about event de-duplication in the implementation part of the chapter

Chris Richardson

unread,
Aug 2, 2020, 8:27:37 PM8/2/20
to ddd...@googlegroups.com
Murilo,

Life is certainly easier if you can update multiple business objects in an ACID transaction.

If you cannot because 
  1. You are following a strict DDD single aggregate per transaction
  2. Your framework/database limits you to a single aggregate per transaction
  3. Your aggregates are in different services (in a microservice architecture) and you don't/can't use distributed transactions.
Then you must use an eventually consistent approach, which typically relies on the participants exchanging messages, such as events.

A few points about eventual consistency:
  1. More generally, I describe it as a Saga (https://microservices.io/patterns/data/saga.html)
  2. There are two types of Sagas: choreography-based sagas that use events and orchestration-based sagas that use asynchronous request/reply
  3. Events/messages need to be published as part of the "transaction" that updates the aggregate: that's an inherent feature of event sourcing; with traditional persistence you need to use https://microservices.io/patterns/data/transactional-outbox.html
  4. You need an idempotent consumer: https://microservices.io/patterns/communication-style/idempotent-consumer.html
I hope this helps.

Chris



--
Learn microservices - http://learn.microservices.io
Microservices application platform http://eventuate.io
Consulting and training http://chrisrichardson.net

Murilo Carlos Cardoso

unread,
Aug 2, 2020, 9:02:09 PM8/2/20
to ddd...@googlegroups.com
reda laanait, my point is that the approach presented in the book for "remote bounded contexts" should be technically the same for "same bounded context" in order to avoid real inconsistency.  Don't you think? 

Thanks!



Murilo Carlos Cardoso

unread,
Aug 2, 2020, 9:13:47 PM8/2/20
to ddd...@googlegroups.com

Chris, 

Following the Vaughn Vernon recommendation, to achieve a high level of decoupling and single responsibility I should do this: 

1. "You are following a strict DDD single aggregate per transaction".

So, if the aggregates are in the same BC or not (this is my question), I should do this follow the items below avoid real inconsistency: 

1. Events/messages need to be published as part of the "transaction" that updates the aggregate: that's an inherent feature of event sourcing; with traditional persistence you need to use https://microservices.io/patterns/data/transactional-outbox.html;
2. You need an idempotent consumer: https://microservices.io/patterns/communication-style/idempotent-consumer.html;

Right? 

Thanks!








Tomas Ptacnik

unread,
Aug 3, 2020, 2:21:37 AM8/3/20
to ddd...@googlegroups.com
Hi Murilo,

In my opinion, it doesn't matter if you are following DDD or not or if you are in the same BC or not, whenever you are publishing events you should follow the 
- "safe events publishing" pattern on the publishing side
- "safe events consumption" with an "idempotent receiver" on the consuming side

if you don't, then as you pointed out the "eventual inconsistency" will turn into perpetual inconsistency.


Best
Tomas


Nik

unread,
Aug 3, 2020, 4:55:08 AM8/3/20
to DDD/CQRS
@Murilo, 

You can still use one single transaction to talk to other aggregates within the same BC using domain events. You just commit the transaction after all domain events have been handled.

Use integration events to forward messages to other bounded contexts in an eventual consistency manner. This, of course, requires more advanced techniques like the outbox pattern, etc.

Murilo Carlos Cardoso

unread,
Aug 3, 2020, 6:22:43 AM8/3/20
to ddd...@googlegroups.com
Thanks Tomas! This is my opinion too. 

I'm looking for a confirmation or a different point of view.

Thanks for the confirmation.

Murilo Carlos Cardoso

unread,
Aug 3, 2020, 6:34:15 AM8/3/20
to ddd...@googlegroups.com
Nik,

The problem with domain events in the same transaction is that I cannot "chain" events. At least I don't know a tool which allows it. 
 In a real world, at least in my experience, it is not rare to chain events across more than two aggregates. Considering CQRS, this becomes impractical. 
Am I wrong?

Thanks!


Nik

unread,
Aug 3, 2020, 6:46:30 AM8/3/20
to DDD/CQRS
Hi Murilo,

I have the aggregate roots holding the domain events to be dispatched and dispatching them before committing the transaction.

In a while(true) loop I get all the agg. roots and the events, clear the event collections on the agg roots and dispatch the events.
In the next iteration of the loop I do the same, checking if there are some new domain events produced, if not I commit the transaction.
I also track the loop iteration count and issue a warning if the chain is too long (too many iterations).

Nik

unread,
Aug 3, 2020, 6:49:02 AM8/3/20
to DDD/CQRS
Btw the aggregate roots I get from the Unit Of Work (dbcontext, db session,..).

Murilo Carlos Cardoso

unread,
Aug 3, 2020, 7:35:52 AM8/3/20
to ddd...@googlegroups.com
Nik,

When I mentioned "chain events" I mean:

Agg A publishes Event A THEN Agg B reacts to Event A and publishes Event B THEN Agg C reacts to Event B and publishes Event C. 

What you have mentioned can deal with this all in the same transaction?  Do you have a reference or some code that I can have a look at?

Thanks!

Andrew Young

unread,
Aug 3, 2020, 12:41:34 PM8/3/20
to DDD/CQRS
Tomas,

> then as you pointed out the "eventual inconsistency" will turn into perpetual inconsistency

There is another way you can get perpetual inconsistency. When the consumer of a domain event throws an unexpected exception and doesn't know how to handle it. In these cases, the consumer still needs to move on to the next event because it can't halt processing. From this point on, the system is perpetually inconsistent.

How do you deal with this? Especially on a production environment when you can't bring the system down?

Thanks
Andrew.
To unsubscribe from this group and stop receiving emails from it, send an email to ddd...@googlegroups.com.


--
Studying for the Turing test

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ddd...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ddd...@googlegroups.com.


--
Learn microservices - http://learn.microservices.io
Microservices application platform http://eventuate.io
Consulting and training http://chrisrichardson.net

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ddd...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ddd...@googlegroups.com.

Murilo Carlos Cardoso

unread,
Aug 3, 2020, 1:05:55 PM8/3/20
to ddd...@googlegroups.com
Andrew,

In the consumer side if something goes wrong the message should go to the dead letter queue so it could be reprocessed in the near future (when the bug has been fixed)

If you are not familiar with the concept, just Google for "dead letter queue".

Here is one example in RabbitMQ -> 


To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/2b0963e4-c632-4dbf-af51-45d76db58472o%40googlegroups.com.

Tomas Ptacnik

unread,
Aug 3, 2020, 1:23:40 PM8/3/20
to ddd...@googlegroups.com
Hi Andrew,

first very important step is to get to know about the problem -> 1. have monitoring and alerting in place.
Step 2. is fixing the problem.

That might be fixing the producer or consumer and then replay event(s). It's highly dependent on the problem -> What was the issue, how, many aggregates were affected.


Best
Tomas

To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/2b0963e4-c632-4dbf-af51-45d76db58472o%40googlegroups.com.

Andrew Young

unread,
Aug 3, 2020, 2:53:11 PM8/3/20
to DDD/CQRS
So let me see if I understand this. Is the takeaway here that:
  1. Assume everything is eventually consistent until monitoring/alerting tells you otherwise
  2. There is no general pattern to mitigate situations like this
  3. Each situation is different
  4. Fixing it is done directly on production whether it is replaying, or SQL script, push code, or whatever else brings the system back to consistency?
Thanks!

Nik

unread,
Aug 4, 2020, 3:48:08 AM8/4/20
to DDD/CQRS
@Murilo something like this:

while(true) {
aggRoots = dbSession.entities.aggRoots;
events = aggRoots.events;
if (events is empty) { break; }
aggRoots.clearEvents();
dispatchEvents(events);
}
dbSession.commitTransaction();

Reply all
Reply to author
Forward
0 new messages