Evolving Contexts: When Boundaries Must Shift
What This Concept Is
Bounded contexts are not permanent. A core subdomain today may be a supporting one in three years; a generic subdomain may become strategic because the business changes direction; a context that once contained two tidy aggregates may bloat as the business adds new responsibilities. This concept is the supporting play: how to recognize when a boundary must move and how to move it without breaking everything.
Common triggers for boundary change:
- Subdomain reclassification. A subdomain's strategic role changes (see concept 2): core -> supporting, supporting -> core, something -> generic.
- Business pivot. The product changes focus. A shipping company opens a consumer marketplace -- an entirely new core subdomain shows up.
- Growth / scale. One context accumulates responsibilities and becomes a distributed ball of mud. Split it.
- Team topology change. Two teams merge (maybe contexts merge), or one team splits into two (maybe contexts split).
- Language drift. Two sub-teams inside one context have started using different words for the same thing -- a single context is trying to hold two models.
- Repeated cross-cutting change. Every feature request touches three contexts in lockstep; the real boundary is not where you drew it.
- Deprecated generic vendor. You swap Stripe for a custom payments engine -- payments stops being generic.
Corresponding moves:
- Split a context into two.
- Merge two contexts into one.
- Extract a new context (often when a supporting subdomain turns core).
- Collapse a context back into another (when a core drift proves false).
- Re-typecast the relationship on an edge (e.g., from conformist to ACL-protected, from partnership to customer-supplier).
Each move is an ADR-worthy decision.
Why It Matters Here
DDD is sometimes taught as if the strategic design phase produces the final boundaries and then you move on to code. It doesn't. A living system will stress every boundary. The test of a good design is not that the boundaries never change; it is that the boundaries are cheap to change.
The practical mechanic: if you keep contracts explicit (OHS + Published Language from concept 5), keep ACLs in place on the edges that feel risky, and version your context map, then moving a boundary becomes a planned migration instead of a rewrite.
Concrete Example
Case: Parcel Shipping Co. -- five years in
Starting context map (year 1):
- Pricing (core)
- Shipping (supporting)
- Tracking (supporting)
- Billing (supporting)
- Carrier Gateway (supporting)
- Support (supporting)
Year 3: a competitor starts delivering same-day in major cities. Parcel Shipping pivots: same-day is now strategic. Two things change on the map:
- Tracking is reclassified core. Live ETAs, driver-side routing, and exception handling in real-time become a competitive differentiator. The subdomain reclassifies from supporting -> core. The context does not need to split yet, but it gets more investment and its contract to downstream is upgraded to an OHS+Published Language.
- A new context is extracted: Dispatch. Assigning couriers to routes used to be a sub-feature of Shipping. With same-day volumes, it becomes its own beast -- different SLAs, different vocabulary (
driver,route,leg,bag). The language heuristic fires (concept 8). Dispatch is extracted.
Year 4: Stripe is no longer enough. A fraud team wants to plug in to the payment flow. Payments, which had been a generic integration, becomes a supporting context owned by a dedicated team, wrapping Stripe behind an internal model.
Year 5: Support has bloated. "Support" now means four different workflows (billing disputes, delivery exceptions, returns escalations, VIP concierge). Cross-cutting change triggers fire -- every feature touches three screens. Support is split into Customer Support and Claims.
Final context map (year 5):
Mechanics of a boundary move: extracting Dispatch
- ADR. Title: "Extract Dispatch context from Shipping." Motivations: language heuristic, rate-of-change heuristic, autonomy (same-day release cadence). Downstream impact listed.
- Seam. Inside the Shipping codebase, introduce a
DispatchingPortinterface. All dispatching logic moves behind the port. Nothing else in Shipping calls dispatching code directly. - Dual-run.
DispatchingAdapterInProcesscontinues to satisfy the port. Meanwhile build aDispatchingAdapterHttpthat talks to a new Dispatch service running the same logic in a separate deployable. - Shadow traffic. For 2 weeks run both adapters; compare outputs.
- Cutover per shipment class. First non-critical shipment classes (pallets), then full.
- Delete the in-process adapter. Remove the dispatch code from Shipping.
This is the same pattern as any careful refactor (strangler fig). The DDD-specific part is that the seam was introduced at the context boundary, not at an arbitrary package line.
Recognizing "it's time"
Signals, from cheapest to most painful:
- engineers say "do I put this in Shipping or Tracking?" more than once a month
- same feature consistently requires three simultaneous PRs in three services
- a code change in one context breaks tests in a distant one
- an incident postmortem finds the root cause in a context adjacent to the one that page-d
- a senior engineer onboards to a context and cannot explain its job in one sentence
By the time you see any of these, you already have a boundary issue.
Common Confusion / Misconception
"We should get the boundaries right the first time so we never have to move them." Untrue in practice. Boundaries encode business assumptions that change. Design for movability, not for permanence.
"Moving a boundary means rewriting." Only if contracts are implicit. If you have OHS + Published Language and ACLs, moving a boundary is a migration, not a rewrite.
"Splitting is always the answer to bloat." Sometimes the answer is merging -- two contexts that are constantly chatty and change together are really one context.
"We'll refactor boundaries later when we have time." Boundary drift compounds. An untreated supporting-turned-core context is how teams end up with a critical path running through a team of two juniors.
"Boundary moves are architect-only decisions." They are negotiated. The team that owns the context has veto power on being split; stakeholder teams have input on relationship pattern.
How To Use It
Quarterly, run an explicit boundary review:
- Pull up the current context map.
- For each context, ask:
- Is the subdomain classification still right? (core / supporting / generic)
- Did any heuristic violations appear this quarter?
- Is the team happy with the scope?
- For each edge, ask:
- Did the volume or kind of traffic change materially?
- Did upstream push any breaking change?
- Is the relationship pattern still honest?
- Flag 0-2 candidate moves. For each, open an ADR.
- Schedule the moves explicitly. Do not try to move three contexts at once.
Check Yourself
- Name two triggers that suggest a context should split.
- Name one trigger that suggests two contexts should merge.
- What two artifacts make a boundary move cheap instead of expensive?
Mini Drill or Application
Pick one:
- Parcel Shipping scenario: the company acquires a last-mile courier startup. The acquired company has its own
Dispatch,Tracking, andDrivercontexts. Propose, in 1 page, which of your existing contexts to merge, extract, or keep. Produce a before/after context map and name the heuristics for each decision. - Conference Ticketing scenario: organizers ask for a white-label product for enterprise customers with custom branding and check-in rules. Does
Check-inbecome a core context? Does a newTenant Administrationcontext appear? Justify with heuristics.