skip to content

questions

6

When a new service needs to integrate with a legacy system that has messy, inconsistent naming and data structures, what problem does putting an Anti-Corruption Layer between them solve?

level: juniorimportance: must knowfreq 70%

answer

  1. translation facade
  2. foreign model quarantine
  3. facade + adapters + translators
  4. prevents legacy leak
  5. paired with strangler fig

basics

~10 s

An Anti-Corruption Layer is a translator between two systems. It converts the old system's weird data and terms into clean data your new system understands, so the mess doesn't spread into your new code.

solid answer

~30 s

An Anti-Corruption Layer (ACL) is a translation facade at the boundary between a new system and a legacy or external one. It converts requests and responses between the two models so the new system's domain code never has to know the legacy system's field names, status codes, or quirks. Without it, those foreign concepts leak into the new codebase and every consumer becomes coupled to legacy shapes and bugs. The ACL isolates that translation in one place, so the new domain model stays clean while the legacy system keeps its own internal shape untouched.

go deeper

for a junior

Should be able to say, in plain terms, that an ACL is a translation boundary that keeps a legacy system's mess out of the new code, and give a rough real-world analogy.

for a middle

Should be able to name the concrete pieces (facade, adapter, translator/mapper) and explain, with a small example, how a request crosses the boundary and gets converted.

for a senior

Should discuss the maintenance and latency cost of the layer, and describe at least one production failure mode (leaky translation, translation drift) they'd guard against with tests or contract checks.

for a principal

Should place the ACL in a broader modernization strategy (e.g. strangler fig migration), discuss when the cost isn't justified, and describe how ownership and lifecycle of the layer is managed as the legacy system is eventually retired.

## Two mental models to reconcile Every integration between two systems has to reconcile two different mental models: the vocabulary, data shapes, and invariants each side uses to represent the same real-world concepts. When two systems are built by different teams, at different times, or joined through a merger or acquisition, their models rarely agree - one side might: - call a customer a 'party', - encode status as a three-letter legacy code instead of an enum, - represent money as an untyped string with an implicit currency. An **Anti-Corruption Layer (ACL)** is a dedicated translation boundary - typically implemented as a facade backed by adapters and mapper/translator components - placed between the 'clean' new system and the 'foreign' legacy or external one. Every inbound and outbound call crosses through this facade, which converts the foreign representation into the new system's own domain model (and back again), so that no legacy-specific concept, naming, or invariant ever appears inside the new codebase. ## The moving parts Mechanically, the ACL is usually built from three cooperating pieces. 1. A **facade** or gateway interface exposes operations phrased in the new system's own ubiquitous language (e.g. `getOrder(orderId): Order`), so consumers on the clean side never see the legacy contract directly. 2. Behind that facade sit **adapters** that know how to actually talk to the legacy system - a SOAP client, a raw JDBC connection to an old database, a screen-scraper, or a vendor SDK. 3. Between the facade and the adapters sit **translators** (mappers) that convert the legacy DTOs, field-by-field and rule-by-rule, into the new system's domain objects, resolving mismatches such as different enumerations, units, null semantics, or missing fields that must be defaulted or looked up elsewhere. In an asynchronous or event-driven setting, the same idea applies to message payloads: an ACL can subscribe to a legacy system's event stream, translate each message into the new system's event schema, and republish it, so downstream consumers only ever see the new format. ## Why the pattern exists The reason this pattern exists is to protect the integrity of the new system's domain model. If a team lets legacy concepts leak straight through - passing legacy status codes into business logic, embedding legacy field names in APIs, or copying legacy validation quirks - the corruption spreads: - every consumer of the new system now has to understand and defensively code around the legacy system's history, - any future change to the legacy system (or its eventual retirement) becomes a shotgun-surgery exercise touching dozens of call sites. Concentrating the translation logic in one boundary means the **blast radius** of a legacy quirk, bug, or format change is confined to the ACL's mappers, and the rest of the new system can be designed, tested, and reasoned about using only its own clean model. ## The trade-off The trade-off is real cost on the other side of that ledger. - **Extra code.** The ACL is extra code that has to be written, tested, and kept in sync with both models as they evolve - a genuine double-maintenance burden, since a schema change on either side can silently break a translator. - **An extra hop.** It also adds a network or in-process hop, which shows up as latency and, at scale, as another component that needs its own monitoring, retries, and failure handling. - **A second legacy system.** If a team is not disciplined, the ACL itself can accrete unrelated business logic over time and become a second legacy system - a shared, poorly-owned service that nobody wants to touch, defeating its own purpose. ## Production failure modes Production failure modes cluster around a few recurring shapes. 1. The most common is a **'leaky' ACL**, where under time pressure a legacy field, status code, or naming convention is passed through untranslated because 'we'll fix it later' - and it never gets fixed, so the corruption the layer was built to prevent happens anyway, just more slowly. 2. A second is **translation drift**: the legacy system's API or database schema changes (a new status value, a renamed column) and the translator silently maps it incorrectly instead of failing loudly, producing subtly wrong domain objects that surface as confusing bugs far downstream. 3. A third is **performance degradation** from naive translation - for example, calling the legacy system once per item instead of batching, turning a single logical operation into an N+1 storm across the ACL boundary. ## A worked example, and where the pattern comes from A concrete, well-documented example is a **strangler-fig-style migration**: a company migrating an old on-premises ERP to a new cloud-native order-management service builds an ACL as a small facade service that calls the ERP's SOAP endpoints, translates its idiosyncratic status codes and XML shapes into the new service's `Order` and `StockLevel` domain types, and exposes only that clean contract to the new microservices. This exact use case - protecting a new system from a legacy or third-party model during incremental modernization - is why cloud architecture guidance (such as Microsoft's Azure Architecture Center) catalogs Anti-Corruption Layer as one of its cloud design patterns, distinct in emphasis from (but historically rooted in) its original definition as a Domain-Driven Design pattern for managing relationships between bounded contexts.

  • Does the ACL have to be a separate deployed service, or can it be a library inside the consuming application?
    It can be either - the pattern only requires that translation logic be isolated behind a facade, not that it run in its own process. For a single consuming service, an in-process module or library with a facade interface and mapper classes is often enough and avoids an extra network hop. A standalone service makes sense when multiple independent consumers need the same translation, or when the legacy protocol (e.g. mainframe screen-scraping) needs specialized infrastructure that shouldn't be duplicated in every consumer.
  • How is an Anti-Corruption Layer different from a plain API gateway or a generic adapter pattern?
    A generic adapter typically just changes an interface's shape (e.g., method signatures) without necessarily protecting a whole domain model, and a gateway usually focuses on routing, auth, and cross-cutting concerns rather than semantic translation. An ACL is narrower in intent: its job is specifically to prevent a foreign domain model's concepts from leaking into a protected domain model, which is why it always includes deliberate mapping/translation logic, not just protocol or routing changes.
  • What happens if you skip the ACL and integrate directly against the legacy system's API?
    Every consumer ends up coupled to the legacy system's naming, status codes, and bugs, so a later legacy change or retirement requires touching every call site instead of one boundary. It also makes the new domain model harder to keep clean, since developers under deadline pressure will often just pass legacy shapes straight through rather than modeling them properly.

