Uniqueness validation in CQRS Architecture
Note: This post is copied almost verbatim from a comment I left on Jérémie Chassaing’s blog, my apologies if you’ve seen it there already!
I’ve really enjoyed reading a series of posts on CQRS written by Jérémie Chassaing. One I particularly like the idea that there is no such thing as global scope:
Even when we say, “Employee should have different user names”, there is a implicit scope, the Company.
What this gives us is the ability to identify potential Aggregate Roots in a domain – in the above relationship, there is potentially a Company Aggregate Root in play.
Another observation Jérémie’s post really got me thinking.
Instead of having a UserName property on the Employee entity, why not have a UserNames key/value collection on the Company that will give the Employee for a given user name ?
If I’ve understood Udi’s posts on CQRS, I think he’d probably advocate the collection of Usernames being part of the Query-side, rather than the Command side. I’ve heard him mention before that the query side is often used to facilitate the process of choosing a unique username – the query store may check the username as the user is filling in the "new user" form, identifying that a username already exists and suggesting alternatives.
Of course this approach isn’t bullet-proof, and it will still remain the responsibility of another component to handle the enforcing of the constraint.
The choice of WHERE to put this logic is a question that is commonly debated.
Some argue that since uniqueness of usernames is required for technical reasons (identifying a specific user) rather than for business reasons, this logic falls outside of the domain to handle.
Others may argue that this logic should fall in the domain – perhaps under a bounded context responsible managing user accounts.
In either case, since we have a technical problem (concurrency conflicts) and we have several possible solutions, the decision of whether on not they are suitable should probably constrained in conjunction with the expected frequency of the problem occurring. This sounds to me like the kind of thing that would appear in a SLA.
The solution chosen to enforce the uniqueness constraint will then depend on the agreed SLA. Perhaps it is acceptable that a command may fail (perhaps due to the RDBMS rejecting the change) on the few cases of concurrency conflicts – it might only be on a 0.0001% of cases.
Alternatively we may decide that it is unacceptable to allow this to occur due to the frequency of this occurring. We could choose to maintain the list of usernames in the Company aggregate, but scale out our system such that all "new user" requests in the username range A-D are handled by a specific server. If we decide to enforce this constraint outside of our domain, we can offload this work to occur with the command handlers.
What do you think?