Skip to content
Richard Cooper
Go back

Bounded contexts and aggregates

In Optimising for deletion I claimed that “the aggregate is the unit you can delete atomically, the bounded context is the seam you cut along,” and called it a post in its own right. A debt is a debt, so here it is.

Domain-Driven Design is a big book and a bigger industry. But two of its ideas do most of the useful work, and you can lift them out without signing up for the whole religion: bounded contexts and aggregates. One tells you where to draw your big boundaries; the other tells you what you’re allowed to change in a single, consistent step.

The same word means different things

Here’s the observation that makes bounded contexts click. Ask three parts of an insurer what a “policy” is and you’ll get three different answers. To sales it’s a quote that someone said yes to — premium, cover, start date. To claims it’s a contract with exclusions and limits they’re checking a loss against. To finance it’s a stream of payments and a slice of risk on a ledger. Same word, three genuinely different models, each correct in its own world.

The classic mistake is to try to build one “Policy” object that serves all three. You end up with a bloated, contradictory model that nobody fully understands and everybody’s afraid to touch — because a change that suits claims breaks something in finance.

A bounded context is the boundary inside which a model and its language are consistent. Within it, “policy” means exactly one thing. Across the boundary, you translate — which is exactly where an anti-corruption layer earns its keep. The boundaries are linguistic and conceptual first, technical second. You find them by listening to where the language changes, not by drawing on an infrastructure diagram.

Aggregates: the unit of “all or nothing”

If the bounded context is the country, the aggregate is the smallest thing you’d post a letter to. It’s a cluster of objects you load, change, and save as one — with a single object at the front (the aggregate root) that everything outside has to go through.

Its job is to protect an invariant — a rule that must always hold. “A claim’s payments can never exceed the policy limit.” “An order’s lines must sum to its total.” You make that rule the aggregate’s responsibility, and you change the whole aggregate in one consistent step. What you don’t do is let outside code reach in and fiddle with the bits, because then nobody owns the rule and it quietly stops being true.

This is why I said the aggregate is the unit you can delete atomically. A well-drawn aggregate owns its data and its rules, so you can lift it — or remove it — as a whole. A badly drawn one has its guts spread across half the schema, and you can neither change nor delete it without archaeology.

Why this is worth your time

These aren’t academic distinctions. They decide things you’ll care about:

The honest caveat

Boundaries are a judgement call, and you will get some wrong. Draw them too big and you’re back to the bloated god-object; too small and you drown in translation and chatter between contexts. The good news is that bounded contexts are reasonably forgiving to move if you’ve kept the translation explicit at the edges — which is the same discipline that makes things deletable and replaceable.

The one-line version: bounded contexts tell you where the meaning changes; aggregates tell you what changes together. Get those two boundaries roughly right and an awful lot of other architectural decisions stop being arguments and start being obvious.


Share this post:

Previous Post
Anti-corruption layers
Next Post
The distributed systems tax