In a DDD approach, suppose I have an Order
(this is my aggregate root) and a Line
(a descendant of an Order
). In both objects I have private setter for properties and a builder that handle creations of the cluster of object.
As far as I know every action on the cluster goes through the aggregate root. Is this right?
For instance, if I need a command to hide a line I can't have simply an hide()
method on the Line
object, but instead I should have an hideLine()
method in the aggregate root that traverse the cluster of object down to the Line
.
At this point I imagine a scenario like that:
// in the aggregate root
public void HideLine(Line line){
line.Hide();
}
// in the Line class
public void Hide(){
this.Hidden = true;
}
but in this way someone could do something like:
Order orderA = _repo.getById(id_A);
Order orderB = _repo.GetById(id_B);
orderB.HideLine(orderA.Lines.First());
with unknown side effects (right now the side effect is clear, but in a more complex scenario this could create headache).
So I could take this counter-measures:
- Accept an id as parameter for
HideLine
mehtd (i.e. public voidHideLine(Guid lineId)
) - Check in
HideLine(Line line)
method thatline
is actually inOrder.Lines
collection
How I handle consistency issues? Are my solutions meaningful or I have completly miss the point?
Post scriptum after comment(s)
@Songo: The Order
aggregate (note: in my real scenario it isn't an Order but a more complex cluster) starts a saga/process manager during its life that oversees the Order
publishing process (try to imagine that in a real scenario an order need to be published). As soon OrderPublishingRequestEvent
is catched the saga starts and try to validate the Order
checking if each Line
is valid, and if not the line will be hided.
Thus my command handler is something like:
public void Handle(OrderValidationCommand message)
{
// get the aggregate
Order order = _repository.GetById(message.OrderId);
// call a service to make validation
foreach (Line line in order.Lines)
{
var result = validator.Validate(line);
if (result.IsValid)
order.HideLine(line);
else
// do something else
}
order.Validate(); // raise event ValidatedOrderEvent
_repository.Update(fornitura);
}
Line
object inpublic void HideLine(Line line)
come from? I mean surely it didn't come from the database because only aggregates have repositories.order.validateLines(validator)
to encapsulate the validation logic?order.validateLines()
without parameter? Does this create a problem of high coupling avoidable in aggregate?