Oh My Learning

Strategic redesign and incremental adoption

ddd-distilled/09 senior drafted · 10 min read

Domain-Driven Design Distilled (Vaughn Vernon), Chapter 7 "Acceleration"

Prerequisites ddd-distilled/03 ddd-distilled/04

Strategic redesign and incremental adoption#

TL;DR#

DDD is rarely applied to a greenfield system with an empty whiteboard — most real adoption happens against an existing, imperfect system, incrementally. Vernon's closing chapter covers the practical mechanics: context mapping an existing (often legacy) system to discover its real, implicit bounded contexts, event storming as a fast, collaborative discovery technique for surfacing process flow and vocabulary, and strangling a monolith into better-bounded pieces gradually rather than attempting a big-bang rewrite.

The idea#

The earlier lessons in this subject describe DDD's patterns as if you're deciding them fresh. In practice, most teams encounter DDD while already sitting on a system that grew organically — a monolith with tangled responsibilities, inconsistent vocabulary, and no explicit bounded contexts, just historical accretion. The strategic question shifts from "how do we design this" to "how do we find the real boundaries hiding in what already exists, and how do we move toward them safely, without a risky rewrite."

This closing part of the primer is deliberately about acceleration and adoption mechanics rather than new tactical patterns — it's the answer to "I believe in this subject's first eight lessons, now what do I actually do on Monday morning with the system I already have."

How it works#

Context mapping an existing system (reverse-engineering boundaries)#

Rather than designing bounded contexts top-down, you can discover them in an existing codebase by looking for the same signal described in ddd-distilled/03: places where a shared term (Product, Customer, Order) is used inconsistently by different parts of the system, or where different teams maintain conflicting mental models of the same class. Mapping these out — even informally, as a whiteboard exercise with the engineers who maintain each part — usually reveals that a "monolith" already has several implicit bounded contexts tangled together in one codebase and one database; the boundaries exist conceptually even though nothing in the code enforces them yet. This reverse-engineered context map becomes the target architecture for incremental extraction.

Worked example. A retail company's five-year-old monolith has one Product table used by the catalog UI, the warehouse management screens, and the pricing/promotions engine. A context-mapping exercise with engineers from each area reveals three different implicit models already coexisting inside that one table: catalog cares about description/images/category, warehouse cares about SKU/bin-location/quantity, pricing cares about cost-basis/margin/promotion-eligibility. The team didn't need to invent new boundaries — they needed to name the boundaries that already existed informally and had been quietly causing cross-team merge conflicts and coordination overhead for years.

Event storming as a discovery tool#

Event storming is the workshop technique Vernon (and the broader DDD community, via Alberto Brandolini) recommends for rapidly surfacing a domain's real process flow, vocabulary, and implicit boundaries, especially useful when a system is unfamiliar, legacy, or poorly documented. The mechanics: gather people who understand different parts of the process (including non-engineers — this is a collaborative-modeling technique, directly building on ddd-distilled/02) in a room with a large surface (a wall, a huge sheet of paper) and unlimited orange sticky notes. Participants write every significant domain event (ddd-distilled/08) they can think of, in past tense, and place them on the timeline in roughly chronological order — OrderPlaced, PaymentAuthorized, InventoryReserved, ShipmentDispatched. Once the event timeline is dense, the group adds commands (what triggered each event, often a user or system action), actors (who issues each command), and external systems. Clusters of related events and commands that "hang together" and share vocabulary tend to reveal candidate bounded contexts directly on the wall — a natural handoff into context mapping (ddd-distilled/03).

Worked example. An insurance company runs a two-hour event-storming session with claims adjusters, underwriters, and engineers to understand their claims process for the first time end-to-end (previously understood only in fragments by different teams). The resulting wall of sticky notes reveals a cluster around intake/triage (ClaimSubmitted, ClaimAssignedToAdjuster), a distinct cluster around investigation (EvidenceRequested, EvidenceReceived, LiabilityDetermined), and a distinct cluster around settlement (SettlementOffered, SettlementAccepted, PaymentIssued) — three candidate bounded contexts that the team hadn't previously thought of as separate, surfaced in one session, faster and with more shared understanding than months of document-driven requirements gathering would have produced.

Incremental adoption / strangling a monolith#

Once target bounded-context boundaries are identified (via context mapping, event storming, or both), Vernon's guidance for moving toward them is explicitly incremental, not a rewrite: extract one bounded context at a time, starting typically with whichever one delivers the clearest value soonest (often, but not always, an aspect of the core domain, per ddd-distilled/04) or whichever is cleanest to peel off with the least entanglement. Each extraction: 1. Draws an explicit boundary around the target context's data and logic (even if it initially stays inside the same codebase/deployment, as a well-separated module). 2. Introduces an anticorruption layer (ddd-distilled/03) at the boundary so the not-yet-refactored rest of the monolith doesn't leak its old, tangled model into the newly-cleaned-up context. 3. Migrates callers to go through the new boundary rather than reaching into the old shared model directly. 4. Only later, if warranted operationally, splits the extracted context into its own deployable service — extraction into a clean module and extraction into a separate service are two different decisions, and the first doesn't require the second.

This mirrors the general "strangler fig" pattern for legacy modernization: the new, well-bounded structure gradually grows around and replaces pieces of the old structure, rather than the old system being torn down and rebuilt all at once — which is consistently the higher-risk, more failure-prone path for any system still needed in production during the migration.

