4

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.

  • 1
    What are your business requirements? What are the invariants that need to be protected? For example, A user can play only a game at the same time. What are the consistency requirements around those entities? – Constantin Galbenu Jan 29 '18 at 9:11
  • 4
    As Constatin commented, The invariant and consistency boundary are the keys to decide the aggregate root. Checkout informit.com/articles/article.aspx?p=2020371, one rule is "Model True Invariants in Consistency Boundaries" – ivenxu Jan 29 '18 at 9:18
  • Also, what is the collaborative profile of your application? What needs to be updated in a single transaction? There's little behavior to it - when is it called? By whom? Do a lot of users compete to get a hold of the resources? – guillaume31 Jan 29 '18 at 12:11
  • Im thinking a single transaction can either be a team, player or a game. So i'm leading towards making them seperate aggregares. Sorry for not being able to answer too well on everything because im lacking terminology knowledge. – tjugg Jan 29 '18 at 12:38
8

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 .

If Considering following Business Rules :

Business Rules Assumptions 1

  1. In the Game , Team is made at runtime and there is not existence of team after game is over .
  2. In the Game , Players are also created in runtime for any User in your system and player also have no existence after game is over
  3. We can consider example of online LUDO game here where players and teams are created at runtime .

Now Considering above rules , It seems that Player and Teams are highly correlated/coupled that Only Game aggregate Root is OK here .

If Considering following Business Rules :

Business Rules Assumptions 2

  1. Teams can exists independently of GAME , which is the more real scenario .
  2. Players also exists independently .
  3. Players can be assigned to different Teams .
  4. Team can play different Games .

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 :

  1. GameStarted
  2. TeamCreated
  3. PlayerAddedToTeam and so on.

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 .

3

is every object I listed above an aggregate root, having their own repositories since every aggregate root should have it's own repository?

It's a particular case of a more general one: your aggregate boundaries are first of all consistency boundaries. The boundaries within which all your invariants hold true at all times. There is no much behavior in your sample app, so it's hard to tell for sure, but I doubt that all invariants within each team and each player should be consistent right away.

So it could be reasonable if you had those three objects as aggregate roots.

  • 1
    Yeah, the keyword probably is could, because by asking this I realized i didnt spend enough time writing the OP. A question like this would need more explaining about the use case / domain, and I guess thats what ddd is all about. – tjugg Jan 29 '18 at 12:40
2

At the moment it looks like your game contains players and teams. So it is the aggregate root.

Presumably this maintains consistency. ie if you remove a player from a team he is no longer in that teams game?

If, as seems likely, you don't have those sorts of consistency rules to enforce. You could have the Game contain Team and Player Ids instead of the objects. Adding Methods such as GetPlayersByTeamIdAndDate() to their repositories.

Or perhaps you need a Larger aggregate root. Season or League, with multiple Games where your consistency rules make more sense and can be enforced. ie a player can only be in one team in a season.

  • Probably should have spent a little more time writing the question. The "application" which I pretty much wrote just to ask this question is I guess, a program to divide persons in to teams of a specific size. I have a bit of a hard time answering some of the answers because I don't know all the terminology too well yet. But i'd assume yes - if you remove a player from a team, the player entity would still exists, but the team would no longer contain that player. So i'm leading towards making them seperate aggregates. I did learn thought that asking a question like this is pretty hard. – tjugg Jan 29 '18 at 12:37
  • @tjugg Yes, I think the example you made has too little behavior and too few invariants to extract a meaningful aggregate design from. DDD's subtitle is tackling complexity in the heart of software for a reason ;) – guillaume31 Jan 29 '18 at 16:14
1

Too many good answers above. Just a note, and correct me if I am wrong, but it seems like you are defining agregates based on how you will store them. In DDD you do the opposite, you define aggregates based on how you use them. Like many others say, your bussiness rules will guide your design.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.