Should a single command address multiple aggregates? - domain-driven-design

In CQRS and DDD, an aggregate is a transactional boundary. Hence I have been modeling commands always in such a way that each command always only ever addresses a single aggregate. Of course, technically, it would be possible to write a command handler that addresses multiple aggregates, but that would not be within a single transaction and hence would not be consistent.
If you actually have to address multiple aggregates, I usually go with a process manager, but this sometimes feels like pretty much overhead. In addition, from my understanding a process manager always only reacts to domain events, it is not directly addressed by commands. So you need to decide which aggregate to put the starting point to.
I have seen that some people solve this using so-called domain or application services, which can receive commands as well, and then work on multiple aggregates – but in this case the transactional nature of the process gets lost.
To give a simple example, to better illustrate the scenario:
A user shall join a group.
A user has a max number of groups.
A group has a max number of users.
Where to put the command that triggers the initial joining process, and what to call it? user.join(group) feels as right or wrong as group.welcome(user). I'd probably go for the first one, because this is closer to the ubiquitous language, but anyway…
If I had something above the aggregates, like the aforementioned services, then I could run something such as:
userManagement.addUserToGroup(user, group);
However, this addUserToGroup function would then need to call both commands, which in turn means it has to take care of both commands being processed – which is somewhat counterintuitive to having separate aggregates at all, and having aggregates as transactional boundaries.
What would be the correct way to model this?

It may be worth reviewing Greg Young on Eventual Consistency and Set Validation.
What is the business impact of having a failure
This is the key question we need to ask and it will drive our solution
in how to handle this issue as we have many choices of varying degrees
of difficulty.
And certainly Pat Helland on Memories, Guesses, and Apologies.
Short version: the two generals tell us that, if two pieces of information must be consistent, then we need to write both pieces of information in the same place. The "invariant" constrains our data model.
The invariant you describe is effectively a couple of set validation problems: the "membership" collection allows only so many members with user A, and only so many members with group B. And if you really are in a "we go out of business if those rules are violated" situation, then you cannot distribute the members of that set -- you have to lock the entire set when you modify it to ensure that the rule is not broken and that first writer wins.
An element that requires some care in your modeling: is the domain model the authority for membership? or is the "real world" responsible for membership and the domain model is just caching that information for later use? You want to be very careful about trying to enforce an invariant on the real world.
There's a risk that you end up over constraining the order in which information is accepted by the model.

Essentially what you have is many to many relationships between users and groups with restrictions on both sides:
Restriction on the number of groups a user can join
Restriction on the number of users a group can have
VoiceOfUnreason already gave a great answer, so I'll share one way I've solved similar problems and go straight to the model and implementation in case you have to ensure that these constraints are enforced at all costs. If you don't have to, do not make the model and implementation that complex.
Ensuring consistency with such constraints on both Group and User entities will be difficult in a single operation because of the concurrency of the operations.
You can model this by adding a collection of RegisteredUsers to a Group or vice versa, adding a collection of JoinedGroups to a User, and enforce the constraint on one side, but enforcing it on the other side is still an issue.
What you can do is introduce another concept in your domain. The concept of a
"Slot" in a Group. "Slots" are limited by the max number of Slots for a Group.
Then a User will issue a JoinGroupRequest that can be Accepted or Rejected.
A Slot can be either Taken or Reserved. Then you can introduce the concept of SlotReservation. The process of joining a User to a Group will be:
Issue a JoinGroupRequest from a User
Try to Reserve a Slot enforcing the MaxUsersPerGroup constraint.
Acquire a Slot or Reject the SlotReservation of a User enforcing the MaxGroupsPerUser constraint.
Accept or Reject the JoinGroupRequest depending on the outcome of the SlotReservation
If the SlotReservation is Rejected, another User will be able to use this Slot later.
For the implementation, you can add SlotReservation Queue Per Group to ensure that once a Slot is free after a Rejected SlotReservation, the next User that wants to join the Group will be able to.
For the implementation, you can add a collection of Slots to a Group, or you can make Slot an aggregate in its own right.
You can use a Saga for this process. The Saga will be triggered when a JoinGroupRequest is made by a User.
Essentially, this operation becomes a Tentative Operation.
For more details take a look and the Accountability Pattern and Life beyond distributed transactions an apostate's opinion and Life beyond distributed transactions an apostate's implementation.

