dddshelf / ddd-in-php-book-issues

Leave your comments, improvements or book mistakes as an issue! Thanks ❤️
https://leanpub.com/ddd-in-php
28 stars 2 forks source link

Architectural Styles / CQRS / Synchronizing the Write Model with the Read Model: dual writes problem #81

Closed max-voloshin closed 6 years ago

max-voloshin commented 6 years ago

In subject chapter, there is an example

public function save(Post $post)
{
    $this
        ->em
        ->transactional(
            function (EntityManager $em) use ($post) {
                $em->persist($post);
                foreach ($post->recordedEvents() as $event) $em->persist($event);
           }
    );
    $this->projector->project(
        $post->recordedEvents()
    );
}

and the following explanation:

This ensures that no events are lost, as we’ll project them to the read model if the transaction is successful. As a result, no inconsistencies will exist between the write model and the read model.

Actually project call may fail, and inconsistencies will exist due to dual writes problem: https://www.confluent.io/blog/using-logs-to-build-a-solid-data-infrastructure-or-why-dual-writes-are-a-bad-idea/

More accurate way to synchronize a read model is pulling events from event store in projector instead of pushing them to projector after primary writes.

keyvanakbary commented 6 years ago

Hey thanks a bunch for your comment!

The source of truth in this case is the relational DB managed by Doctrine that complies with ACID, so both the aggregate and the events are stored correctly within a transaction.

The danger comes with inconsistencies in the projection side (projecting into Elasticsearch), you are right. But is not an issue in our example because we can always re-create the index based in the source of truth (this could be even done in an async fashion).

As the article you've sent, the proper way of doing this is with logs or probably better: with Event Sourcing, but ES is such a complex topic that it deserves its own book. That why we though it was enough for the CQRS example 👍

Thanks anyway :)