Two microservices, each aligned to its own bounded context, need to integrate — one is the 'upstream' source of a concept, the other 'downstream' consumes it. What DDD context-mapping patterns describe how the downstream service should protect its own model from the upstream one, and when would you pick each?
answer
- context map = relationships between contexts
- Conformist = adopt upstream model as-is
- ACL = translate at the boundary
- Shared Kernel = small shared library, needs coordination
- Open Host Service = stable public contract for many consumers
basics
~20 sWhen one service depends on another's data, you can either just copy the upstream's model as-is (fast but fragile), or build a small translation layer that converts it into your own local model (more work but protects you from their changes). DDD calls these patterns things like "Conformist" and "Anti-Corruption Layer."
solid answer
~60 sA context map documents the relationships between bounded contexts/services and, for each upstream-downstream pair, prescribes an integration pattern. 'Conformist' means the downstream service just accepts and uses the upstream's model as-is — cheap, but the downstream is now coupled to every upstream change. 'Anti-Corruption Layer (ACL)' means the downstream builds a translation layer that converts the upstream's model into its own local model at the boundary, insulating its internal domain logic from upstream churn at the cost of extra code to build and maintain. 'Shared Kernel' means two contexts deliberately share a small common model/library, which requires tight coordination between the two teams on any change to that shared piece. 'Customer-Supplier' and 'Open Host Service + Published Language' describe how the upstream team treats downstream needs — whether downstream gets a say in the upstream's roadmap, and whether the upstream exposes a stable, well-documented public interface for many consumers rather than a bespoke one per consumer. You pick Conformist when the upstream is stable, authoritative, and not worth fighting; you pick ACL when the upstream is unstable, poorly modeled, or conceptually foreign to your context and you need long-term protection.
go deeper
Should grasp that services integrating across bounded contexts sometimes need a translation step, even without naming the formal patterns.
Should be able to name and describe Conformist and Anti-Corruption Layer specifically, and give a rough rule for choosing between them.
Should be able to select the right context-map pattern (including Shared Kernel and Open Host Service) for a given upstream-downstream relationship and justify the trade-off, plus design a concrete ACL for a real integration.
Should be able to design and govern a context map across a whole organization's service landscape, set policy for when Shared Kernel is permitted, and recognize/correct relationships that have drifted into the wrong pattern (e.g. an accidental Conformist relationship with an unstable upstream) before it causes an incident.
## What a context map is A **context map** is a diagram (or, more usefully, a living document) that shows the bounded contexts in a system and the relationships between them — which context depends on which, and what kind of relationship that dependency is. It exists because bounded contexts are deliberately isolated in their internal models, but real systems still need those contexts to exchange information, and the way they do so has real consequences for coupling, team autonomy, and how much a downstream team can be blindsided by an upstream team's changes. Eric Evans' original DDD catalog names several integration patterns, and the most commonly used pairing in microservices work is **Conformist** versus **Anti-Corruption Layer**, alongside **Shared Kernel**, **Customer-Supplier**, and **Open Host Service** with **Published Language**. ## Conformist Conformist means the downstream context simply adopts the upstream context's model wholesale — its types, its vocabulary, its shape — without translation. The mechanism is direct: the downstream service calls the upstream's API and deserializes the response straight into types that mirror the upstream's own domain model, using the upstream's terms internally too. This is cheap to build and keeps the two contexts in lockstep, but it means every change the upstream team makes to their model can ripple directly into the downstream's internal logic, and the downstream has effectively given up having its own independent ubiquitous language for that concept. ## Anti-Corruption Layer Anti-Corruption Layer (ACL) is the opposite strategy: the downstream context builds an explicit translation layer — often a small set of adapter classes or a facade service — that sits at the boundary, converts the upstream's model into the downstream's own local model, and is the only place upstream concepts are allowed to leak into. Internally, the downstream's domain logic never sees the upstream's types directly; it only sees its own, translated ones. The mechanism costs real engineering effort (you're writing and maintaining a translation layer, and keeping it in sync with both models), but the payoff is **insulation**: if the upstream changes its model, only the ACL needs to change, and the downstream's core domain logic — arguably the most valuable, most carefully modeled code in the service — stays untouched. ## Picking between the two | Pattern | When it fits | |---|---| | **Conformist** | The right call when the upstream is authoritative, stable, and not realistically going to change to suit you — a classic example is conforming to a third-party payment provider's data model, or to a well-governed internal identity/auth platform whose team isn't going to redesign its User model around your needs | | **Anti-Corruption Layer** | The right call when the upstream's model doesn't map cleanly onto your own domain concepts (e.g., integrating with a legacy system that has an ugly, denormalized model, or a third-party API whose `Customer` concept conflates three things you keep separate), or when you expect the upstream to be unstable, poorly governed, or likely to change in ways you don't control | ## Shared Kernel Shared Kernel is a different kind of relationship: two teams deliberately agree to share a small subset of model code (say, a `Money` value object or a common set of domain events) as a literal shared library, accepting that any change to it requires coordination between both teams before it ships. This trades some team autonomy for avoiding duplicated, potentially-diverging logic for a genuinely small, stable, foundational concept — it's a narrow, deliberate exception to *each bounded context owns its own model*, not a general integration strategy, and teams that let a shared kernel grow unchecked end up rebuilding the coupling problems bounded contexts were meant to solve. ## Governance and interface shape Customer-Supplier and Open Host Service describe the governance and interface shape of an upstream-downstream relationship rather than the translation mechanics. - **Customer-Supplier.** The downstream (*customer*) has organizational standing to influence the upstream (*supplier*) team's roadmap and can negotiate for features it needs — common when both teams are in the same organization and the upstream genuinely serves the downstream's use case. - **Open Host Service with Published Language.** The upstream deliberately exposes a well-documented, stable public API/schema (a *published language*, e.g. a versioned REST contract or an event schema) designed to serve many consumers uniformly, rather than negotiating a bespoke integration per downstream team — this is what lets an upstream scale to dozens of consumers without a bespoke ACL-or-Conformist negotiation with each one. ## Failure modes Failure modes appear when teams pick the wrong pattern for the relationship they actually have. - **Conforming to an upstream that's actually unstable or poorly modeled** means every upstream release becomes a downstream firefight, with domain logic quietly corrupted by leaked upstream concepts — this is literally what ACL's name warns against, model corruption crossing the boundary unchecked. - **Building an ACL against an upstream that's genuinely stable and well-governed** is wasted effort: maintaining a translation layer nobody needs. A concrete real-world example: a checkout service integrating a third-party tax-calculation API typically uses an ACL, translating the vendor's tax-line response format into the checkout domain's own `TaxAssessment` value object, so that if the vendor changes their response schema in a major version bump, only the ACL adapter needs a rewrite — the checkout service's own pricing and order logic never has to know the vendor's API shape existed.
- How would you decide between Conformist and Anti-Corruption Layer when integrating with an internal upstream service owned by another team in your own company?Look at how stable and well-modeled the upstream's domain concept is, and how much it matches your own context's needs — if the upstream team is mature, the model is clean, and it's unlikely to change in disruptive ways, Conformist saves you real engineering effort. If the upstream is early-stage, frequently refactored, or models the concept in a way that's foreign to your domain (e.g. their 'Account' bundles billing and auth together when you need them separate), an ACL protects your domain logic from that churn even though it costs more to build.
- What risk does a Shared Kernel introduce that Conformist and ACL don't have, and how do teams typically mitigate it?A Shared Kernel means both teams' code changes together whenever the shared piece changes, so an uncoordinated change from either side can break the other team's build or runtime behavior without warning. Teams typically mitigate this by keeping the shared kernel deliberately tiny and stable (e.g. just a Money or DateRange value type), requiring joint code review or a shared test suite for any change to it, and treating any growth pressure on the kernel as a signal to split it back into per-context models instead.
- If an upstream team wants to serve ten different downstream consumers, why would Open Host Service with Published Language usually beat ten separate Conformist or ACL relationships?Without a published language, the upstream either negotiates a bespoke contract with each of the ten teams (expensive, and every upstream change risks breaking several bespoke integrations differently) or forces every downstream into ad hoc Conformist/ACL work against an undocumented internal model. A single well-documented, versioned public contract lets the upstream evolve behind that contract's compatibility guarantees and lets every downstream team integrate against one stable target instead of ten different negotiated arrangements.
Like traveling to a country with a different currency: Conformist is spending in their currency directly and just mentally converting prices as you go (fast, but you're exposed to their exchange-rate swings); an Anti-Corruption Layer is exchanging your money at the border into your home currency once, so everything you do afterward uses your own, stable numbers regardless of what happens to their currency later.
saying these in an interview costs you the question
- Uses 'context map' and 'API gateway' interchangeably
- Can't distinguish Conformist from Anti-Corruption Layer
- Recommends Shared Kernel as a default/frequent integration strategy
- Thinks translation/ACL logic belongs inside the domain model rather than at the boundary
- Assumes every integration should use the same pattern regardless of upstream stability