web-dev-qa-db-fra.com

DDD: identification de la racine agrégée dans un exemple de domaine d'application simple

J'essaie de lire sur DDD, et j'ai du mal à identifier les racines agrégées. J'ai écrit une application vraiment simple pour diviser les joueurs en différentes équipes dans un jeu.

Donc, mes entités sont quelque chose comme ça:

Entité de jeu:

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);
        }
    }
}

Entité d'équipe:

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);
    }
}

Entité joueur:

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;
    }
}

Maintenant, je pensais d'abord que le jeu serait une racine agrégée. Cela aurait du sens dans un sens. Mais ensuite, j'ai commencé à penser que si vous voulez faire persister les joueurs séparément afin de ne pas avoir à ajouter de nouveaux joueurs pour chaque jeu? Que faire si vous souhaitez conserver les équipes séparément si vous avez des équipes qui peuvent être réutilisées plus tard? Le jeu lui-même, serait racine agrégée, car je persisterais des jeux pour par exemple charger un historique de jeux de persistance.

La question est donc la suivante: chaque objet répertorié ci-dessus est-il une racine agrégée, ayant ses propres référentiels puisque chaque racine agrégée doit avoir son propre référentiel?

Merci d'avance.

6
tjugg

Décision selon laquelle ce qui doit être sélectionné comme racine agrégée dépend fortement des règles métier de votre application. Si, par exemple, deux entités disent que A et B sont fortement dépendants, c'est-à-dire que certaines opérations sur l'entité B nécessitent un changement de titre A, alors A et B doivent être sous la même racine agrégée. En bref, si la cohérence est nécessaire entre deux entités, elles peuvent avoir la même racine agrégée.

Dans votre cas, cela dépend aussi des règles métier que ce qui doit être sélectionné comme un agrégat.

Si vous envisagez les règles commerciales suivantes:

Hypothèses des règles commerciales 1

  1. Dans le jeu, l'équipe est constituée au moment de l'exécution et il n'y a plus d'équipe après la fin du jeu.
  2. Dans le jeu, les joueurs sont également créés au moment de l'exécution pour tout utilisateur de votre système et le joueur n'a pas non plus d'existence après la fin du jeu.
  3. Nous pouvons considérer un exemple de jeu en ligne LUDO ici où les joueurs et les équipes sont créés au moment de l'exécution.

Considérant maintenant les règles ci-dessus, il semble que le joueur et les équipes soient fortement corrélés/couplés que seule la racine agrégée du jeu est OK ici.

Si vous envisagez les règles commerciales suivantes:

Hypothèses des règles commerciales 2

  1. Les équipes peuvent exister indépendamment de GAME, qui est le scénario le plus réel.
  2. Les joueurs existent également indépendamment.
  3. Les joueurs peuvent être affectés à différentes équipes.
  4. L'équipe peut jouer à différents jeux.

Compte tenu des règles ci-dessus, il semble que le jeu, le joueur et l'équipe devraient exister en tant que racines d'agrégat distinctes.

Une approche différente pour décider des agrégats peut être l'assaut d'événement, où vous écrivez d'abord une seule source de vérité, c'est-à-dire les choses qui se sont produites, par exemple:

  1. GameStarted
  2. TeamCreated
  3. PlayerAddedToTeam et ainsi de suite.

En définissant ces événements, Business deviendra également plus clair pour vous.

Maintenant, écrivez quelle commande peut provoquer ces événements et continuez à écrire le flux.

Après un certain temps, il deviendra clair que les commandes et les événements doivent provenir d'un seul point et peuvent être sélectionnés comme un agrégat.

Mais veuillez noter qu'après cela, vous devez également vérifier les règles commerciales si vous devez en outre casser l'agrégat ou non.

J'espère que cela dissipera certains doutes.

Veuillez commenter si vous avez un doute en réponse.

Merci d'avance.

8
Sahil Aggarwal

chaque objet que j'ai répertorié au-dessus d'une racine agrégée a-t-il ses propres référentiels puisque chaque racine agrégée doit avoir son propre référentiel?

C'est un cas particulier, plus général: vos limites globales sont d'abord des limites de cohérence. Les limites à l'intérieur desquelles tous vos invariants sont toujours d'actualité. Il n'y a pas beaucoup de comportement dans votre exemple d'application, il est donc difficile de dire avec certitude, mais je doute que tous les invariants au sein de chaque équipe et de chaque joueur devraient être cohérents immédiatement.

Il est donc pourrait être raisonnable si vous aviez ces trois objets comme racines agrégées.

3
Zapadlo

Pour le moment, il semble que votre jeu contient des joueurs et des équipes. C'est donc la racine agrégée.

Vraisemblablement, cela maintient la cohérence. c'est-à-dire que si vous retirez un joueur d'une équipe, il n'est plus dans ce jeu par équipes?

Si, comme cela semble probable, vous n'avez pas ce genre de règles de cohérence à appliquer. Vous pouvez faire en sorte que le jeu contienne des identifiants d'équipe et de joueur au lieu des objets. Ajout de méthodes telles que GetPlayersByTeamIdAndDate () à leurs référentiels.

Ou peut-être avez-vous besoin d'une racine agrégée plus grande. Saison ou ligue, avec plusieurs jeux où vos règles de cohérence ont plus de sens et peuvent être appliquées. c'est-à-dire qu'un joueur ne peut appartenir qu'à une seule équipe au cours d'une saison.

2
Ewan

Trop de bonnes réponses ci-dessus. Juste une note, et corrigez-moi si je me trompe, mais il semble que vous définissez des agrégats en fonction de la façon dont vous les stockerez. Dans DDD, vous faites le contraire, vous définissez des agrégats en fonction de la façon dont vous les utilisez. Comme beaucoup d'autres le disent, vos règles commerciales guideront votre conception.

1
Alex Favieres