Related

Update two aggregate instances in a single transaction

Lets say we have an Account aggregate for a banking service. Someone wants to transfer money from their Account to another person's Account. There are a number of rules: payer needs to have enough money in their Account, and the payee's Account must be active. If these rules pass then the balance on both Accounts are updated. In a traditional system this can easily be done in a single acid db transaction.
In DDD this would not be allowed, as we can't update two aggregate instances in a single transaction? Firstly, why? Secondly, does that mean using eventual consistency to handle the two Accounts? If so, I can see how that can be done, but it adds a lot of complexity.
In DDD this would not be allowed
Not really true - there's a lot going on here.
What Evans (2003), and also Vaughn 2013, wrote is that transaction management is not a domain model concern, but rather that transaction control belongs in the application code.
There is, however, a real concern with changing multiple aggregates at the same time: to do so assumes that you can acquire locks on those entities at the same time and also commit all of those changes together.
That's relatively straightforward when all of the aggregates that you are changing are stored in a single relational database; but it becomes very difficult when the aggregates are stored in different places.
If you design your system such that it assumes that all aggregates are stored together, then you greatly restrict your scaling options.
Be careful not to overuse the ability to commit modifications to multiple Aggregates in a single transaction just because it works in a unit test environment -- Vaughn 2013.
does that mean using eventual consistency to handle the two Accounts
That, or changing how you model your aggregates. Sometimes both.
For instance, it's somewhat common to have aggregates that handle (short lived) processes, which are different from the long lived aggregates.
When I look at my credit card statement, a charge will normally fall in one of three states: it's not yet posted to my statement (not visible), or it's pending (visible), or it is actually posted as a charge (visible). Clearly, there is stuff going on "somewhere else", and that information is eventually copied to my statement where I can see it.
I can see how that can be done, but it adds a lot of complexity.
Yup. If it wasn't complicated/complex, we wouldn't be creating our own model; we'd instead be buying some general purpose solution off the self.
Greg Young talked about this in a 2011 presentation: domain driven design makes sense in places where we can derive a competitive advantage from the work we are doing. In other words, we are using it in places where giving the business control over that complexity improves the bottom line.
Making sure you are working on the correct side of the build versus buy line is an important step. Don't skip it.

How to handle hard aggregate-wide constraints in DDD/CQRS?

