I have a problem understanding how to separate aggregates in DDD. At least, I have a conflict of interests that I do not know how to solve. As far as I understand, in DDD an aggregate is defined as a transactional boundary that enforces invariants / consistency. However, this results in my aggregate being huge and difficult to maintain.
My use case is a system for processing e-commerce orders. The basic flow based on the business requirements is as follows:
- External system (shopping cart) creates an
Order, containing shipping address, billing address and line items. - The order is fulfilled by creating a
FulfillmentOrder, which is sent to an external fulfillment service provider. - The external fulfillment service sends the goods and creates one or several
Shipments for a given fulfillment order. Each shipment includes shipped line items, a departure country (the country of the warehouse of this external service) and a destination country (=== shipping address country) - An
Invoiceis created for eachshipment, with VAT / sales tax calculated based on departure and destination country of theShipment.
This is a simplified version of the domain. As you can see, there is a sequence of well-defined steps, but there are some entanglements.... Steps depend on data from previous steps and different entities. Most importantly: the relationship between Order and Shipment (and therefore Order and Invoice) is 1:many and not 1:1.
Based on the aggregate definition above I would design the described use case as one Bounded Context (order processing) with Order being my aggregate root and Fulfillment Order, Shipments and Invoices being collections of entities managed by the aggregate root.
There are several invariants, that span across all multiple entities, for example:
- Total sum of all invoices must not exceed the order total.
- You cannot create an invoice that includes a non exiting order item.
- A shipment item qty must not exceed the qty of the fulfillment order that triggered the shipment.
Having the order as the sole aggregate root allows enforcing these invariants. But it results in the order aggregate being huge and hard to maintain.
On the other hand I could model Order, Fulfillment Order, Shipment and Invoice as aggregates each. This would make each aggregate slimmer and more focused. But I lose the ability to enforce the invariants above (which is obviously bad for business, but also against DDD principles)
Is there any guidance in domain modelling in which direction to go or some comparisons of benefits / drawbacks of strategies?