Like a diplomatic interpreter at a summit: each side keeps speaking its own language, and the interpreter translates in both directions so neither side's phrasing or idioms contaminate the other's understanding.

saying these in an interview costs you the question

  • Can't explain what leaks into the domain if there's no ACL
  • Thinks the ACL is only about changing method names/HTTP paths
  • Assumes ACL must always be a separate microservice
  • No mention of the extra latency/maintenance cost
  • Confuses this pattern with a generic API gateway with no translation logic

context

open as a page

Concretely, what are the moving parts you'd build when implementing an Anti-Corruption Layer between a new service and a legacy backend, and how does a request flow through them?

level: middleimportance: must knowfreq 60%

basics

~20 s

You build three parts: a front door that speaks your new system's language, a connector that knows how to call the old system, and a converter that maps data between the two. A request goes in the front door, gets converted, sent to the old system via the connector, and the response gets converted back.

open as a page

A staff engineer is deciding whether to introduce an Anti-Corruption Layer for a new microservice's integration with an external partner API. What are the concrete costs versus benefits they should weigh before committing to it?

level: middleimportance: should knowfreq 55%

basics

~20 s

It costs extra code, extra testing, and a bit of speed to build and maintain the translator. It pays off by keeping your own system clean and easy to change even if the outside system is messy or changes later.

open as a page

In a cloud migration where a new set of microservices is being built to gradually replace an on-premises monolith, how does an Anti-Corruption Layer typically get deployed and operated as a cloud integration pattern, and how does that differ from its original description as a Domain-Driven Design concept?

level: seniorimportance: should knowfreq 45%

basics

~20 s

In cloud migrations, the ACL is often its own small deployed service (or gateway) that talks to the old on-prem system and the new cloud services, translating between them while the old system is gradually replaced. Originally the same idea was described as code inside a team's own codebase for keeping domain models from mixing.

open as a page

In a production system, an Anti-Corruption Layer sits in front of a flaky third-party API and has been running fine for months. What are the most likely ways this kind of layer fails or degrades in production, and how would you design against each one?

level: seniorimportance: should knowfreq 50%

basics

~20 s

The translator can quietly mis-map data when the outside system changes without telling you, the extra layer can slow things down or become a single point of failure, and it can turn into a dumping ground for random logic if nobody owns it. Guard against these with tests, monitoring, and clear ownership.

open as a page

As a principal engineer setting integration standards across many teams, when would you actively tell a team NOT to build an Anti-Corruption Layer for a given integration, and how do you decide when an existing one should be retired?

level: principalimportance: should knowfreq 40%

basics

~20 s

Skip it when the outside system's model is already close to yours, the integration is small, short-lived, or has one consumer, or you're about to fully replace the outside system anyway. Retire an existing ACL once nothing is left using the system it protects against.

open as a page