How to handle set based consistency validation in CQRS?
Asked Answered
Q

4

37

I have a fairly simple domain model involving a list of Facility aggregate roots. Given that I'm using CQRS and an event-bus to handle events raised from the domain, how could you handle validation on sets? For example, say I have the following requirement:

  1. Facility's must have a unique name.

Since I'm using an eventually consistent database on the query side, the data in it is not guaranteed to be accurate at the time the event processesor processes the event.

For example, a FacilityCreatedEvent is in the query database event processing queue waiting to be processed and written into the database. A new CreateFacilityCommand is sent to the domain to be processed. The domain services query the read database to see if there are any other Facility's registered already with that name, but returns false because the CreateNewFacilityEvent has not yet been processed and written to the store. The new CreateFacilityCommand will now succeed and throw up another FacilityCreatedEvent which would blow up when the event processor tries to write it into the database and finds that another Facility already exists with that name.

Quadrangular answered 26/5, 2010 at 21:18 Comment(1)
What kind of store are you using on command side of the solution? Classic ORM or Event Sourcing?Gilberto
Q
19

The solution I went with was to add a System aggregate root that could maintain a list of the current Facility names. When creating a new Facility, I use the System aggregate (only one System as a global object / singleton) as a factory for it. If the given facility name already exists, then it will throw a validation error.

This keeps the validation constraints within the domain and does not rely on the eventually consistent query store.

Quadrangular answered 23/6, 2010 at 18:25 Comment(3)
I know this is an old question, but your approach implies that the CreateFacility command handler actually modifies two aggregates, the "global" System aggregate Nd the newly created Facility aggregate. Or have I misunderstood?Archambault
@user1420752 You cannot "modify" something that has just been created ;) There is no potential contention over creations.Vanderpool
OK, poorly worded. He modified the global "System" aggregate and at the same time created a new "Facility" aggregate. If the two aggregates are modified in a single transaction (e.g., if you're using "traditional" DDD over an ACID-compliant database), there's no problem, but the CQRS "purists" would say "Aggregates represent transactionally consistent boundaries! You're limiting scalability!". But if you don't need high scalability (and I believe most people don't) then the above solution is fine.Archambault
H
6

Three approaches are outlined in Eventual Consistency and Set Validation:

  1. If the problem is rare or not important, deal with it administratively, possibly by sending a notification to an admin.
  2. Dispatch a DuplicateFacilityNameDetected event, which could kick off an automated resolution process.
  3. Maintain a Service that knows about used Facility names, maybe by listening to domain events and maintaining a persistent list of names. Before creating any new Facility, check with this service first.

Also see this related question: Uniqueness validation when using CQRS and Event sourcing

Hosier answered 13/5, 2016 at 17:55 Comment(1)
Wouldn’t the service in (3.) be eventually consistent if it listened for domain events? A previous command may have completed but the event not be received and processed by the service before next command is being processed.Sparkle
S
1

In this case, you may implement a simple CRUD style service that basically does an insert in a Sql table with a primary key constraint.

The insert will only happen once. When duplicate commands with the same value that should only exist one time hits the aggregate, the aggregate calls the service, the service fails the Insert operation due to a violation of the Primary Key constraint, throws an error, the whole process fails and no events are generated, no reporting in the query side, maybe a reporting of the failure in a table for eventual consistency checking where the user can query to know the status of the command processing. To check that, just query again and again the Command Status View Model with the Command Guid.

Obviously, when the command holds a value that does not exists in the table for primary key checking, the operation is a success.

The table of the primary key constraint should be only be used as a service, but, because you implemented Event sourcing, you can replay the events to rebuild the table of primary key constraint.

Slifka answered 17/1, 2015 at 14:26 Comment(0)
H
-1

Because uniqueness check would be done before data writing, so the better method is to build a event-tracking service, which would send a notification when the process finished or terminated.

Hippocrene answered 17/12, 2016 at 2:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.