1

I'm trying to model a specific domain using DDD techniques and have some doubts. To better contextualize, a brief description: It's a domain of deliveries and delivery confirmation. We have two main actors:

  • Customers who bought products and need to track their deliveries

  • Drivers who receive travel schedules with multiple deliveries for various customers to deliver and record them

Each trip has a loading phase. After the loading phase, the truck leaves the distribution center to make deliveries. In this case, we say that the delivery and/or the trip are 'en route'. Both for the Customer and the Driver, it is necessary to present the loading phase info of that trip.

When the truck leaves the DC to make deliveries, we say that the delivery is en route. Each customer should see only their delivery but should track the trip from loading. When a delivery (with several items) is recorded for a customer, that delivery is considered finished. Another way to finish a delivery is through GPS monitoring. In this case, even if the driver does not record the delivery, that delivery is finished but remains pending registration.

The driver must track the entire trip and see all deliveries. The trip is finished when all deliveries are recorded or all customers have been visited (again, through GPS monitoring that indicates when the truck entered and left the customer's polygon).

I know that because it's a DDD modeling, I shouldn't focus on data but on behaviors. However, perhaps the data relationships below can help understand the context of the problem.

  • Trip 1 ---- 1 Driver

  • Trip 1 ---- 1:n Delivery

  • Delivery 1 ---- 1:n DeliveryItem

  • Delivery 1 ---- 1 Customer

As for behaviors, we would have:

Delivery Planning System:

  • Schedule Deliveries (create the Trip and all Deliveries in that trip)

Customer:

  • Check Deliveries en route deliveries with all load phases that belongs to Trip.

Driver:

  • Check Trips en route and their Deliveries
  • Check Trips with deliveries pending confirmation.
  • Record Delivery

Monitoring:

  • Record entry into the customer's polygon
  • Record exit from the customer's polygon

Questions and doubts

Initially, I thought about Aggregates: Customer, Driver, Trip, and Delivery. Customer and Driver seem quite obvious. Trip and Delivery leave me confused...

A Delivery does not exist without a Trip: deliveries are created when a trip is created by the delivery planning system, which includes customers and delivery items for a customer on a trip according to various criteria beyond the decisions of this domain.

In this case, the "schedule/create deliveries" usecase should create both the Trip and the Deliveries and their items. From this point of view, only Trip should be an aggregate, and Delivery only entities.

But when we go to the usecases "record delivery", "record customer polygon entry", "record customer polygon exit", the Delivery becomes an Aggregate with its items and confirmation records.

Customers consult and see only Deliveries but with Trip informations (load phases).

Drivers record Deliveries.

  • Would Delivery be an Aggregate?

Drivers should view the entire Trip of Deliveries that have confirmation pending.

  • If Delivery is an Aggregate, how could I query an Aggregate (Trip) based on attributes of another (Delivery)?

  • Could one usecase create 2 Aggregates (Trip and its Deliveries) in a transactional way?

2
  • 1
    Next time when posting a question, please double check how the markdown is formatted by the system. Thanks.
    – Doc Brown
    Jan 2 at 15:19
  • 1
    ... Are you shure the cardinalities are correct? Shouldn't it be possible for a customer to get multiple deliveries? And can't the same driver participate in multiple trips?
    – Doc Brown
    Jan 2 at 15:32

3 Answers 3

1

Initially, I thought about Aggregates: Customer, Driver, Trip, and Delivery. Customer and Driver seem quite obvious.

I have doubts that either Customer or Driver will actually be useful aggregates.

Aggregates tend to be documents where we copy information that is of use to some business process, rather than being sources of information ("actors").

Information from a single real world event - driver 123 placed package 456 at location 789 at 10:11am - might end up being copied into a number of different documents, which derive their own conclusions from the new information combined with information previously recorded.

For example: how would we determine that package 456 was delivered correctly? We'd probably want some sort of check that location 789 actually does match the delivery instructions for that package, and to trigger some sort of escalation/mitigation if in fact the package has been misrouted.

It may be useful to review "the blue book", as Eric Evans leans on a cargo shipping domain for a number of his examples.

2
  • Thank you for the response, VoiceOfUnreason! When I mentioned Customer and Driver as Aggregates, I understand that they do not belong to this subdomain or delimited context. They are Aggregates with their lifecycles managed by other applications. In both cases, I only maintain the ID in Trip and Delivery. Jan 2 at 9:35
  • Regarding the question "how would we determine that package 456 was delivered correctly?": business rules do not require a more advanced delivery control mechanism, only its registration. For example, whenever the driver records that it was not possible to deliver an item, it is necessary to redirect the product to another customer, reducing costs My questions are about the Trip and Delivery Aggregates: for Customers and the registering delivery process, Delivery should be an Aggregate. For Drivers who receive and carry out a trip, Trip should be an Aggregate. Jan 2 at 9:40
1

I know that because it's a DDD modeling, I shouldn't focus on data but on behaviors.

Excellent start!

Also: The whole distinction between "aggregates" and "entities" is an arbitrary one, in the sense that it helps to understand what kinds of objects there might be, but it's not a designing tool. You don't have to actually know or plan which is which.

The design tool is actually whether you can express your behavior correctly using your model, without resorting to "getting" information out of objects, or using other methods that are technical in nature. If you do this, aggregates (in the sense of entry points into use-cases, or where the "transactions live") will emerge organically.

There are a couple of ideas that might help:

"Monitoring"

public interface Trip {
   void at(Position position);
}

The Trip presumably knows which customers need to be visited, so it needs to decide whether a customer polygon was entered or not. Hence, it needs to be fed the position and it will update itself accordingly.

Customer checks en-route deliveries

I would be cautious with such requirements. Normally this is a UI-bound requirement to display some information. I'm actually in favor of not "hiding" requirements like this with returning a "list of deliveries with statuses" or something like that, but to actually return the Page, Html or even Json of whatever is actually needed to be returned by the system.

The modeling for this is quite easy:

public interface Customer {
   Page displayEnRouteDeliveries();
}

How that information gets collect is up to the object, we don't really care and it doesn't really matter.

Record delivery

This seems obvious, but:

public interface Delivery {
   void record();
}

Don't complicate it. You'll find out whether this works when you try to implement it and you can't for some reason. But this is a good start.

Btw: Is this an aggregate? Again, we shouldn't really care, as long as we don't pull information out and try to reach into whatever internal models it might have. As long as you define business behavior on your models, and nothing else, you should be fine.

Ps.: I'm using interfaces everywhere, but they can be classes too, it's just shorter this way.

1
1

I think your approach is fine in general, it seems there are only a few terminology issues, so lets focus on them.

Your design makes a Trip plus it's Delivery entities (plus further child objects, maybe value objects or other entities) an aggregate, so the "aggregate" is not a single object, it is a set of objects. "Trip" is the aggregate root, and Delivery just a normal entity with child entities. Aggregates can span more than one level in a tree-like hierarchy. Have a look at these Stackoverflow Q&As, they provide more examples for hierarchical aggregates and the DDD terms:

But when we go to the usecases "record delivery", "record customer polygon entry", "record customer polygon exit", the Delivery becomes an Aggregate with its items and confirmation records.

No, "Delivery" is not an aggregate (and definitely not an aggregate root), it should be just an entity. I would not model it as a value object, since you want to refer to certain Deliveries inside the aggregate and hence they need an identity on their own.

An importan point is, however, not to store any outside references to Delivery objects directly (for example, in context of Customer or Driver object). One can store their IDs, but when it comes to manipulating the status of a Delivery, it should be done exclusively through the Trip object:

   trip.recordDelivery(deliveryID);

and not

   delivery.record();

(at least not from classes / object outside the aggregate - inside this is ok).

That's important to give the Trip object a chance to maintain it's invariants.

If Delivery is an Aggregate, how could I query an Aggregate (Trip) based on attributes of another (Delivery)?

Well, as I said, in your design Delivery is not an aggregate. You query this like

 deliveriesToProcess = trip.getPendingDeliveries();

Could one use case create 2 Aggregates (Trip and its Deliveries) in a transactional way?

Since Trip and it's Deliveries are objects in one aggregate, not two, they can be created in one transaction. However, it is not "forbidden" in DDD to create a Trip first, and add Deliveries later, in more than one transaction. Still such operations should be done through the Trip object to make sure it can maintain things like a counter of all Deliveries or other invariants.

1

Your Answer

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

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