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.
Best Answer
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 theShape
could be an aggregate root on its own?Your
Shape
class should remain an only internal entity of thePicture
aggregate root when thePicture
defines some rules among multipleShape
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 theShape
s then theShape
really should be an aggregate root on its own with the following structure:and the
Picture
would have the following method: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.