ddd-by-examples / library

A comprehensive Domain-Driven Design example with problem space strategic analysis and various tactical patterns.
MIT License
4.92k stars 726 forks source link

Data consistency #55

Closed crorodriguezro closed 4 years ago

crorodriguezro commented 4 years ago

Hi, I've been studying this project for a while. I really like what you've done here.

I would like to hear advice on how to solve the next issue. The consistency problem be replicated with this steps:

  1. Patron tries to hold a book with the controller.
  2. Patron Aggregate validates the command and then creates a BookPlacedOnHold event.
  3. BookPlacedOnHold arrives to the PatronsDatabaseRepository. The adds the new hold to the patron and then publishes the event.
  4. The event is listen in the Book's PatronEventsHandler Here comes the problem: If, for some reason, the database fails to persist the Book the database will be at an inconsistent state. The Patron will have a Hold but the book state will be Available.

Thanks in advance!

pilloPl commented 4 years ago

Hi! Very good question, we should have put that in the readme.

Basically if we want to have eventual consistency between Book and Patron, we should have some way of retries, max retries policy and escalation. Currently it is done as in memory events database with scheduler

bslota commented 4 years ago

Hi @crorodriguezro! Thank you for your question. Let me answer it.

There are two mechanisms for domain event publishing are available. If we go for the first one, that is JustForwardDomainEventPublisher then all events are sent synchronously so the update of both aggregates will be done synchronously. However, this is not an advised approach I we prefer to use eventual consistency between aggregates, what @pilloPl already mentioned.

The second solution, StoreAndForwardDomainEventPublisher is based on scheduler, and the problem you mentioned is there - we might have a hold registered in Patron aggregate, while Book aggregate will still wait for it. In single app environment (one common in memory collection of events to send) if any of events causes the error, the whole batch will be rolled back. The next execution of scheduler will try to do it once more.

As it is described in the documentation, there is always a tradeoff when going for eventual consistency. We consciously decided to choose Patron as our main aggregate, because this is the Patron that has the majority of critical business rules. If the Patron aggregate is immediately consistent, any temporal inconsistency in Book aggregate can just be accepted with all its drawbacks.

I hope it makes it a bit clearer to you!

Cheers!

CamilYed commented 4 years ago

So StoreAndForwardDomainEventPublisher is implementation of events Sourcing pattern?

bslota commented 4 years ago

It is rather a transactional outbox