Ch. 10 · Microservices

Microservices Anti-Corruption Layer

Protect a service's domain model from a foreign or legacy model with a translation layer at the boundary.

~2 min readadvancedupdated Oct 5, 2026

When a service integrates with a legacy system or a vendor API whose model does not match its own, letting that foreign model spread through the code couples the service to someone else’s design. An anti-corruption layer translates at the boundary, so the service’s domain stays clean and independent.

Before you start

You should be comfortable with domain modeling and service boundaries. This article covers the translation boundary; it assumes basic data-mapping skills.

Step-by-step walkthrough

Step 1: Define your own model first

Start with the model your service needs, named in your domain’s language, rather than importing the vendor’s types. This keeps the core logic independent and testable, and it makes the foreign dependency an implementation detail behind the boundary.

Step 2: Translate at a single boundary

Write one adapter that maps the foreign payload to your model and back, with its own DTOs. Every call to the external system passes through it, so a change on their side touches one file instead of the whole codebase.

Step 3: Keep foreign types from leaking

Do not expose the foreign types in your public interfaces or persistence. If a vendor object appears in your handlers or database, the isolation is gone and the coupling returns. The adapter returns your types only.

Worked scenario

The adapter maps a vendor payload to the domain type.

interface VendorOrder { order_id: string; total_cents: number }
interface Order { id: string; total: number }

function toOrder(vendor: VendorOrder): Order {
  return { id: vendor.order_id, total: vendor.total_cents / 100 };
}
TypeScript

Walk through the example

toOrder converts the vendor’s naming and units to the domain’s. If the vendor renames order_id or changes units, only this function changes, and the rest of the service is untouched. The domain type Order never depends on VendorOrder, which is the isolation the layer provides.

Common mistake

Sharing DTOs across services or letting the vendor’s model become the internal model, which spreads the foreign coupling everywhere. Another is doing the translation in many places, so behavior drifts and fixes must be repeated.

Verify the behavior

Change a field name in the vendor payload and confirm only the adapter fails, not the domain. Assert the domain tests run without any vendor fixture. Check that no vendor type appears in the service’s public interfaces or database schema.

Interview exercise

Where should the anti-corruption layer live: in the integrating service or the legacy system?

Answer and reasoning

In the consuming service, because that is the side that needs its own model protected. Putting it in the legacy system would require changing the system you are trying to isolate from, which is usually the harder and riskier side. The consumer owns the translation and can evolve it as its own model changes.

Continue learning

Compare boundaries in Service boundaries and Data ownership. Read the microservices.io patterns and try the Microservices interview questions.

More in Microservices

read ✓Microservices · hard

Microservices Backend for Frontend

Use a per-client BFF to aggregate services and shape responses, without letting it become a shared god service.

~2 min readread →
read ✓Microservices · hard

Microservices API Versioning and Evolution

Evolve service APIs without breaking consumers using additive changes, explicit versioning and consumer-driven contracts.

~2 min readread →
esc