Worked example — incremental extraction in practice#

Continuing the retail Product example: rather than a big-bang split into three services, the team first introduces three separate modules within the existing deployment — CatalogProduct, WarehouseProduct, PricingProduct — each with its own narrow model, each reading from (initially) the same underlying tables via context-specific views or mapping code that acts as a lightweight anticorruption layer. Callers are migrated module by module. Only once PricingProduct (identified as part of the core domain, since dynamic pricing is where the company differentiates) has proven out its boundary and needs independent scaling does the team extract it into its own deployed service; CatalogProduct and WarehouseProduct, both supporting subdomains, may stay as well-separated modules in the same deployment indefinitely, since there's no operational pressure requiring the extra complexity of separate services.

Pros#

  • Avoids the high failure rate of big-bang rewrites, which frequently take longer than planned, freeze feature work, and risk delivering a "rewrite" that's no better designed than the original because the same time pressure that produced the mess reappears under a new deadline.
  • Event storming produces shared understanding fast, across roles (engineers, domain experts, product) that traditional requirements documents rarely achieve as quickly or as collaboratively.
  • Context-mapping an existing system surfaces boundaries the organization was already implicitly operating with, which tends to make the resulting model land as "finally naming what we all sort of knew" rather than an imposed, unfamiliar redesign.
  • Incremental extraction (module first, service later) decouples the "clean up the model" decision from the "adopt microservices" decision — you get DDD's modeling benefits without being forced into a distributed-systems commitment before it's operationally justified.

Cons#

  • Incremental extraction takes real, sustained discipline over a long period — it's tempting to declare victory after extracting the first, easiest context and never finish the harder, more entangled ones, leaving the system in a permanently half-migrated state.
  • Event storming's value depends heavily on facilitation quality and genuinely getting the right people in the room; a poorly facilitated session produces a wall of vague sticky notes with limited actionable insight.
  • Anticorruption layers at extraction boundaries add real, ongoing translation code and maintenance overhead, especially while large parts of the system are still on the old model — this cost is often underestimated when the extraction is planned.
  • Context-mapping an existing system requires the same cross-team, cross-role access and time investment as any collaborative modeling effort, which can be harder to secure for a "cleanup" initiative than for new, visible feature work.

Alternatives#

  • Big-bang rewrite — occasionally justified when a system is so fundamentally broken (or on a genuinely dead technology stack) that incremental extraction isn't feasible, but carries substantially higher delivery risk and is generally the alternative Vernon's incremental guidance is explicitly steering teams away from.
  • Leave the monolith as-is, apply DDD only to new features — a legitimate, lower-risk starting point: build new core-domain features as well-bounded modules from day one, without committing to retrofitting the entire legacy system; the "reverse-engineer the whole monolith" effort described in this lesson is the next step once new-feature discipline alone isn't enough.
  • Domain-agnostic technical refactoring first (extract layers, add tests, reduce coupling generically) before attempting any DDD-specific boundary work — sometimes a necessary precondition when a codebase is in poor enough shape that no boundary, however well-chosen, could be safely extracted yet.

When to use it#

Apply context mapping and event storming whenever you're inheriting or working within an existing system that lacks explicit bounded contexts — which describes most real-world DDD engagements, as opposed to greenfield projects. Favor incremental, module-first extraction whenever the system must keep running and delivering value throughout the migration (almost always).

When NOT to use it#

Skip this chapter's heavier discovery machinery for a genuinely small, well-understood, single-team system where the earlier lessons' strategic design (ddd-distilled/03, ddd-distilled/04) can simply be applied directly without needing to reverse-engineer anything first. Also avoid extracting a bounded context purely because it's the easiest one, if it isn't actually where the business value or the worst pain is — extraction order should still be guided by the core-domain distillation from ddd-distilled/04, not just convenience.

Key takeaways / mental model#

Most DDD adoption is archaeology followed by careful surgery, not a blank whiteboard: find the boundaries already implicitly present in a messy system (context mapping, event storming), then extract toward them one bounded context at a time, module first and service later, protected at each boundary by an anticorruption layer — never a single big-bang rewrite of a system still needed in production.

Self-check questions#

  1. Explain the difference between extracting a bounded context into a well-separated module versus extracting it into an independently deployed service. Why does Vernon's guidance treat these as two separate decisions rather than one?
  2. Walk through how you'd run a first event-storming session for a domain you don't know well. Who would you invite, and what would you be looking for in the resulting wall of sticky notes?
  3. Why is a big-bang rewrite generally considered higher risk than incremental extraction, specifically for a system that must keep running in production?
  4. In the retail Product example, why does the team extract PricingProduct into its own service while leaving CatalogProduct and WarehouseProduct as in-process modules? Connect your answer to ddd-distilled/04.
  5. What symptom in an existing, undocumented system would tell you an implicit bounded context boundary already exists, even though nothing in the code currently enforces it?

References#

  • Domain-Driven Design Distilled (Vaughn Vernon), Chapter 7: "Acceleration".
  • Event storming as a technique is attributed to Alberto Brandolini; Vernon's primer covers it as a practical discovery tool rather than in full depth.
  • For deeper legacy-modernization and strangler-pattern case studies alongside full DDD tactical depth, see domain-modeling/implementing-ddd; for facilitation technique and broader modeling practice, see domain-modeling/learning-ddd.