Cloud · Gateway & Routing · DWG NO. 043

Anti-Corruption Layer

Put a translation boundary between your domain model and an external or legacy system, so its model never leaks into yours.

TypeGateway/Routing
ScopeBoundary between two systems
ComplexityMedium
Common inLegacy integrations, third-party APIs, mergers of separate systems

A foreign data model quietly becomes part of your own

Calling a legacy system's API directly from deep inside your domain logic — and passing its field names, quirks, and status codes straight through — means your domain model is now shaped by decisions someone else made in a system you don't control. When that legacy system's model changes or gets replaced, the damage spreads throughout your codebase.

A translator that guards the boundary

The anti-corruption layer sits between the two systems. It speaks the legacy system's model on one side and your domain's model on the other, translating between them. Your domain code only ever sees its own clean model — it has no idea the legacy system's data shape even exists.

Domain Model(yours)translateACLraw callsLegacy Sys

Translating a legacy customer record into a clean domain model

Domain code only ever sees a `Customer`. All of the legacy system's odd field names and status codes stay contained inside the ACL.

customer-acl.js
// Legacy system's actual shape — verbose, inconsistent field names, numeric status codes.
const legacySystem = {
  getCustRecord: (id) => ({
    CUST_ID: id, FNAME: "Alex", LNAME: "Rivera", STAT_CD: 2, // 2 = active
  }),
};

// Your clean domain model.
class Customer {
  constructor({ id, fullName, isActive }) {
    this.id = id;
    this.fullName = fullName;
    this.isActive = isActive;
  }
}

// The anti-corruption layer: the only code that knows the legacy shape exists.
class LegacyCustomerAdapter {
  getCustomer(id) {
    const raw = legacySystem.getCustRecord(id);
    return new Customer({
      id: raw.CUST_ID,
      fullName: `${raw.FNAME} ${raw.LNAME}`,
      isActive: raw.STAT_CD === 2,
    });
  }
}

const acl = new LegacyCustomerAdapter();
const customer = acl.getCustomer("c-1");
console.log(customer); // clean Customer — no CUST_ID, FNAME, or STAT_CD in sight

Trade-offs