Aggregate
can use View
this fact is described in Vaughn Vernon
's book:
Such Read Model Projections are frequently used to expose information to various clients (such as desktop and Web user interfaces), but they are also quite useful for sharing information between Bounded Contexts and their Aggregates. Consider the scenario where an Invoice Aggregate needs some Customer information (for example, name, billing address, and tax ID) in order to calculate and prepare a proper Invoice. We can capture this information in an easy-to-consume form via CustomerBillingProjection, which will create and maintain an exclusive instance of CustomerBilling-View. This Read Model is available to the Invoice Aggregate through the Domain Service named IProvideCustomerBillingInformation. Under the covers this Domain Service just queries the document store for the appropriate instance of the CustomerBillingView
Let's imagine our application should allow to create many users, but with unique names. Commands/Events flow:
CreateUser{Alice}
command sentUserAggregate
checksUsersListView
, since there are no users with name Alice, aggregate decides to create user and publish event.UserCreated{Alice}
event published // By UserAggregateUsersListProjection
processedUserCreated{Alice}
// for simplicity let's think UsersListProjection just accumulates users names if receivesUserCreated event
.CreateUser{Bob}
command sentUserAggregate
checksUsersListView
, since there are no users with name Bob, aggregate decides to create user and publish event.UserCreated{Bob}
event published // By UserAggregateCreateUser{Bob}
command sentUserAggregate
checksUsersListView
, since there are no users with name Bob, aggregate decides to create user and publish event.UsersListProjection
processedUserCreated{Bob}
.UsersListProjection
processedUserCreated{Bob}
.
The problem is - UsersListProjection
did not have time to process event and contains irrelevant data, aggregate used this irrelevant data. As result - 2 users with the same name created.
how to avoid such situations? how to make aggregates and projections consistent?
In the common case, we don't. Projections are consistent with the aggregate at some time in the past, but do not necessarily have all of the latest updates. That's part of the point: we give up "immediate consistency" in exchange for other (higher leverage) benefits.
The duplication that you refer to is usually solved a different way: by using conditional writes to the book of record.
In your example, we would normally design the system so that the second attempt to write Bob to our data store would fail because conflict. Also, we prevent duplicates from propagating by ensuring that the write to the data store happens-before any events are made visible.
What this gives us, in effect, is a "first writer wins" write strategy. The writer that loses the data race has to retry/fail/etc.
(As a rule, this depends on the idea that both attempts to create Bob write that information to the same place, using the same locks.)
A common design to reduce the probability of conflict is to NOT use the "read model" of the aggregate itself, but to instead use its own data in the data store. That doesn't necessarily eliminate all data races, but you reduce the width of the window.
Finally, we fall back on Memories, Guesses and Apologies.