I'm new to DDD and I'm trying to model and implement a simple CRM system based on DDD, CQRS and event sourcing to get a feel for the paradigm. I have, however, run in to some difficulties that I'm not sure how to handle. I'm not sure if my difficulties stem from me not having modeled the domain properly or that I'm missing something else.
For a basic illustration of my problems, consider that my CRM system has the aggregate CustomerAggregate (which seems reasonble to me). The purpose of this aggregate is to make sure each customer is consistent and that its invarints hold up (name is required, social security number must be on the correkct format, etc.). So far, all is well.
When the system receives a command to create a new customer, however, it needs to make sure that the social security number of the new customer doesn't already exist (i.e. it must be unique across the system). This is, of cource, not an invariant that can be enforced by the CustomerAggregate aggregate since customers don't have any information regarding other customers.
One suggestion I've seen is to handle this kind of constraint in its own aggregate, e.g. SocialSecurityNumberUniqueAggregate. If the social security number is not already registered in the system, the SocialSecurityNumberUniqueAggregate publishes an event (e.g. SocialSecurityNumberOfNewCustomerWasUniqueEvent) which the CustomerAggregate subscribes to and publishes its own event in response to this (e.g. CustomerCreatedEvent). Does this make sense? How would the CustomerAggregate respond to, for example, a missing name or another hard constraint when responding to the SocialSecurityNumberOfNewCustomerWasUniqueEvent?
The search term you are looking for is set-validation.
Relational databases are really good at domain agnostic set validation, if you can fit the entire set into a single database.
But, that comes with a cost; designing your model that way restricts your options on what sorts of data storage you can use as your book of record, and it splits your "domain logic" into two different pieces.
Another common choice is to ignore the conflicts when you are running your domain logic (after all, what is the business value of this constraint?) but to instead monitor the persisted data looking for potential conflicts and escalate to a human being if there seems to be a problem.
You can combine the two (ex: check for possible duplicates via query when running the domain logic, and monitor the results later to mitigate against data races).
But if you need to maintain an invariant over a set, and you need that to be part of your write model (rather than separated out into your persistence layer), then you need to lock the entire set when making changes.
That could mean having a "registry of SSN assignments" that is an aggregate unto itself, and you have to start thinking about how much other customer data needs to be part of this aggregate, vs how much lives in a different aggregate accessible via a common identifier, with all of the possible complications that arise when your data set is controlled via different locks.
There's no rule that says all of the customer data needs to belong to a single "aggregate"; see Mauro Servienti's talk All Our Aggregates are Wrong. Trade offs abound.
One thing you want to be very cautious about in your modeling, is the risk of confusing data entry validation with domain logic. Unless you are writing domain models for the Social Security Administration, SSN assignments are not under your control. What your model has is a cached copy, and in this case potentially a corrupted copy.
Consider, for example, a data set that claims:
000-00-0000 is assigned to Alice
000-00-0000 is assigned to Bob
Clearly there's a conflict: both of those claims can't be true if the social security administration is maintaining unique assignments. But all else being equal, you can't tell which of these claims is correct. In particular, the suggestion that "the claim you happened to write down first must be the correct one" doesn't have a lot of logical support.
In cases like these, it often makes sense to hold off on an automated judgment, and instead kick the problem to a human being to deal with.
Although they are mechanically similar in a lot of ways, there are important differences between "the set of our identifier assignments should have no conflicts" and "the set of known third party identifier assignments should have no conflicts".
Do you also need to verify that the social security number (SSN) is really valid? Or are you just interested in verifying that no other customer aggregate with the same SSN can be created in your CRM system?
If the latter is the case I would suggest to have some CustomerService domain service which performs the whole SSN check by looking up the database (e.g. via a repository) and then creates the new customer aggregate (which again checks it's own invariants as you already mentioned). This whole process - the lookup of existing SSN and customer creation - needs to happen within one transaction to to ensure consistency. As I consider this domain logic a domain service is the perfect place for it. It does not hold data by itself but orchestrates the workflow which relates to business requirements - that no to customers with the same SSN must be created in our CRM.
If you also need to verify that the social security number is real you would also need to perform some call the another service I guess or keep some cached data of SSNs in your CRM. In this case you could additonally have some SocialSecurityNumberService domain service which is injected into the CustomerService. This would just be an interface in the domain layer but the implementation of this SocialSecurityNumberService interface would then reside in the infrastructure layer where the access to whatever resource required is implemented (be it a local cache you build in the background or some API call to another service).
Either way all your logic of creating the new customer would be in one place, the CustomerService domain service. Additional checks that go beyond the Customer aggregate boundaries would also be placed in this CustomerService.
Update
To also adhere to the nature of eventual consistency:
I guess as you go with event sourcing you and your business already accepted the eventual consistency nature. This also means entries with the same SSN could happen. I think you could have some background job which continually checks for duplicate entries and depending on the complexity of your business logic you might either be able to automatically correct the duplicates or you need human intervention to do it. It really depends how often this could really happen.
If a hard constraint is that this must NEVER happen maybe event sourcing is not the right way, at least for this part of your system...
Note: I also assume that command de-duplication is not the issue here but that you really have to deal with potentially different commands using the same SSN.

DDD - Relaxing the rule of Eventual Consistency between aggregate

I`m reading the book PATTERNS, PRINCIPLES, AND PRACTICES OF DOMAIN-DRIVEN DESIGN, written by Scott Millett with Nike Tune. In the chapter 19, Aggregates, he states:
Sometimes it is actually good practice to modify multiple aggregates within a transaction. But it’s
important to understand why the guidelines exist in the first place so that you can be aware of the
consequences of ignoring them.
When the cost of eventual consistency is too high, it’s acceptable to consider modifying two objects in the same transaction. Exceptional circumstances will usually be when the business tells you that the customer experience will be too unsatisfactory.
To summarize, saving one aggregate per transaction is the default approach. But you should
collaborate with the business, assess the technical complexity of each use case, and consciously ignore
the guideline if there is a worthwhile advantage, such as a better user experience.
I face to a case in my project when user request a operation to my app and this operation affects two aggregate, and there are rules that must be verified by the two aggregates for the operation takes place successfully.
it is something like "Allocating a cell for a detainee":
the user makes the request
the Detainee (AR1) is fetched from database and receives a command: detainee.AllocateTo(cellId);
3 the Cell (AR2) is fetched and receive a command: cell.Allocate(detaineeId);
Both steps 2 and 3 could throw an exception, depending on the detainee's status and cell capacity. But abstract it.
Using eventual consistency, if step 2 is executed successfully, emiting the event DetaineeAllocated, but step 3 fails (will run in another transaction, inside an event handler), the state of aggregates will be inconsistent, and worse, the operation seemed to be executed successfully for the user.
I know that there are cases like "when the user makes a purchase over $ 100, its type must be changed to VIP" that can be implemented using eventual consistency, but the case I mentioned does not seem to be one.
Do you think that this is a special case that the book mentions?
Each aggregate must not have an invalid state (internal state), but that does not imply aggregates have to be consistent with one another (external, or system state).
Given the context of your question, the answer could be either yes or no.
The Case for No
The external state can become eventually consistent, which may be acceptable to your product owner. In this case you design ways to detect the inconsistency and deal with it (e.g. by retrying operations, issuing compensating transactions, etc.)
The Case for Yes
In your orchestration layer, go ahead and update the aggregates in a transaction. You might choose to do this because it's "easy" and "right", or you might choose to do this because your product owner says the inconsistency can't be tolerated for whatever reason.
Another Case for No
There's another way out for saying this is not a special case, not a reason for more than one transaction. That way out requires a change to your model. Consider removing the mutual dependency between your detainee and the cell, and instead introducing another aggregate, CellAssignment, which represents a moment-interval (a temporal relationship) that can be constructed and saved in a single transaction. In this case, your detainee and the cell don't change.
"the state of aggregates will be inconsistent"
Well, it shouldn't be inconsistent forever or that wouldn't be eventual consistency. You would normally discuss with business experts to establish an acceptable consistency timeframe.
Should something go wrong an event will be raised which should trigger compensating actions and perhaps a notification to a human stating something went wrong after-all.
Another approach could be to introduce a process manager which is responsible to carry out the business process by triggering commands and listening to events, until completion or timeout. The ARs are often designed to allow small incremental steps
towards consistency. For instance, there could be a command to reserve cell space first rather than directly allocating the detainee. The UI could always poll the state of the process to know when it's complete if necessary.
Eventual consistency obviously comes at a cost. If you have a single DB in a monolith that doesn't need extreme scalability you could very well favor to modify both ARs in a single transaction until that becomes a problem.
Eventual consistency is often sold as less costly that strong consistency, but I believe that's mostly for distributed systems where you'd have to deal with XA transactions.
Do you think that this is a special case that the book mentions?
No.
What I suspect you have here is a modeling error.
From your description, it sounds like you are dealing with something like a CellAssignment, and the invariant that you are trying to maintain is to ensure that there are no conflicts among active cell assignments.
That suggests to me that you are missing some sort of aggregate - something like a seating chart? - that keeps track of all of the active assignments and conflicts.
How can you know? One way is to graph your aggregates; create a node for each piece of information you need to save, and join nodes with lines if there is a rule that requires locking both nodes. If you find yourself with disconnected graphs, or two graphs that only connect at the root id, then it's a good bet that separating some information into a new graph will improve your modeling.
All Our Aggregates Are Wrong, by Mauro Servienti, would be a good talk to review.

DDD Modify one aggregate per transaction with invariants in both aggregates

Suppose I have an aggregate root Tenant and an aggregate root Organization. Multiples Organizations can be linked to a single Tenant. Tenant only has the Id of the Organizations in it's aggregate.
Suppose I have the following invariant in the Organization aggregate: Organization can only have one subscription for a specific product type.
Suppose I have the following invariant in the Tenant aggregate: only one subscription for a product type must exists across all Organizations related to a Tenant.
How can we enforce those invariants using the one aggregate per transaction rule?
When adding a subscription to an Organization, we can easily validate the first invariant, and fire a domain event to update (eventual consistency) the Tenant, but what happens if the invariant is violated in the Tenant aggregate?
Does it imply to fire another domain event to rollback what happens in the Organization aggregate? Seems tricky in the case a response had been sent to a UI after the first aggregate had been modified successfully.
Or is the real approach here is to use a domain service to validate the invariants of both aggregates before initiating the update? If so, do we place the invariants/rules inside the domain service directly or do we place kind of boolean validation methods on aggregates to keep the logic there?
UPDATE
What if the UI must prevent the user from saving in the UI if one invariants is violated? In this case we are not even trying to update an aggregate.
One thing you might want to consider is the possibility of a missing concept in your domain. You might want to explore the possibility of your scenario having something as a Subscription Plan concept, which by itself is an aggregate and enforces all of these rules you're currently trying to put inside the Tenant/Organization aggregates.
When facing such scenarios I tend to think to myself "what would an organization do if there was no system at all facilitating this operation". In your case, if there were multiple people from the same tenant, each responsible for an organization... how would they synchronize their subscriptions to comply with the invariants?
In such an exercise, you will probably reach some of the scenarios already explored:
Have a gathering event (such as a conference call) to make sure no redundant subscriptions are being made: that's the Domain Service path.
Each make their own subscriptions and they notify each other, eventually charging back redundant ones: that's the Event + Rollback path.
They might compromise and keep a shared ledger where they can check how subscriptions are going corporation wide and the ledger is the authority in such decisions: that's the missing aggregate path.
You will probably reach other options if you stress the issue enough.
How can we enforce those invariants using the one aggregate per transaction rule?
There are a few different answers.
One is to abandon the "rule" - limiting yourself to one aggregate per transaction isn't important. What really matters is that all of the objects in the unit of work are stored together, so that the transaction is an all or nothing event.
BEGIN TRANSACTION
UPDATE ORGANIZATION
UPDATE TENANT
COMMIT
A challenge in this design is that the aggregates no longer describe atomic units of storage - the fact that this organization and this tenant need to be stored in the same shard is implicit, rather than explicit.
Another is to redesign your aggregates - boundaries are hard, and its often the case that our first choice of boundaries are wrong. Udi Dahan, in his talk Finding Service Boundaries, observed that (as an example) the domain behaviors associated with a book title usually have little or nothing to do with the book price; they are two separate things that have a relation to a common thing, but they have no rules in common. So they could be treated as part of separate aggregates.
So you can redesign your Organization/Tenant boundaries to more correctly capture the relations between them. Thus, all of the relations that we need to correctly evaluate this rule are in a single aggregate, and therefore necessarily stored together.
The third possibility is to accept that these two aggregates are independent of each other, and the "invariant" is more like a guideline than an actual rule. The two aggregates act like participants in a protocol, and we design into the protocol not only the happy path, but also the failure modes.
The simple forms of these protocols, where we have reversible actions to unwind from a problem, are called sagas. Caitie McCaffrey gave a well received talk on this in 2015, or you could read Clemens Vasters or Bernd Rücker; Garcia-Molina and Salem introduced the term in their study of long lived transactions.
Process Managers are another common term for this idea of a coordinated protocol, where you might have a more complicated graph of states than commit/rollback.
The first idea that came to my mind is to have a property of the organization called "tenantHasSubscription" that property can be updated with domain events. Once you have this property you can enforce the invariant in the organization aggregate.
If you want to be 100% sure that the invariant is never violated, all the commands SubscribeToProduct(TenantId, OrganizationId) have to be managed by the same aggregate (maybe the Tenant), that has internally all the values to check the invariant.
Otherwise to do your operation you will always have to query for an "external" value (from the aggregate point of view), this will introduce "latency" in the operation that open a window for inconsistency.
If you query a db to have values, can it happen that when the result is on the wire, somebody else is updating it, because the db doesn't wait you consumed your read to allow others to modify it, so your aggregate will use stale data to check invariants.
Obviously this is an extremism, this doesn't mean that it is for sure dangerous, but you have to calculate the probability of a failure to happen, how can you be warned when it happen, and how to solve it (automatically by the program, or maybe a manual intervention, depending on the situation).

Enforce invariants spanning multiple aggregates (set validation) in Domain-driven Design

To illustrate the problem we use a simple case: there are two aggregates - Lamp and Socket. The following business rule always must be enforced: Neither a Lamp nor a Socket can be connected more than once at the same time. To provide an appropriate command we conceive a Connector-service with the Connect(Lamp, Socket)-method to plug them.
Because we want to comply to the rule that one transaction should involve only one aggregate, it's not advisable to set the association on both aggregates in the Connect-transaction. So we need an intermediate aggregate which symbolizes the Connection itself. So the Connect-transaction would just create a new Connection with the given components. Unfortunately, at this point the troubles begin; how can we ensure the consistency of connection-state? It may happen that many simultaneous users want to plug the same components at the exact same time, so our "consistency check" wouldn't reject the request. New Connection-aggregates would be stored, because we only lock at aggregate-level. The system would be inconsistent without even knowing that.
But how should we set the boundary of our aggregates to ensure our business rule? We could conceive a Connections-aggregate which gathers all active connections (as Connection-entity), thereby enabling our locking-algorithm which would properly reject duplicate Connect-requests. On the other hand this approach is inefficient and does not scale, further it is counter-intuitive in terms of domain language.
Do you know what I'm missing?
Edit: To sum up the problem, imagine an aggregate User. Since the definition of an aggregate is to be a transaction-based unit we are able to enforce invariants by locking this unit per transaction. All is fine. But now a business rule arises: the username must be unique. Therefore we must somehow reconcile our aggregate boundaries with this new requirement. Assuming millions of users registering at the same time, it becomes a problem. We try to ensure this invariant in a non-locked state since multiple users means multiple aggregates.
According to the book "Domain-driven Design" by Eric Evans one should apply eventual consistency as soon as multiple aggregates are involved in a single transaction. But is this really the case here and does is make sense?
Applying eventual consistency here would entail registering the User and afterwards checking the invariant with the username. If two Users actually set the same username the system would undo the second registering and notify the User. Thinking about this scenario disconcerts me because it disrupts the whole registering process. Sending the confirmation e-mail, for example, had to be delayed and so forth.
I think I'm just forgetting about something in general but I don't know what. It seems to me that I need something like invariants on Repository-level.
We could conceive a Connections-aggregate which gathers all active
connections (as Connection-entity), thereby enabling our
locking-algorithm which would properly reject duplicate
Connect-requests. On the other hand this approach is inefficient and
does not scale, further it is counter-intuitive in terms of domain
language
On the contrary, I think you're on the right track with this approach. It seems convoluted because you're using an example that doesn't make any sense - there is no real-life system that checks if a lamp is connected to more than one socket or a socket to more than one lamp.
But applying that approach to the second example would lead you to ask yourself what the "connection" aggregate is in that case, i.e. inside which scope a user name is unique. In a Company? For a given Tenant or Customer? For the whole <whatever-subdomain-youre-in>System? Find the name of the scope and there you have it - an Aggregate to enforce the unique name invariant. Choose the name carefully and if it doesn't exist in the ubiquitous language yet, invent a new concept with the help of a domain expert. DDD is not only about respecting existing domain terms, you're also allowed to introduce new ones when Breakthroughs are achieved.
Sometimes though, you will find that concurrent access to this aggregate is too intensive and generates problematic contention. With domain expert assent, you can introduce eventual consistency with a compensating action in case of conflict - appending a suffix to the nickname and notifying the user, for instance. Or you can split the "hot" aggregate into smaller, smarter, more efficient ones.
The problem you are describing is called set validation. Greg Young makes a very good point that a key question is whether or not the cost/benefit analysis justifies enforcing this constraint in code.
But let's suppose it does....
I find it's most useful to think about set validation from the perspective of an RDBMS. How would we handle this problem if we were doing things with tables? A likely candidate is that we would have some sort of connection table, with foreign keys for the Lamp and the Socket. Then we would define constraints that would say that each of those foreign keys must be unique in the table.
Those foreign key constraints span the entire table; which is the database's way of telling us that the entire table represents a single aggregate.
So if you were going to lift those constraints into your domain model, you would do so by making an aggregate of all connections, so that the domain model can immediately rule on whether or not a given Lamp-Socket connection should be allowed.
Now, there's an important caveat here -- we're assuming that the domain model is the authority for connections between lamps and sockets. If we are modeling lamps in the real world connected to sockets in the real world, then its important to recognize that the real world is the authority, not the model.
Put another way, if the domain model gets conflicting information about the real world (two lamps are reportedly connected to the same socket), the model only knows that its information about the world is incorrect -- maybe the first lamp was plugged in, maybe the second, maybe there's a message missing about a lamp being unplugged. So in this sort of case, it's common that you'll want to allow the conflict, with an escalation to a human being for resolution.
the username must be unique
This is the single most commonly asked variation of the set validation problem.
The basic remedy is the same: you now have a User Profile aggregate, with an identifier, and a separate user name directory aggregate, which ensures that each name is uniquely associated with a profile.
If you aren't worried that a profile has at most one user name linked to it, then there is another approach you can take, which is to introduce an aggregate for each user name, which includes the profileId as a member. Thus, each aggregate can enforce the constraint that the name can only be assigned if the previous assignment was terminated.
I think I'm just forgetting about something in general but I don't know what.
Only that constraints don't come from nowhere -- there should be a business motivation for them; and somebody (the domain expert) should be able to document the cost to the business of failing to maintain the proposed set constraint.
For instance, if you are already collecting an email address, do you really need a unique username? How much additional value are you creating by including username in the model? How much more by making it unique...?
If we plan an online game, for example, with millions of users which request games constantly, it's a real problem.
Yes, it is; but that may indicate that the game design is wrong. Review Udi Dahan's discussion of high contention domains, and his essay Race Conditions Don't Exist.
A thing to notice, however, is that if you really have an aggregate, you can scale it independently from the rest of your system. One monster box is dedicated to managing the set aggregate and nothing else (analog: an RDBMS dedicated to managing a single table).
A more likely choice is going to be sharding by realm/instance/whatzit; in which case you'd have a smaller set aggregate for each realm instance.
In addition to the suggestions already made, consider that some of these problems are very similar to database concurrency problems. Say that you have a contact, and one user changes the name, and another user changes the phone number for this contact. If you write a command that updates the whole contact with the state as it was after modification, then one of the two will overwrite the change of the other with the old value, unless measures are taken.
If, however, you write a 'ChangeEmailForContact' command, then already you will only change that one field and not have a conflict with the name change, which would similarly be a 'Name' or 'RenameContact' command.
Now what if two people change the email address shortly after the other? A really efficient way is to pass the original value (original email address) along with the new value in your command. Now you can check when updating the email address if the original email address was the same as the current email address (so it is a valid starting point), or if the new email address is the same as the current email address (no need to do anything). If not, then, only then, are you in a conflict situation.
Now, apply this to your 'set operation'. The first time a lightbulb is moved into a 'connection' (perhaps I would call it fixture), it is moving from unassigned to connection1. Then, when a lightbulb is moved, it must be moved from connection1 to connection2, say. Now you can validate if that lightbulb is already assigned, if it was assigned to connection1 or if something has changed in the meantime.
It doesn't solve everything of course, but for the tiny case that remains, that tiny moment where two initial assignments happen close enough together, you either have to go for say a redis cache of assigned usernames to validate against or give an admin an easy tool to solve this very rare instance. You could for instance make a projection that occasionally reports on such situations and make sure renaming isn't too painful.

Resources