Context Mapping
debt(d7/e7/b8/t7)
Closest to 'only careful code review or runtime testing' (d7). While deptrac can enforce some boundary constraints, context mapping as a strategic design practice—identifying team relationships, power dynamics, and integration patterns—cannot be automated. Detection requires careful architectural review and organizational analysis. The absence of documented boundaries or the wrong integration pattern choice only surfaces through code review or when integration pain emerges over time.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix suggests drawing a map with your team, but actually remediating a wrong context relationship (e.g., switching from Conformist to ACL when the upstream pollutes your domain) requires introducing translation layers, potentially restructuring module boundaries, and coordinating across teams. This is cross-cutting work that touches multiple bounded contexts.
Closest to 'strong gravitational pull' (b8), +1 from b7. Context maps define the fundamental integration architecture between bounded contexts. Per the common_mistakes, undocumented integrations produce the worst coupling, and Conway's Law means team changes affect integration patterns. Every cross-context feature, every API change, every team restructuring is shaped by these decisions. Applies to web and cli contexts—essentially all PHP application types.
Closest to 'serious trap' (t7). The misconception explicitly states developers think context mapping is 'just API design' when it actually defines organizational relationships and power dynamics that determine what integration patterns are feasible. This contradicts how developers typically think about system integration—they focus on technical contracts rather than team dynamics. Choosing Conformist when ACL is needed, or Shared Kernel for convenience, are traps that seem reasonable but produce unmaintainable codebases.
Also Known As
TL;DR
Explanation
Context mapping identifies how different bounded contexts relate to each other: which is upstream (produces) and downstream (consumes), and what integration pattern is appropriate. Patterns: Partnership (collaborate closely), Shared Kernel (shared model subset), Customer-Supplier (upstream provides, downstream requests), Conformist (downstream adopts upstream model), ACL (downstream translates), Open Host (upstream publishes a protocol), Published Language (formal exchange format). The context map is a team topology tool as much as a technical one.
Diagram
flowchart LR
subgraph Upstream
CRM[CRM System<br/>legacy]
BILLING[Billing<br/>Service]
end
subgraph Downstream
ORDER[Order<br/>Service]
REPORT[Reporting<br/>Service]
end
CRM -->|Conformist or ACL| ORDER
BILLING -->|Open Host Service| ORDER
BILLING -->|Published Language| REPORT
ORDER -->|Customer-Supplier| REPORT
style CRM fill:#f85149,color:#fff
style ORDER fill:#238636,color:#fff
style REPORT fill:#1f6feb,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Not mapping all contexts — undocumented integrations are the ones that produce the worst coupling.
- Choosing Shared Kernel for convenience — it requires joint ownership and coordination; it is often the wrong choice.
- Conformist when ACL is needed — absorbing an upstream model directly pollutes the downstream domain.
- Not updating the context map when team structures change — Conway's Law means team changes affect integration patterns.
Code Examples
// Conformist — downstream absorbs upstream model uncritically:
// Legacy CRM uses 'CUSTNO', 'ACCT_STATUS_CD', 'AMT_OUTSTANDING'
class InvoiceService {
public function generateInvoice(string $custNo, string $acctStatusCd): Invoice {
// Upstream's naming convention pollutes the entire downstream domain
// Every developer must know what ACCT_STATUS_CD means
}
}
// ACL pattern — downstream translates upstream model:
class CrmTranslator { // Anti-Corruption Layer
public function toCustomer(array $crmData): Customer {
return new Customer(
id: new CustomerId($crmData['CUSTNO']),
status: match($crmData['ACCT_STATUS_CD']) {
'A' => CustomerStatus::Active,
'S' => CustomerStatus::Suspended,
},
balance: Money::fromCents((int)($crmData['AMT_OUTSTANDING'] * 100), 'GBP'),
);
}
}