Files of Bounded contexts and context mapping
wondelai/
Show the full text204 lines
Bounded Contexts and Context Mapping
A bounded context is the most important strategic pattern in Domain-Driven Design. It defines an explicit boundary within which a particular domain model is defined, consistent, and applicable. Context mapping describes the relationships between bounded contexts and the strategies for translating between them.
What Is a Bounded Context?
A bounded context is not a module, a microservice, or a deployment unit -- though it may coincide with any of these. It is a linguistic boundary: within this boundary, every term has a single, precise meaning, and the model is internally consistent.
The Problem It Solves
In any system of sufficient size, the same word means different things to different people:
| Term | In Sales Context | In Shipping Context | In Billing Context |
|---|---|---|---|
| Customer | A prospect or account with contact info and purchase history | A delivery address with receiving instructions | A billing entity with payment methods and credit terms |
| Product | A catalog item with descriptions, images, and pricing tiers | A physical item with weight, dimensions, and handling requirements | A line item with a price, tax category, and discount rules |
| Order | A quote or deal being negotiated | A shipment to be picked, packed, and dispatched | An invoice to be generated and collected |
Trying to create a single Customer class that serves all three contexts produces a bloated, contradictory monstrosity with dozens of fields, most of which are irrelevant in any given use case. The bounded context pattern says: stop fighting this. Let each context have its own Customer model, optimized for its own needs.
Identifying Bounded Context Boundaries
Boundaries emerge from several signals:
Linguistic signals:
- When the same term means different things to different groups, those groups are in different contexts
- When conversations between groups require "translation" ("when you say X, do you mean Y?"), there is a boundary
- When a concept exists in one group but has no analog in another, the boundary is clear
Organizational signals:
- Different teams owning different parts of the system
- Different departments with different processes and vocabulary
- Different regulatory requirements (e.g., PCI compliance for payments vs. HIPAA for patient data)
Technical signals:
- Different data storage needs (relational vs. document vs. event store)
- Different consistency requirements (strong consistency for payments vs. eventual consistency for recommendations)
- Different rate of change (billing rules change quarterly; product catalog changes daily)
Context Size
There is no formula for the right size of a bounded context. However, guidelines help:
- Too large: If a single context contains concepts that do not cohesively relate, it is too large. A context that contains both "insurance underwriting" and "marketing campaign management" is probably two contexts.
- Too small: If you find yourself creating translation layers between closely related concepts that change together and are owned by the same team, you may have split too aggressively.
- Rule of thumb: A bounded context should be ownable by a single team (5-9 people). If a context requires multiple teams to modify, it is either too large or the team boundaries are wrong.
Context Mapping Patterns
Context mapping describes how bounded contexts relate to each other. Eric Evans and the DDD community have identified nine primary patterns. Each represents a different political and technical relationship.
1. Shared Kernel
What it is: Two bounded contexts share a small subset of the model, typically a library of common types. Both teams co-own this shared code and must coordinate changes.
When to use: When two closely collaborating teams need the same domain concept (e.g., a Money value object or a DateRange type) and the overhead of translation is not justified.
Risks: Coupling. Any change to the shared kernel requires coordination between both teams. If not carefully managed, the shared kernel grows uncontrollably.
Rules:
- Keep the shared kernel as small as possible -- value objects and basic types only
- Require explicit agreement (both teams) for any change
- Automated tests in both contexts must pass before any shared kernel change is merged
- Never put entities or aggregates in the shared kernel
Example: Two contexts (billing and shipping) share a Money(amount, currency) value object and an Address(street, city, state, zip, country) value object.
2. Customer-Supplier
What it is: An upstream context (supplier) provides data or services that a downstream context (customer) depends on. The upstream team plans with the downstream team's needs in mind but has its own priorities.
When to use: When one team produces something another team consumes, and there is a reasonable working relationship. The downstream team can influence the upstream team's roadmap but does not control it.
Example: The Product Catalog team (upstream) provides product data to the Pricing team (downstream). The Pricing team requests new attributes when needed, and the Catalog team accommodates these requests in their planning.
3. Conformist
What it is: The downstream context conforms to the upstream context's model without translation. The downstream team accepts the upstream model as-is, even if it is not ideal.
When to use: When the upstream team has no incentive or ability to accommodate the downstream team (e.g., a large external service, a legacy system, or a dominant upstream team), and the cost of building a translation layer exceeds the cost of conforming.
Risks: Your model is constrained by someone else's design decisions. If the upstream model changes, your context must change too.
Example: A startup integrating with a major ERP system may conform to the ERP's data model rather than building translation layers, accepting the ERP's concept of "customer" and "order" even if they do not perfectly fit.
4. Anti-Corruption Layer (ACL)
What it is: A translation layer that sits between the downstream context and the upstream context, converting the upstream model into the downstream context's own domain language. The ACL protects the downstream model from being polluted by foreign concepts.
When to use: When you need to integrate with a system whose model does not fit yours, and you cannot afford to let that foreign model leak into your domain. This is the most important defensive pattern in DDD.
Structure:
Your Domain Layer <--> ACL (Adapters + Translators) <--> External System
The ACL contains:
- Adapters that handle the technical protocol (HTTP, gRPC, message queue)
- Translators that convert external model objects into your domain objects
- Facades that present a clean interface to your domain layer
Example: Integrating with a legacy mainframe that represents customers as CUST_REC with fields like CUST_NM, CUST_ADDR1. The ACL translates this into your domain's Customer(name: PersonName, address: Address) value objects.
5. Open Host Service (OHS)
What it is: An upstream context exposes a well-defined, documented protocol (API, message format) that downstream contexts can integrate with. The upstream team provides a stable, versioned interface designed for general consumption.
When to use: When an upstream context serves multiple downstream consumers and cannot tailor its interface to each one. The OHS provides a standard integration point.
Characteristics:
- Versioned API with backward compatibility guarantees
- Documentation and contracts (OpenAPI, protobuf schemas, JSON Schema)
- The interface is deliberately designed for external consumption, not a direct exposure of the internal model
Example: An Identity Provider exposes an OAuth 2.0 / OpenID Connect API. Any downstream context can integrate using the standard protocol without knowing the internal user model.
6. Published Language
What it is: A well-documented, shared language (schema, format, protocol) used for communication between contexts. Often paired with Open Host Service.
When to use: When multiple contexts need to exchange data and a standard format prevents each integration from inventing its own.
Examples:
- Industry-standard formats: HL7 for healthcare, SWIFT for banking, EDI for supply chain
- Internal schemas: a company-wide JSON schema for events published on a shared message bus
- Protocol Buffers or Avro schemas for service-to-service communication
7. Separate Ways
What it is: Two contexts decide not to integrate at all. Each builds its own solution independently, even if there is some overlap.
When to use: When the cost of integration (coordination, translation, coupling) exceeds the cost of duplication. Sometimes it is cheaper and simpler for two teams to build their own Address validation than to share one.
Signals that Separate Ways is appropriate:
- The integration would be trivial functionality on both sides
- The teams are in different organizations with different release cycles
- The shared functionality is not core to either context
8. Big Ball of Mud
What it is: A system with no clear boundaries, where models are entangled and concepts leak everywhere. This is not a recommended pattern -- it is a recognition of reality. Many existing systems are Big Balls of Mud.
When to use (as a label): When mapping an existing landscape, some systems simply are Big Balls of Mud. Acknowledging this is the first step toward improvement. Drawing a boundary around the mud and treating it as a single (messy) context allows you to build clean contexts alongside it, protected by an ACL.
Migration strategy:
- Draw a boundary around the entire mudball
- Build new functionality in a clean bounded context
- Protect the new context with an ACL against the mudball
- Gradually extract functionality from the mud into clean contexts
9. Partnership
What it is: Two contexts evolve together with mutual coordination. Both teams jointly plan features and synchronize releases. Neither dominates.
When to use: When two contexts are tightly coupled in the domain and both teams are committed to evolving together. More intimate than Customer-Supplier; both teams have equal say.
Risks: Requires strong coordination discipline. If one team slips, both are affected.
Example: A checkout context and a payment processing context that must evolve in lockstep when payment methods change.
Team Relationships and Context Boundaries
Conway's Law in Practice
Conway's Law states that systems mirror the communication structures of the organizations that build them. In DDD, this is not a warning -- it is a design tool:
- Align context boundaries with team boundaries. If one team owns billing and another owns shipping, these should be separate bounded contexts.
- If two teams must share a context, expect friction. Either split the context or merge the teams.
- Cross-team integration should happen at context boundaries, using well-defined mapping patterns, not through shared code or databases.
Choosing the Right Pattern
| Situation | Recommended Pattern |
|---|---|
| Two teams with a good relationship and shared concepts | Shared Kernel (kept small) or Customer-Supplier |
| Integrating with a system you do not control | Anti-Corruption Layer |
| Exposing your context to many consumers | Open Host Service + Published Language |
| Integrating with a hostile or unresponsive upstream | Conformist (if cost is low) or Separate Ways |
| Legacy system with no clear model | Big Ball of Mud (label it) + ACL around it |
| Two teams that must evolve in lockstep | Partnership |
| Integration cost exceeds duplication cost | Separate Ways |
Drawing a Context Map
A context map is a visual representation of all bounded contexts and their relationships. It should include:
- Every bounded context drawn as a labeled box
- The relationships between them using the patterns above (arrows show upstream/downstream)
- The translation mechanism (ACL, OHS, Shared Kernel)
- Team ownership for each context
- The Big Balls of Mud explicitly labeled
The context map is a communication tool. It should be understandable by both developers and non-technical stakeholders. Keep it on a whiteboard or in a shared diagram that the team references and updates regularly.
When Boundaries Change
Context boundaries are not permanent. They evolve as the business and team structure change:
- Splitting: A context grows too large for one team; split it along natural seams and introduce a mapping pattern between the new contexts.
- Merging: Two small contexts owned by the same team with heavy inter-communication; merge them and eliminate the translation overhead.
- Reclassifying: A context that was Generic Subdomain becomes Core Domain as business strategy shifts; increase investment and modeling rigor.
The key is to make boundaries explicit and intentional, so that when they need to change, the change is a deliberate design decision rather than an accidental drift.
| 1 | # Bounded Contexts and Context Mapping |
| 2 | |
| 3 | A bounded context is the most important strategic pattern in Domain-Driven Design. It defines an explicit boundary within which a particular domain model is defined, consistent, and applicable. Context mapping describes the relationships between bounded contexts and the strategies for translating between them. |
| 4 | |
| 5 | ## What Is a Bounded Context? |
| 6 | |
| 7 | A bounded context is not a module, a microservice, or a deployment unit -- though it may coincide with any of these. It is a linguistic boundary: within this boundary, every term has a single, precise meaning, and the model is internally consistent. |
| 8 | |
| 9 | ### The Problem It Solves |
| 10 | |
| 11 | In any system of sufficient size, the same word means different things to different people: |
| 12 | |
| 13 | | Term | In Sales Context | In Shipping Context | In Billing Context | |
| 14 | |------|-----------------|--------------------|--------------------| |
| 15 | | Customer | A prospect or account with contact info and purchase history | A delivery address with receiving instructions | A billing entity with payment methods and credit terms | |
| 16 | | Product | A catalog item with descriptions, images, and pricing tiers | A physical item with weight, dimensions, and handling requirements | A line item with a price, tax category, and discount rules | |
| 17 | | Order | A quote or deal being negotiated | A shipment to be picked, packed, and dispatched | An invoice to be generated and collected | |
| 18 | |
| 19 | Trying to create a single `Customer` class that serves all three contexts produces a bloated, contradictory monstrosity with dozens of fields, most of which are irrelevant in any given use case. The bounded context pattern says: stop fighting this. Let each context have its own `Customer` model, optimized for its own needs. |
| 20 | |
| 21 | ### Identifying Bounded Context Boundaries |
| 22 | |
| 23 | Boundaries emerge from several signals: |
| 24 | |
| 25 | **Linguistic signals:** |
| 26 | When the same term means different things to different groups, those groups are in different contexts |
| 27 | When conversations between groups require "translation" ("when you say X, do you mean Y?"), there is a boundary |
| 28 | When a concept exists in one group but has no analog in another, the boundary is clear |
| 29 | |
| 30 | **Organizational signals:** |
| 31 | Different teams owning different parts of the system |
| 32 | Different departments with different processes and vocabulary |
| 33 | Different regulatory requirements (e.g., PCI compliance for payments vs. HIPAA for patient data) |
| 34 | |
| 35 | **Technical signals:** |
| 36 | Different data storage needs (relational vs. document vs. event store) |
| 37 | Different consistency requirements (strong consistency for payments vs. eventual consistency for recommendations) |
| 38 | Different rate of change (billing rules change quarterly; product catalog changes daily) |
| 39 | |
| 40 | ### Context Size |
| 41 | |
| 42 | There is no formula for the right size of a bounded context. However, guidelines help: |
| 43 | |
| 44 | **Too large:** If a single context contains concepts that do not cohesively relate, it is too large. A context that contains both "insurance underwriting" and "marketing campaign management" is probably two contexts. |
| 45 | **Too small:** If you find yourself creating translation layers between closely related concepts that change together and are owned by the same team, you may have split too aggressively. |
| 46 | **Rule of thumb:** A bounded context should be ownable by a single team (5-9 people). If a context requires multiple teams to modify, it is either too large or the team boundaries are wrong. |
| 47 | |
| 48 | ## Context Mapping Patterns |
| 49 | |
| 50 | Context mapping describes how bounded contexts relate to each other. Eric Evans and the DDD community have identified nine primary patterns. Each represents a different political and technical relationship. |
| 51 | |
| 52 | ### 1. Shared Kernel |
| 53 | |
| 54 | **What it is:** Two bounded contexts share a small subset of the model, typically a library of common types. Both teams co-own this shared code and must coordinate changes. |
| 55 | |
| 56 | **When to use:** When two closely collaborating teams need the same domain concept (e.g., a `Money` value object or a `DateRange` type) and the overhead of translation is not justified. |
| 57 | |
| 58 | **Risks:** Coupling. Any change to the shared kernel requires coordination between both teams. If not carefully managed, the shared kernel grows uncontrollably. |
| 59 | |
| 60 | **Rules:** |
| 61 | Keep the shared kernel as small as possible -- value objects and basic types only |
| 62 | Require explicit agreement (both teams) for any change |
| 63 | Automated tests in both contexts must pass before any shared kernel change is merged |
| 64 | Never put entities or aggregates in the shared kernel |
| 65 | |
| 66 | **Example:** Two contexts (billing and shipping) share a `Money(amount, currency)` value object and an `Address(street, city, state, zip, country)` value object. |
| 67 | |
| 68 | ### 2. Customer-Supplier |
| 69 | |
| 70 | **What it is:** An upstream context (supplier) provides data or services that a downstream context (customer) depends on. The upstream team plans with the downstream team's needs in mind but has its own priorities. |
| 71 | |
| 72 | **When to use:** When one team produces something another team consumes, and there is a reasonable working relationship. The downstream team can influence the upstream team's roadmap but does not control it. |
| 73 | |
| 74 | **Example:** The Product Catalog team (upstream) provides product data to the Pricing team (downstream). The Pricing team requests new attributes when needed, and the Catalog team accommodates these requests in their planning. |
| 75 | |
| 76 | ### 3. Conformist |
| 77 | |
| 78 | **What it is:** The downstream context conforms to the upstream context's model without translation. The downstream team accepts the upstream model as-is, even if it is not ideal. |
| 79 | |
| 80 | **When to use:** When the upstream team has no incentive or ability to accommodate the downstream team (e.g., a large external service, a legacy system, or a dominant upstream team), and the cost of building a translation layer exceeds the cost of conforming. |
| 81 | |
| 82 | **Risks:** Your model is constrained by someone else's design decisions. If the upstream model changes, your context must change too. |
| 83 | |
| 84 | **Example:** A startup integrating with a major ERP system may conform to the ERP's data model rather than building translation layers, accepting the ERP's concept of "customer" and "order" even if they do not perfectly fit. |
| 85 | |
| 86 | ### 4. Anti-Corruption Layer (ACL) |
| 87 | |
| 88 | **What it is:** A translation layer that sits between the downstream context and the upstream context, converting the upstream model into the downstream context's own domain language. The ACL protects the downstream model from being polluted by foreign concepts. |
| 89 | |
| 90 | **When to use:** When you need to integrate with a system whose model does not fit yours, and you cannot afford to let that foreign model leak into your domain. This is the most important defensive pattern in DDD. |
| 91 | |
| 92 | **Structure:** |
| 93 | |
| 94 | Your Domain Layer <--> ACL (Adapters + Translators) <--> External System |
| 95 | |
| 96 | |
| 97 | The ACL contains: |
| 98 | **Adapters** that handle the technical protocol (HTTP, gRPC, message queue) |
| 99 | **Translators** that convert external model objects into your domain objects |
| 100 | **Facades** that present a clean interface to your domain layer |
| 101 | |
| 102 | **Example:** Integrating with a legacy mainframe that represents customers as `CUST_REC` with fields like `CUST_NM`, `CUST_ADDR1`. The ACL translates this into your domain's `Customer(name: PersonName, address: Address)` value objects. |
| 103 | |
| 104 | ### 5. Open Host Service (OHS) |
| 105 | |
| 106 | **What it is:** An upstream context exposes a well-defined, documented protocol (API, message format) that downstream contexts can integrate with. The upstream team provides a stable, versioned interface designed for general consumption. |
| 107 | |
| 108 | **When to use:** When an upstream context serves multiple downstream consumers and cannot tailor its interface to each one. The OHS provides a standard integration point. |
| 109 | |
| 110 | **Characteristics:** |
| 111 | Versioned API with backward compatibility guarantees |
| 112 | Documentation and contracts (OpenAPI, protobuf schemas, JSON Schema) |
| 113 | The interface is deliberately designed for external consumption, not a direct exposure of the internal model |
| 114 | |
| 115 | **Example:** An Identity Provider exposes an OAuth 2.0 / OpenID Connect API. Any downstream context can integrate using the standard protocol without knowing the internal user model. |
| 116 | |
| 117 | ### 6. Published Language |
| 118 | |
| 119 | **What it is:** A well-documented, shared language (schema, format, protocol) used for communication between contexts. Often paired with Open Host Service. |
| 120 | |
| 121 | **When to use:** When multiple contexts need to exchange data and a standard format prevents each integration from inventing its own. |
| 122 | |
| 123 | **Examples:** |
| 124 | Industry-standard formats: HL7 for healthcare, SWIFT for banking, EDI for supply chain |
| 125 | Internal schemas: a company-wide JSON schema for events published on a shared message bus |
| 126 | Protocol Buffers or Avro schemas for service-to-service communication |
| 127 | |
| 128 | ### 7. Separate Ways |
| 129 | |
| 130 | **What it is:** Two contexts decide not to integrate at all. Each builds its own solution independently, even if there is some overlap. |
| 131 | |
| 132 | **When to use:** When the cost of integration (coordination, translation, coupling) exceeds the cost of duplication. Sometimes it is cheaper and simpler for two teams to build their own `Address` validation than to share one. |
| 133 | |
| 134 | **Signals that Separate Ways is appropriate:** |
| 135 | The integration would be trivial functionality on both sides |
| 136 | The teams are in different organizations with different release cycles |
| 137 | The shared functionality is not core to either context |
| 138 | |
| 139 | ### 8. Big Ball of Mud |
| 140 | |
| 141 | **What it is:** A system with no clear boundaries, where models are entangled and concepts leak everywhere. This is not a recommended pattern -- it is a recognition of reality. Many existing systems are Big Balls of Mud. |
| 142 | |
| 143 | **When to use (as a label):** When mapping an existing landscape, some systems simply are Big Balls of Mud. Acknowledging this is the first step toward improvement. Drawing a boundary around the mud and treating it as a single (messy) context allows you to build clean contexts alongside it, protected by an ACL. |
| 144 | |
| 145 | **Migration strategy:** |
| 146 | Draw a boundary around the entire mudball |
| 147 | Build new functionality in a clean bounded context |
| 148 | Protect the new context with an ACL against the mudball |
| 149 | Gradually extract functionality from the mud into clean contexts |
| 150 | |
| 151 | ### 9. Partnership |
| 152 | |
| 153 | **What it is:** Two contexts evolve together with mutual coordination. Both teams jointly plan features and synchronize releases. Neither dominates. |
| 154 | |
| 155 | **When to use:** When two contexts are tightly coupled in the domain and both teams are committed to evolving together. More intimate than Customer-Supplier; both teams have equal say. |
| 156 | |
| 157 | **Risks:** Requires strong coordination discipline. If one team slips, both are affected. |
| 158 | |
| 159 | **Example:** A checkout context and a payment processing context that must evolve in lockstep when payment methods change. |
| 160 | |
| 161 | ## Team Relationships and Context Boundaries |
| 162 | |
| 163 | ### Conway's Law in Practice |
| 164 | |
| 165 | Conway's Law states that systems mirror the communication structures of the organizations that build them. In DDD, this is not a warning -- it is a design tool: |
| 166 | |
| 167 | **Align context boundaries with team boundaries.** If one team owns billing and another owns shipping, these should be separate bounded contexts. |
| 168 | **If two teams must share a context, expect friction.** Either split the context or merge the teams. |
| 169 | **Cross-team integration should happen at context boundaries,** using well-defined mapping patterns, not through shared code or databases. |
| 170 | |
| 171 | ### Choosing the Right Pattern |
| 172 | |
| 173 | | Situation | Recommended Pattern | |
| 174 | |-----------|-------------------| |
| 175 | | Two teams with a good relationship and shared concepts | Shared Kernel (kept small) or Customer-Supplier | |
| 176 | | Integrating with a system you do not control | Anti-Corruption Layer | |
| 177 | | Exposing your context to many consumers | Open Host Service + Published Language | |
| 178 | | Integrating with a hostile or unresponsive upstream | Conformist (if cost is low) or Separate Ways | |
| 179 | | Legacy system with no clear model | Big Ball of Mud (label it) + ACL around it | |
| 180 | | Two teams that must evolve in lockstep | Partnership | |
| 181 | | Integration cost exceeds duplication cost | Separate Ways | |
| 182 | |
| 183 | ### Drawing a Context Map |
| 184 | |
| 185 | A context map is a visual representation of all bounded contexts and their relationships. It should include: |
| 186 | |
| 187 | **Every bounded context** drawn as a labeled box |
| 188 | **The relationships between them** using the patterns above (arrows show upstream/downstream) |
| 189 | **The translation mechanism** (ACL, OHS, Shared Kernel) |
| 190 | **Team ownership** for each context |
| 191 | **The Big Balls of Mud** explicitly labeled |
| 192 | |
| 193 | The context map is a communication tool. It should be understandable by both developers and non-technical stakeholders. Keep it on a whiteboard or in a shared diagram that the team references and updates regularly. |
| 194 | |
| 195 | ### When Boundaries Change |
| 196 | |
| 197 | Context boundaries are not permanent. They evolve as the business and team structure change: |
| 198 | |
| 199 | **Splitting:** A context grows too large for one team; split it along natural seams and introduce a mapping pattern between the new contexts. |
| 200 | **Merging:** Two small contexts owned by the same team with heavy inter-communication; merge them and eliminate the translation overhead. |
| 201 | **Reclassifying:** A context that was Generic Subdomain becomes Core Domain as business strategy shifts; increase investment and modeling rigor. |
| 202 | |
| 203 | The key is to make boundaries explicit and intentional, so that when they need to change, the change is a deliberate design decision rather than an accidental drift. |
| 204 |
Discussion
Alternatives
Browse more free Claude skills or everything in Development.