DDD: identificazione della radice aggregata in un dominio di applicazione di esempio semplice

2

Sto cercando di leggere su DDD, e sto faticando un po 'cercando di identificare le radici aggregate. Ho scritto un'app molto semplice per dividere i giocatori in squadre diverse all'interno di un gioco.

Quindi le mie entità sono qualcosa del genere:

Entità del gioco:

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à team:

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à giocatore:

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

Ora pensavo che il gioco sarebbe stato una radice aggregata. Avrebbe senso in un modo. Ma poi ho iniziato a pensare che cosa succederebbe se volessi mantenere i giocatori separatamente in modo da non dover aggiungere nuovi giocatori per ogni partita? Cosa succede se si desidera mantenere le squadre separatamente se si dispone di squadre che possono essere riutilizzate in seguito? Il gioco in sé, sarebbe una radice aggregata, perché vorrei persistere nei giochi per esempio caricare una cronologia di giochi dalla persistenza.

Quindi la domanda è, è ogni oggetto che ho elencato sopra una radice aggregata, con i propri repository dato che ogni radice aggregata dovrebbe avere il proprio repository?

Grazie in anticipo.

    
posta tjugg 29.01.2018 - 09:32
fonte

4 risposte

7

Decisione che ciò che deve essere selezionato come radice aggregata dipende in gran parte dalle regole aziendali dell'applicazione. Se per esempio ci sono due entità che affermano che A e B sono altamente dipendenti, ad esempio alcune operazioni sull'entità B richiedono cambiamenti nell'entità A, quindi A e B dovrebbero trovarsi nella stessa radice aggregata. In breve, se è necessaria la coerenza tra due entità rispetto a quella che può avere la stessa radice aggregata.

Nel tuo caso dipende anche dalle regole aziendali che cosa dovrebbe essere selezionato come aggregato.

If Considering following Business Rules :

Assunzioni delle regole aziendali 1

  1. Nel gioco, il team viene creato in fase di runtime e non esiste esistenza della squadra dopo partita finita.
  2. Nel gioco, i giocatori vengono creati anche in runtime per qualsiasi utente nel tuo sistema e il giocatore non ha alcuna esistenza dopo che il gioco è finito
  3. Possiamo considerare un esempio di gioco LUDO online qui dove giocatori e team vengono creati in fase di runtime.

Ora considerando le regole precedenti, sembra che i giocatori e le squadre siano altamente correlati / accoppiati che la radice di aggregazione di Solo gioco è OK qui.

If Considering following Business Rules :

Assunzioni delle regole aziendali 2

  1. I team possono esistere indipendentemente da GAME, che è lo scenario più reale.
  2. I giocatori esistono anche indipendentemente.
  3. I giocatori possono essere assegnati a diversi team.
  4. Il team può giocare a giochi diversi.

Ora considerando le regole precedenti, sembra che Gioco, Giocatore e Squadra debbano esistere come radici aggregate separate.

Un approccio diverso per decidere gli aggregati può essere un evento tempestoso, in cui per prima cosa scrivi un'unica fonte di verità, ovvero le cose che sono accadute ad esempio:

  1. GameStarted
  2. TeamCreated
  3. PlayerAddedToTeam e così via.

Nella definizione di questi eventi, Business diventerà anche più chiaro per te.

Ora scrivi ciò che Command può causare questi eventi e continua a scrivere il flusso.

Dopo qualche tempo diventerà chiaro che i comandi e gli eventi dovrebbero provenire da un singolo punto e che possono essere selezionati come un aggregato.

Ma tieni presente che anche dopo questo devi controllare le regole aziendali se hai bisogno di rompere il totale o meno.

Spero che questo chiarisca alcuni dubbi.

Si prega di commentare se avete qualche dubbio in risposta.

Grazie in anticipo.

    
risposta data 31.01.2018 - 18:01
fonte
3

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

È un caso particolare di uno più generale: i tuoi confini aggregati sono prima di tutto confini di consistenza. I limiti entro i quali tutti i tuoi invarianti rimangono fedeli in ogni momento. Non c'è molto comportamento nella tua app di esempio, quindi è difficile dirlo con certezza, ma dubito che tutti gli invarianti all'interno di ogni squadra e ogni giocatore siano immediatamente coerenti.

Quindi potrebbe essere ragionevole se avessi quei tre oggetti come radici aggregate.

    
risposta data 29.01.2018 - 10:15
fonte
2

Al momento sembra che il tuo gioco contenga giocatori e squadre. Quindi è la radice aggregata.

Presumibilmente ciò mantiene la coerenza. cioè se rimuovi un giocatore da una squadra non è più in quel gioco di squadre?

Se, come sembra probabile, non hai quel tipo di regole di coerenza da applicare. Potresti avere il Gioco con Id Team e Player invece degli oggetti. Aggiunta di metodi come GetPlayersByTeamIdAndDate () ai loro repository.

O forse hai bisogno di una radice aggregata più ampia. Stagione o Lega, con più giochi in cui le regole di coerenza hanno più senso e possono essere applicate. cioè un giocatore può essere in una sola squadra in una stagione.

    
risposta data 29.01.2018 - 11:31
fonte
1

Troppe buone risposte sopra. Solo una nota e correggimi se sbaglio, ma sembra che tu stia definendo gli aggregati in base a come li memorizzerai. In DDD fai il contrario, definisci gli aggregati in base a come li usi. Come molti altri dicono, le tue regole di bussiness guideranno il tuo design.

    
risposta data 01.02.2018 - 11:45
fonte