Discover a novel technique for discovering where to draw aggregate boundaries between domain entities… by taking them into the fourth dimension!
Video transcript & code
Imagine it’s our job to design an order entry system for a wholesaler of magical artifacts, and it’s time to sketch out an initial data model for the system.
It’s pretty obvious that an order entry system is going to need Order model. And everyone knows that Orders have LineItems, right? A LineItem in turn has a quantity and a particularly Artifact being ordered. Artifacts have various attributes associated with them, like a name and a price-per-unit.
Is this an adequate model? Well, consider what happens when a sales rep is talking to one of their favorite merchants. The merchant complains that another regional wholesaler is offering a better price on eye-of-newt, so the sales rep does a price-match just for them.
What happens to every other open order in the system that contains eye-of-newt? Well, remember that the price information is attached to the artifact, not the line item. So suddenly the total changes for open orders all across the system. Which could lead to some unintended follow-on effects, like orders now falling just under the free-shipping threshold, and actually becoming more expensive.
We might consider some kind of locking schemes at the Order or LineItem level, but that just adds contention and doesn’t really get at the root of the problem.
This is a very simple example, so you’ve probably already figured out the flaw in this design. Orders and LineItems are closely associated; they should be grouped together and change together. Domain-Driven Design calls this an aggregate.
The artifacts table, meanwhile, is its own aggregate. When we create a new LineItem, we need to copy in the price, rather than linking the price to the original artifact entity.
This is a pretty naive example with a fairly commonplace business domain, and we don’t have much trouble picking out this design flaw while it’s still at the whiteboard stage. But many times, problems like this aren’t nearly as obvious when we look at the design as a snapshot of boxes and arrows. It would be nice if there were a technique that could reliably guide us toward identifying the aggregates in our systems.
What if we were to turn the whiteboard sideways, and look at the entities stretched out on the axis of time?
What we see now are lifetimes. Orders are created, updated, and then are finalized and moved to fulfilment, which makes them effectively dead from our perspective. Inside them, LineItems are added, and sometimes removed.
There is an obvious grouping here, a temporal relationship between Orders and LineItems. Meanwhile, at this timescale the Artifacts effectively live forever.
Looking at it this way, we could use the clumping of Orders and LineItems to draw a an aggregate boundary around them, excluding Artifact. This gives us the same conclusion we initially came to. But this time, we arrived at it using the heuristic of entity lifetimes, instead of having to step through specific scenarios.
When we look look at the system from this perspective, we’re dipping our toes into a technique called temporal modeling. The next time you are designing the domain entities in your system, try diagramming them along the axis of time instead of, or in addition to, static snapshots of their structure. You might gain some useful insights into your system!