Question

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:

  1. 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.

  2. 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?

  3. 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?

Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top