You got it right, with 2 Aggregates but now you need to understand why.
The Member Aggregate owns the data and the behavior for the members. What a member means, how to ca become a member, when can it change its name, when can it be unlocked and so on.
The Game Aggregate owns the rules of the game and two players. It has all the data it needs to protect its invariants and to display it in the UI. The invariants it protects are:
- at least two different players must exist before the game starts and one member can play only one time in the same game
- each player makes a move, one after another
- when a player is out-of-game it cannot move anymore
- and so on
In order to protect the second invariant, the Game has a turn
state that is a pointer to the next Player. But here comes the interesting question: what is a Player, how can it be represented in the code?
A player is a pointer to one of the Members. It has all the attributes that are needed by the Game aggregate or the UI. The Game Aggregate needs its ID in order to protect the first invariant (to detect duplicate IDs), in order to load all the games that a Member plays and in order to display in the UI the name of the Player so that the other players will know with who they are playing.
The Player seems to be an Entity, because it has an ID and another property, it's name, but it isn't because it doesn't own the name nor the behavior for it: when and how a member can change it's name is controlled by the Member Aggregate, the Player's name is updated without the Player to have a word on this, maybe in the background.
From the Game's point of view, a Player is just a plain object, immutable, with an ID and a name as a string
. This is a perfect candidate for a Value object.
So, although a Member is an Aggregate root (which is a type of entity), a Player, having the same ID and name as a Member is not an Entity but a Value object.