7

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?

4
  • Are you entities event sourced? If the answer is no, it's better if they do not even know the event concept exists within your system and do not react to it directly.
    – Andy
    Jun 29, 2017 at 13:29
  • @DavidPacker The aggregate itself is event-sourced which means the entities under it, which form a part of the aggregate's state, also are, if I understand correctly. In my example, ItemAdded is an event that is important to the system, which also affects the OrderLine entity internally. Jun 29, 2017 at 13:39
  • 1
    No. Having an event sourced aggregate does not mean you MUST have event sourced entities or value objects. Let's say an event sourced User Aggregate Root (AR) is constructed with an Email value object. When the user changes email the Email VO is not responsible for holding this information, the User AR is.
    – Andy
    Jun 29, 2017 at 13:46
  • @DavidPacker I think it's easier to say that for value objects as you can create them, and replace them with something of equal value. Entities however, are usually chosen to be represented as such, having identities in them, because they are usually expected to change over time. In any case, yes, in my code the internal entities are also event sourced. Jun 29, 2017 at 13:56

4 Answers 4

2

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.

3
  • I actually considered passing the event to the constructor before but when I considered that I might need other apply methods in the future for things like QuantityUpdated, etc. I thought it'd be more consistent to keep apply for ItemAdded as is. As for naming my events with ~Event suffix, I personally prefer not to do it. I think it's easily understandable that classes ending in past tense action words are events. I agree with creating an Item model though, but I think that's beyond the context of my question. Jun 29, 2017 at 18:35
  • The name ItemAdded already perfectly describes an event. Appending the Event suffix at the end of the class name seems meaningless.
    – Andy
    Jun 30, 2017 at 5:50
  • @DavidPacker really? I was actually confused. I've seen developers use variable names with all kinds of strange verb conjugation where it didn't make sense. My belief is that code is meant to be read by people and being explicit is always to be favored over being terse or implicit. If something is an Event, or some other noun, then that should be reflected in the name. But of course others may have a different view. Jun 30, 2017 at 6:11
1

You've got one thing wrong in your design. It seems like you're modeling according to a relational database rather than your domain. Why do I think so? Because you have an Order aggregate root, which internally contains an OrderLine holding Items. You have modeled an M:N relationship using the OrderLine class.

The thing is, when using DDD you do not model to match your data, so your structure should not reflect the relational database either.

Ask yourself the following:

  1. What does the business care about?
  2. Does the business care you need to have an additional (most likely weak) entity called OrderLine in order to map items to an order?
  3. Does business care you need to add/remove records form the mapping OrderLine table?

The answers to questions 2 and 3 are no. Your business managers could not care less how you model the relationship, but what they are going to care about when it comes to statistics is the following:

  • How many items does on average have an Order which gets completely processed?
  • How many times do people add an Item to an Order and the remove the Item from the Order?
  • What is the average cost of an Order which gets completely processed?
  • How many Orders does a User on average have?
  • How many times has an Item been removed from an Order only to be added back?

These are all valid questions which may come from the business department in the future. I have highlighted the affected aggregates in each of those questions. As you can see, there is no OrderLine at all, because that entity really just models the relationship. Your business cares about your Items and Orders and so should you.

With this in mind, the OrderLine really should just be a simple List/Vector/Set containing most likely very simple value objects/entities like this one:

class OrderLineItem extends Entity {
    private ItemId itemId;
    private int quantity = 1;

    public bool Equals(Entity other) {
        return
          other instanceof OrderLineItem &&
          itemId.Equals(other.itemId);
    }
}

An Item is referenced only by an id, because you SHOULD NOT hold other aggregates directly.

As you can see with this approach, you no longer need to care whether the OrderLine should accept an event directly, because the entity is simply gone.


In an event sourced system the M:N relationship table will not exist at all, because your datastore are events, not relations.

When building read models from your events for the query side, the read models should be as flat as possible. Ideally you should be able to do SELECT * FROM read_model WHERE ... and nothing else. No joins. So it's quite likely you will not have the the M:N relationship on the read side either. To be honest, because the read models are documents anyway, it makes much more sense to use a different, more fitting database such as MongoDB.

2
  • Thanks for your very insightful contribution. However, I think you're looking much into the Order model, which I originally meant to be a disposable example to help illustrate my idea. Jun 30, 2017 at 8:12
  • For now, let's assume the model is sound. I am really more interested on how nested entities work for event-sourced aggregates. Jun 30, 2017 at 8:20
1

Which one is more appropriate?

I'm almost certain the first approach is the one that will be healthier in the long run.

Riddle: if the businesses understanding of OrderLine were to change over the lifetime of the domain model, which design is going to be easier to work with?

In the first case, you extend the ItemAdded message to include the new fields that you need, and you update the OrderLine item to read those new fields and do something interesting, and that's it.

In the second case, you still have to extend the ItemAdded message, and you still need to modify OrderLine, and you also have to modify Order. That doesn't sound nearly as good.

Parnas wrote in ... Decomposing Systems Into Modules

We propose instead that one begins with a list of difficult design decisions or design decisions which are likely to change. Each module is then designed to hide such a decision from the others.

In this example, the OrderLine is the module that is hiding the decision "how to we consume an ItemAdded message?"

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.

Command Query Separation and Role Interfaces can help here. The rough idea is to think about the OrderItem as having different roles that it can play. So the interface the OrderItem implements would be divided into different slices, one of which would be an IApplyEvents or even IHandle<ItemAdded>, and you design your logic so that the roles that allow the item to be modified are only exposed in the use cases where you want them.

So if you have an accessor like getOrderLines, it returns a collection of RoleInterfaces that are composed entirely of queries.

interface OrderLineDetails {
    ProductDetails getProductDetails();
}

interface IHandle<Event> {
    void apply(Event event);
}

class OrderLine implements OrderLineDetails, IHandle<ItemAdded> {
    // ...
}

Names can get messy; everybody wants "OrderLine" to mean what it means in that specific context. DependencyInversion can help here.

One way to prevent this is to return clones, but that can get a little cumbersome to implement for fairly complex nested entities.

Sometimes your fairly complex nested entities are really complex nested values, which may simplify things.

State next = current.apply(change)

is pretty reasonable. In the abstract, anyway. Be careful to recognize what complexity is coming from the idea, and what from the tools you happen to be using; it may be a hint that you need something that fits the problem better.

1
  • Thanks! For now, I decided to go with approach 1, because, like you said, it makes the code easier to adapt to changes. To address the issue of exposing the apply method that triggers state transition, I limited its scope so that only the aggregate root can access it at the moment. However, when my code base reaches the degree of complexity, I can see how your ideas can really help. Jul 3, 2017 at 14:05
0

no rules there. It depends on so much: - Your persistence model - If you are appplying CQRS - the complexity & the invariants of your entities.

I usually relies on an abstraction for my AR & ES that cascade naturally events to any ES interface in the same AR.

But most of my ES aggregate roots are small and quite “simple”, I try to design them with only VO. Usually, your AR shouldn’t mutate another entity state.

In your example, I wouldn’t ES an OrderLine. Unless you have quite some complexes rules that care & apply when OrderLine change its state. Considering your current business behavior: add an item. I would design OrderLine as a VO.

Since you are ES’ing your Order, I can’t find any usage to ES the lines. You will always be able to track classic e-commerce lifecycle.

Cascading ES means your aggregate is very complex, question your design before going there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.