3

What is the correct way to apply commands to objects deep within the model hierarchy?

Given the following model:

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

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

We can define a CreatePicture command and PictureCreated event:

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

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

A simple implementation of the command handler might look like this:

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

Then we can define an AddShape command and ShapeAdded event:

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

Again, the command handler looks like this:

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

But now, we define a ChangeShapeColor command and ShapeColorChanged event:

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

I'm not sure how the command handler should be implemented:

Option 1. All operations go via the Picture:

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

Option 2. Operations go via the shape, which we look up from the Picture:

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

Option 1 doesn't seem too bad until we start building up a more complicated graph. e.g.

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

It also seems like the aggregate root has turned in to a 'controller' (sorry if this is the wrong terminology) and is not an actualy domain object anymore.

What is the correct way to handle this? I can only find trivial/simple examples online where the model is practically flat.

Many thanks in advance.

6

If you want to do it the right way as DDD suggests, then you are only allowed to change shape colour by using a method on the aggregate root directly and you cannot do it on the shape directly. The aggregate root is only allowed to expose its internal entities as read-only objects (e.g. exposing internal collections as IEnumerable).

However, good aggregate design is also a simple design and when you come to a situation in which your aggregate root grows, the first question you should ask yourself is: Which context does the aggregate actually provide for the entities?

In your case, you should think whether the Shape really is just an internal entity, or whether the Shape could be an aggregate root on its own?

Your Shape class should remain an only internal entity of the Picture aggregate root when the Picture defines some rules among multiple Shape instances, such as: A picture may not contain two shapes of the same color. Having defined this rule, the aggregate root - knowing about all of its shapes - can now verify this rule.

But, if the Picture aggregate root does not really define any rule as such and only internally stores the Shapes then the Shape really should be an aggregate root on its own with the following structure:

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

and the Picture would have the following method:

class Picture
{
    private PictureId pictureId;

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

Always think of an aggregate root as a protector of boundaries which the aggregate root defines for its internal parts. If it does not define any boundaries and rules for its internal parts, split the internals into separate aggregate roots to remove complexity.

1

IMHO option 1 is the one that conforms best with the principles from programming. Even if it seems that in complex graphs the solution complicate things, it does not, it keeps the complexity inside the aggregate, where it belongs.

Option 2 breaks the Law of Demeter, an important principle. Also, for complicate graphs, it moves the complexity/knowkedge in the Application services which become more inteligent that they should be.

  • I think that the implementation might be confusing the theory for me. I guess that all commands operate on an Aggregate (which is multiple domain objects) via the Aggregate Root only (which is the top object in the bounded context)? – Karle Aug 20 '17 at 9:54
  • 1
    Indeed, the Aggregate root is the only way to access its nested entities. You should not assume/depend on any internal structure. You should protect Aggregates encapsulation. – Constantin Galbenu Aug 20 '17 at 10:07

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.