On page 134 ( DDD: Tackling Complexity in the Heart of Software ) author improves Purchase Order model by making Purchase Order PO an Aggregate Root containing PO Line Items, while Part entity is made a root of its own Aggregate ( making Part an Aggregate root does make sense, since parts will be shared by many POs ):
An implementation consistennt with this model would guarantee the
invariant relating PO and its items, while changes to the price of a
part would not have to immediately affect the items that reference it.…
But this is not an invariant that must be enforced at all times. By
making the dependency of line items on parts looser, we avoid
contention and reflect the realities of the business better. At the
same time, tightening the relationship of the PO and its line items
guarantees that an important business rule will be followed.
Author argues that changes to the part prices don’t have to immediately propagate to the PO aggregates that reference it, since:
-
locking the part when particular PO is being updated may cause contention ( due to the possibility of several POs simulaneously trying to get a lock on the same part )
-
parts get modified less frequently than POs and as such chances of POs having invalid data are relatively small
a) I understand author’s arguments, but shouldn’t the consistency of PO be a top priority in such a model and as such parts should be locked together with POs being updated, even at the risk of contention?
b) In contrast, on pages 176, 177 author did find it necessary to enforce invariants spanning two aggregates within single transaction ( ie when Handling Event is added, Delivery History should also be updated accordingly within the same transaction ):
The Delivery History holds a collection of Handling Events relevant to
its Cargo and new object must be added to this collection as part of
the transaction. If this back-pointer were not created, the objects
would be inconsistent.…
The need to update Delivery History when adding a Handling Event gets
the Cargo AGGREGATE involved in the transaction.
I can’t figure out why would maintaining consistency within single transaction be more important in this example than in PO example?
Note ( I’m assuming that by “back-pointer” he’s referring to Handling Event instance? )
c) Is there a particular reason why author didn’t as an alternative propose an implementation of Optimistic Concurency check, where Parts table would contain a rowversion field, which our code would inspect each time some PO was being updated?
d) Btw, why must Price value be copied into Line Item ( Figure 6.11, page 134 )? Can’t PO‘s invariants check the price by inspecting Part entity?
Thank you
A PO, once placed, can be regarded as an immutable event. This is why the price value must be copied. Future changes in price should not be reflected on existing POs – that would be a violation of business rules. This is why the relationship between a PO and the Part can and should be loosened. This is also why these consistency characteristics apply to a PO but not in the other scenario.