Should internal entities in an aggregate respond to domain events directly

cqrsdomain-driven-designevent-sourcing

I am fairly new to implementing CQRS and Event Sourcing, while applying rules of domain driven design. Let's say I have an Order aggregate and inside it is a list of OrderLine entities. I'm using this model in the context of an event-sourced system (with CQRS) and so the state changes are mainly driven by events. Changes to the aggregate should run through the Order as it serves as the aggregate root. When, for example, an event like ItemAdded is raised, the respective Order aggregate will be called to apply it to itself, something like order.apply(itemAdded). This will then trigger a creation of a new internal OrderLine object to represent the newly added item in the order. In terms of implementation, does it make sense for the new OrderLine object to apply the event to itself directly? Or, should I just leave it to the Order aggregate to create the OrderLine object itself through a constructor?

Which one is more appropriate?

void apply(ItemAdded itemAdded) {
    OrderLine orderLine = new OrderLine();
    oderLine.apply(itemAdded);
    orderLines.add(orderLine);
}

or

void apply(ItemAdded itemAdded) {
    OrderLine orderLine = new OrderLine(
        itemAdded.getId(), 
        itemAdded.getProductId(), 
        itemAdded.getQuantity(), 
        itemAdded.getPrice()
    );
    orderLines.add(orderLine);
}

For now, I think the first approach is more concise and easier to understand. There are events whose fields can get very lengthy and having the internal entity itself deal with construction makes the code cleaner. The problem, however, I think, is that, by providing an apply method in the OrderLine, I'm opening it up for possible mutation outside the context of the Order. If I provide an accessor, say, getOrderLines(), in Order, even if I make the collection/list itself unmodifiable, the objects contained will still be mutable. One way to prevent this is to return clones, but that can get a little cumbersome to implement for fairly complex nested entities.

To address this, what I'm thinking is to keep the apply method in OrderLine class, but limit its visibility. The approach that gives me most flexibility in this regard is to make OrderLine a nested class under Order class. This means I can keep the apply method private but still accessible within the context of Order class. However, this could mean a very long Order class especially if I need to add more nested entity in the future.

Alternatively, I can limit access to the apply method at package level, which means I have to do some restructuring to limit all domain classes in one package.

Any advice regarding this?

Best Answer

If we take your question solely at face value, then the answer is simply to pass the itemAdded object into the constructor of OrderLine instead of providing an apply() method. In that way, the only difference between your two examples is whether or not you are passing the whole object in a single parameter vs its properties as multiple parameters.

But, if itemAdded is actually an Event and not an Item, you should rename it to be something like itemAddedEvent. You might also consider having an Item class that represents the Item, and construct that from the Event. Then you would be able to pass an Item into the OrderLine. It might make your domain more in line with how a business expert or customer would talk about the process.

Related Topic