When using EventSourcing and CQRS you usually have eventual consistency in your read models. Let's say we have a Customer
entity which is eventsourced and this entity has a social security number property. This number must be unique, so when creating a new Customer
we have to check if that number is not already used.
What is the usual approach to do that sort of validation? I can see three options:
Load the whole bunch of events for all Customer
entities, rehydrate all of them, then verify is none is using the number again.
Query the read model, but everything I read about ES+CQRS explicitly says we should not do that because of eventual consistency.
Have a separate table for those numbers only (preferably in the same database of the events, so we can save the events and the numbers in the same transaction, assuming it is a relational database).
Option 3 seems to be the best option if we're using relational databases, because if everything happens within the same transaction, we don't suffer from eventual consistency.
I can see the same happening for queries like 'get all customers with age above 30'. I cannot see different options than rebuilding the whole streams to query them, or querying the read models.
Any thoughts on that?
Without having a dependence on a relational DB, the purer CQRS/ES approach I can think of would be to have the tracking of the SS# -> customer mapping (since it's likely going to be immutable (changing an SS# is possible but rare enough that you may well be able to defer incorporating that until a lot later)) in an event-sourced entity that only tracks that: the customer entity gets created as a projection of that mapping entity emitting an event associating the SS# to a prospective customer.
For example:
AssociateSocialSecurityWithCustomer("012-34-5678", desiredCustId, name)
=> ssnum-to-customer entitySocialSecurityNumberAssociatedWith(desiredCustId, name)
event is emittedIf there are multiple validations like this, you can resort to a saga which is sending commands to multiple entities (e.g. to handle the case where customer already has an SS#) and rolling back on failures.