2

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 void HideLine(Guid lineId))
  • Check in HideLine(Line line) method that line is actually in Order.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);
}
  • Just wondering, but where did the Line object in public void HideLine(Line line) come from? I mean surely it didn't come from the database because only aggregates have repositories. – Songo Nov 12 '14 at 1:05
  • I answered. Check the updated question and let me know what do you think about it. – BAD_SEED Nov 12 '14 at 9:27
  • hmmmm why don't you make a function order.validateLines(validator) to encapsulate the validation logic? – Songo Nov 12 '14 at 9:47
  • And what about to call the validator directly inside the aggregate? Doing order.validateLines() without parameter? Does this create a problem of high coupling avoidable in aggregate? – BAD_SEED Nov 12 '14 at 10:10
  • well it depends :) Validation is a nasty topic which depends on the context. Why are you validating the order? Normally I would validate the aggregate in the constructor or in a dedicated factory class if the validation logic is complex enough. A validator is a service which may change implementation at some time and I hate injecting service s inside entities. It all depends on the complexity of the validation. – Songo Nov 12 '14 at 11:42
1

The Command Handler doesn't seem like the best place to put line hiding logic. It should contain orchestration but not domain rules.

Is there a way you could give the validator to Order and have it validate itself, as a single action executed in one transaction ? That would solve the Line access problem at the same time.

Maybe this would be more in line with the domain too, since I doubt you have "hide a line" as part of your ubiquitous language. It seems to me more like a side effect of validating Order lines, not a domain requirement.

  • You're right. This sounds good. The question is (I imagine the answer but just t be sure....): why order can't call the validator and do the validation in a way completly autonomus? – BAD_SEED Nov 12 '14 at 11:39
  • Yes, why not ? Whether the Order receives the Validator from outside or instantiates it directly probably depends on whether the Validator needs references to data from outside the aggregate. Validator could also be a simple Specification if the validation logic doesn't require external resources. – guillaume31 Nov 12 '14 at 12:14

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.