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
HideLinemehtd (i.e. public voidHideLine(Guid lineId)) - Check in
HideLine(Line line)method thatlineis actually inOrder.Linescollection
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); }
Lineobject 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?