Gestione della radice aggregata con la gerarchia degli oggetti profondi

3

Qual è il modo corretto di applicare i comandi agli oggetti in profondità nella gerarchia del modello?

Dato il seguente modello:

public class Picture : AggregateRoot
{
    private string title;
    private List<Shape> shapes;
}

public class Shape
{
    private ShapeType Type;
    private Color color;
}

Possiamo definire un comando CreatePicture e un evento PictureCreated:

public class CreatePicture
{
    Guid PictureId { get; set;}    
    string Title { get; set;}
}

public class PictureCreated
{
    Guid PictureId { get; set;}    
    string Title { get; set;}    
}

Una semplice implementazione del gestore comandi potrebbe essere simile a questa:

var Picture = new Picture(cmd.PictureId, cmd.Title); // Internally store a PictureCreated event
repo.Add(Picture);
repo.Save(); // PictureCreated published

Quindi possiamo definire un comando AddShape e un evento ShapeAdded:

public class AddShape
{
    Guid PictureId { get; set;}
    ShapeType Type { get; set;}
}

public class ShapeAdded
{
    Guid PictureId { get; set; }
    Guid ShapeId { get; set;}
    ShapeType Type {get; set; }
}

Ancora una volta, il gestore di comando ha questo aspetto:

var picture = repo.Get<Picture>(cmd.PictureId);
picture.AddShape(cmd.ShapeId, cmd.Title);
repo.Save(); // ShapeAdded published

Ma ora definiamo un comando ChangeShapeColor e l'evento ShapeColorChanged:

public class ChangeShapeColor
{
    Guid PictureId { get; set; }
    Guid ShapeId { get; set;}
    Color Color { get; set;}
}

public class ShapeColorChanged
{
    Guid PictureId { get; set; }
    Guid ShapeId { get; set;}
    Color Color { get; set;}
}

Non sono sicuro di come dovrebbe essere implementato il gestore di comandi:

Opzione 1. Tutte le operazioni passano attraverso l'immagine:

var picture = repo.Get<Picture>(cmd.PictureId);       
picture.ChangeShapeColor(cmd.ShapeId, cmd.Color);
repo.Save(); // ShapeColorChanged published

Opzione 2. Le operazioni passano attraverso la forma, che cerchiamo da Picture:

var picture = repo.Get<Picture>(cmd.PictureId);
var shape = Picture.GetShape(cmd.ShapeId);
shape.SetColor(cmd.Color);        
repo.Save(); // ShapeColorChanged published

L'opzione 1 non sembra troppo male finché non iniziamo a costruire un grafico più complicato. per es.

Building --(1..n)--> Floor --(1..n)--> Room --(1..n)--> Desk --(1..n)--> Equipment

Sembra anche che la radice aggregata si sia trasformata in un "controllore" (mi dispiace se questa è la terminologia sbagliata) e non è più un oggetto di dominio attuale.

Qual è il modo corretto di gestirlo? Posso trovare solo esempi banali / semplici online in cui il modello è praticamente piatto.

Molte grazie in anticipo.

    
posta Karle 19.08.2017 - 20:20
fonte

2 risposte

5

Se vuoi farlo nel modo giusto, come suggerisce DDD, allora ti è permesso solo cambiare il colore della forma usando direttamente un metodo sulla radice aggregata e non puoi farlo direttamente sulla forma. La radice aggregata è autorizzata ad esporre le sue entità interne come oggetti di sola lettura (ad es. Esporre le raccolte interne come IEnumerable).

Tuttavia, una buona progettazione aggregata è anche un progetto semplice e quando si arriva a una situazione in cui la radice aggregata cresce, la prima domanda che si dovrebbe porre è: Quale contesto l'aggregato fornisce effettivamente per le entità?

Nel tuo caso, dovresti pensare se il Shape è in realtà solo un'entità interna o se Shape potrebbe essere una radice aggregata autonoma?

La tua classe Shape dovrebbe rimanere una sola entità interna della radice di% com_de% aggregata quando Picture definisce alcune regole tra più istanze di Picture , ad esempio: Un'immagine non può contenere due forme dello stesso colore . Dopo aver definito questa regola, la radice aggregata - la conoscenza di tutte le sue forme - ora può verificare questa regola.

Tuttavia, se la radice aggregata Shape non definisce realmente alcuna regola in quanto tale e memorizza solo internamente Picture s, la Shape in realtà dovrebbe essere una radice aggregata autonoma con la seguente struttura:

class Shape
{
    private ShapeId shapeId;
    private PictureId associatedPictureId;
    private ShapeType type;
    private Color color;
}

e Shape avrebbe il seguente metodo:

class Picture
{
    private PictureId pictureId;

    public Shape AddShape(ShapeId shapeId, ShapeType shapeType, Color color)
    {
        return new Shape(shapeId, pictureId, shapeType, color);
    }
}

Pensa sempre a una radice aggregata come a una protezione dei limiti che la radice aggregata definisce per le sue parti interne. Se non definisce alcun limite e regola per le sue parti interne, suddividi le parti interne in radici aggregate separate per rimuovere la complessità.

    
risposta data 20.08.2017 - 18:50
fonte
1

L'opzione 1 IMHO è quella che si adatta meglio ai principi della programmazione. Anche se sembra che nei grafici complessi la soluzione complichi le cose, non lo fa, mantiene la complessità all'interno di aggregate , a cui appartiene.

L'opzione 2 rompe la Legge di Demetra , un principio importante. Inoltre, per i grafici complicati, sposta la complessità / knowkedge in Application services che diventa più intelligente che dovrebbero essere.

    
risposta data 20.08.2017 - 07:44
fonte

Leggi altre domande sui tag