Reputation: 150842
Except the question whether the following scenario is a good case for DDD at all, I'd like to discuss it and ask for advice:
Given we have users and groups. A user has a name, and a group has a name as well. Users may join of leave groups, and they may also change groups. The rules they have to obey are: A user may only be in maximally 2 groups, and a group may consist of maximally 10 users.
How do you model this? So far I can think of three options, where each option has its individual advantages and disadvantages:
Users and groups are entities, and both are aggregates as well. Join
, Switch
and Leave
are commands on the group aggregate. While this works great for Join
and Leave
(as they only refer to a single group), it does not work for Switch
, as this refers to two groups at the same time, and hence needs to modify two aggregates in a single transaction, which is not nice.
Users and groups are entities, and both are aggregates as well. Join
, Switch
and Leave
are commands on the user aggregate. This works great for all three, and it is easy to check that a user is not in more than two groups at the same time, but how would you check that the rule of maximally 10 users per groups is not violated?
Users and groups are entities, both are aggregates. But there is also a third aggregate: Relationship. Join
, Switch
and Leave
are now commands on the relationship aggregate. While this seems to be the best approach (since it gives the relation between users and groups a name and makes it explicit), I am now completely lost on how to model the constraints.
Can anybody give me a hint?
If there was only one of the two constraints it would be dead-easy: Then you could put the commands to the aggregate which also has the constraints. But if you have a constraint on both sides, I'm lost. Any help?
Upvotes: 7
Views: 2722
Reputation: 51654
You may hand an instance of group to the user to have it check aggregate-encompassing invariants. Then let the user know it joined the group and let the group know that a user has joined.
class Application
handle(UserWantsToJoinGroup command)
user = users.withId(command.userId)
group = groups.withId(command.groupId)
user.join(group)
class User
join(Group g)
if g.isFull throw
if this.isMemberOf(g) throw
if this.numberOfGroupsImIn >= 2 throw
publish new JoinedGroup(this.userId, g.groupId)
handle(JoinedGroup evt)
// modify state
class Group
handle(JoinedGroup evt)
// modifiy state
Upvotes: 7