I'm trying to read about DDD, and i'm struggling a bit trying to to identify aggregate roots. I wrote a really simple app to divide players into different teams inside a game.
So my entities are something like this:
Game Entity:
public class Game : DomainEntityBase, IDomainEntity
{
private List<Team> teams = new List<Team>();
private List<Player> players = new List<Player>();
private int teamSize;
public Game(
string gameName,
int teamSize,
IEnumerable<Player> players) : base(Guid.NewGuid())
{
this.teamSize = teamSize;
this.players = players.ToList();
}
public string GameName { get; private set; }
public ReadOnlyCollection<Team> Teams => teams.AsReadOnly();
public void SplitPlayersToTeams()
{
if (players.Count() % 2 != 0)
{
throw new NotSupportedException("Only equally dividable teams are supported");
}
var teamCount = players.Count / teamSize;
var playersPerTeam = players.Count / teamCount;
SetPlayersToTeam(teamCount, playersPerTeam);
}
private void SetPlayersToTeam(int teamCount, int playersPerTeam)
{
var rnd = new Random();
for (var i = 0; i < teamCount; i++)
{
var team = new Team(i.ToString());
while (team.Players.Count != playersPerTeam)
{
var randomIndex = rnd.Next(players.Count);
var player = players[randomIndex];
if (!team.Players.Contains(player))
{
player.SetTeam(team);
team.AddPlayer(player);
}
}
teams.Add(team);
}
}
}
Team Entity:
public class Team : DomainEntityBase, IDomainEntity
{
private List<Player> players = new List<Player>();
public Team(
string teamIdentifier) : base(Guid.NewGuid())
{
TeamIdentifier = teamIdentifier;
}
public string TeamIdentifier { get; }
public ReadOnlyCollection<Player> Players => players.AsReadOnly();
public void AddPlayer(Player player)
{
players.Add(player);
}
}
Player entity:
public class Player : DomainEntityBase, IDomainEntity
{
public Player(
string nickName) : base(Guid.NewGuid())
{
Nickname = nickName;
}
public string Nickname { get; private set; }
public Team Team { get; private set; }
public void SetTeam(Team team)
{
Team = team;
}
}
Now first I was thinking that the game would be an aggregate root. It would make sense in a way. But then I started thinking that what if you want to persist players separately so that you don't have to add new players for every game? What if you want to persist teams separately if you have teams that can be re-used later? The game it self, would be aggregate root, because I would persist games to for example load a history of games from persistence.
So the question is, is every object I listed above an aggregate root, having their own repositories since every aggregate root should have it's own repository?
Thanks in advance.
Best Answer
Decision that what should be selected as an aggregate root is highly dependent on business rules of your application . If for example , there are two entities say A and B are highly dependent i.e. some operations on entity B requires change in entitiy A then A and B should be under same aggregate root . In short if consistency is needed between two entities than they can have same aggregate root .
In Your case it is also dependent on business rules that what should be selected as an aggregate .
Business Rules Assumptions 1
Now Considering above rules , It seems that Player and Teams are highly correlated/coupled that Only Game aggregate Root is OK here .
Business Rules Assumptions 2
Now Considering above rules , it seems that Game, Player and Team should exists as seperate aggregate roots .
One Different approach for deciding aggregates can be event storming , where you first write single source of truth i.e. the things which has happened for example :
On defining these events , Business will also become more clear to you .
Now write what Command can cause these events and keep on writing the flow .
After some time it will become clear that what commands and events should come from a single point and that can be selected as an aggregate .
But please note that after this also you need to cross check with business rules if you further need to break aggregate or not .
Hope this will clear some doubts .
Please comment if have some doubt in answer .
Thanx in advance .