This is a short follow up on Bjarte’s Post.
There’s an important thing to consider when needing set validation : why ?
Why do these things need to be considered together and cannot just be handled separately ?
We can distinct two different parameters in uniqueness, Cardinality and Scope.
There are mainly two types of cardinality :
Only one employee can be the boss.
The model could provide a IsBoss property on every employee… But constancy would be very hard to achieve, especially in a CQRS architecture.
We should read the preceding rule as :
The company has only one boss. The boss is an employee.
Now, we can model a Boss property on the Company Aggregate Root that will reference the employee that is the boss. Changing the boss can now be an atomic and consistent operation.
We can see that we had to introduce a upper level to manage it (we’ll se it in the Scope section).
Employee should have different user names.
We can clearly see here that user names must be different because they’ll act as identifiers. This is the goal of almost any uniqueness constraint. The property will be used as a key in a lookup.
The 1 (or 2 or 3) cardinality also act this way. It’s a way to tag an entity. You can ask “who is the boss ?” and get the answer by a simple lookup at the Boss property that acts like a bucket in a hash table.
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.
Even when we say, “You Id Card number should be unique”, understand, “at the Country scope”.
Even when we say, “Your DNA should be unique”, understand, “At our life understanding scope”.
Find the scope and see the volume of data whose uniqueness should be enforced.
As we said, properties that have a uniqueness constraint are usually used as lookup values to find those entities. As such they rarely take part in the child entity domain logic.
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 the expected Employee count is expected to be in a limited range, this is the most appropriate solution.
If the number can grow, loading it in memory on each Company hydratation is a bit heavy, so keep the directory on disk (using a table with a unique key in a RDBMS as suggested by Bjarte) or any other way that provide the expected performance.
In every case, when a uniqueness constraint appear on a property, the property does not belong the the entity itself but should be viewed as a key to access the entity from the upper level scope.
Do you have examples that cannot be solved this way ?