pattern

Conformist

also called Conformist Context Relationship, Upstream Model Adoption

Accepting an upstream model as your own at the integration boundary instead of translating it - trading independence you cannot use for the cost of a second model you would otherwise maintain forever.

dddcontext mappingintegrationvendoranti-corruption layer

A four-engineer team has 6 weeks to ship identity verification through a third-party provider whose API has not changed in two years. The reviewing architect requires an anti-corruption layer: a model the team defines itself, with mappings in both directions.

The insurance is real and so is the premium. A second model means two shapes, a translation each way, tests for the translation, and two changes for every new field the provider exposes instead of one. On a team of 4 with 6 weeks, that premium comes out of the only budget that matters.

Conformist, one of the context relationships in Evans' 2003 work, is the deliberate decision not to pay it: adopt the upstream model as the integration model, and translate only where one of your own invariants depends on the shape. It is the relationship for an upstream you cannot influence and do not need to improve.

Why it matters

Most integration advice is written as though translation were free and coupling were the only cost. It is not. Translation layers are code that must be maintained, understood and kept in step with two models that drift at different rates, and they are built most often exactly when there is the least evidence about what the eventual abstraction should look like.

Conformist is also the honest name for what many teams already do. A codebase with a "wrapper" that mirrors the vendor's fields one-to-one has conformed; calling it an anti-corruption layer only hides that the protection is nominal. Naming the relationship makes the containment a decision rather than an accident.

Implementation patterns

  • One module owns the upstream types. The vendor's classes, enums and error codes live there and nowhere else. This is the whole discipline, and it is what separates conformist from contamination.
  • Translate only invariant-bearing concepts. If your rule is "no transaction until verification is approved and unexpired", you need your own approved and expires-at with your own validity logic; the other forty fields stay in the provider's shape.
  • Expose use-case-shaped functions, not the client. verify(customer) returning your two concepts, rather than handing callers the provider's response object.
  • Record the real interactions. A fake built from recorded provider responses plus a nightly conformance run against the sandbox keeps the fake honest for the cost of one scheduled job.
  • Write down the exit. One page naming what would be needed to switch: the module boundary, the concepts to be translated, and the estimate. A conformist relationship with a known exit is a decision; one without is a bet.

Industry example

A food-delivery platform in the mould of Swiggy integrating restaurant point-of-sale vendors faces this on every integration. The menu models differ in every detail, none of the vendors will change for a single partner, and the platform's own domain cares about perhaps 6 of 40 fields: item availability, price, modifier groups, preparation time, tax class, store open or closed.

The pattern that works is conformist per vendor inside the integration module, with those six concepts translated into the platform's own model at the edge. The failure mode is the opposite choice made early: one canonical menu model derived from the first vendor, which the third vendor's modifier structure does not fit, rewritten under delivery pressure with twelve consumers already depending on it.

Failure scenarios

  • Containment breach. Vendor types appear in pricing, onboarding and reporting; the integration module is no longer where the vendor lives, and switching providers becomes a rewrite.
  • Invariant adopted along with the model. The provider allows an approval with no expiry, so your database now does too, and the illegal state is in your data.
  • Conforming to an unstable upstream. The vendor redesigns its API and the breakage is spread over every call site rather than absorbed by a translator.
  • Silent semantic drift. A vendor field changes meaning without changing its name or type - a status that used to mean "queued" now meaning "accepted" - and nothing in your code notices.
  • Conforming to an internal team out of politeness, which hides a boundary problem that a conversation would have fixed.

Trade-offs

Choose Gains Pays
Conformist no second model - no mapping to maintain - fastest path to working integration upstream's vocabulary in your integration module - their changes are your changes - a switch costs more later
Anti-corruption layer your vocabulary everywhere - a shock absorber for upstream change - a second provider slots in a permanent second model plus mappings and tests - two changes per new field - an abstraction designed on one example
Shared kernel no translation and no drift between the two models only available with a team you can negotiate with - a shared release cadence

When not to use it

When a second provider is contracted. Two upstream shapes and one domain concept is the condition the anti-corruption layer exists for; translation is now real work rather than insurance.

When the upstream model contradicts an invariant you must hold. Your domain cannot represent an impossible state just because the vendor can.

When the upstream is unstable - a startup mid-redesign, or an internal service being rewritten - where the premium buys a known absorber for a change you can evidence.

When the upstream is another team in your organisation. There you have influence, and the better move is usually to ask them to publish a supported model at their boundary rather than conforming quietly or translating in the dark.

Interview question

Q: Your team is told to integrate a payment provider in 6 weeks and an architect insists on a full anti-corruption layer. Make the case for conforming instead, and tell me what you would commit to so that the decision is reversible.

What a strong answer covers: the three deciding facts - influence over the upstream, adequacy of its model, and how much of your domain touches it · the quantified premium of the translator, including the two-changes-per-field tax · the containment commitment, with the vendor's types confined to one module and a test that fails if they appear elsewhere · translating the two or three concepts your invariants depend on, so the domain is not importing the vendor's rules · the written exit estimate · and the named conditions that would flip the decision, so the architect's concern is scheduled rather than dismissed.

Quick check

Quiz: When is conformist the correct context relationship, and what single rule keeps it from becoming contamination? — When the upstream is stable, adequate and beyond your influence, and your domain touches it in few places; the rule is that the upstream's types live in one module and never reach the core.

Flashcard: What does an anti-corruption layer cost per new upstream field, and conformist? — Two changes against one: the translator and the mapping tests both move, plus your own model, where a conformist integration changes only the point of use.