Anti-Corruption Layer

Enterprise Integration Jan 6, 2025 JAVASCRIPT

Definition

Imagine you’re building a beautiful, modern house, but you have to connect it to a 100-year-old sewer system that uses completely different pipe sizes and fittings. You could rebuild your entire plumbing to match the old system, but then your new house would be stuck with outdated standards. Or you could build an adapter section between your modern plumbing and the old system - a translation layer that lets each side speak its own language.

An Anti-Corruption Layer (ACL) is that adapter section for software. It sits between your clean, modern application and messy external systems - legacy APIs, third-party services with weird data formats, or old databases with cryptic column names. The ACL’s job is to translate between the two worlds so that the ugliness of the external system doesn’t “corrupt” the clean design of your application.

The term comes from Domain-Driven Design (DDD), where “corruption” refers to foreign concepts leaking into your carefully designed domain model. Without an ACL, you might find legacy field names like “CUST_ADDR_LN_1” scattered throughout your modern codebase, or business rules that only make sense in the context of a 20-year-old system. The ACL absorbs all that mess, presenting a clean interface to the rest of your application.

Example

Integrating a Legacy Mainframe: A bank is building a modern mobile app but needs to talk to their 1980s mainframe for account data. The mainframe returns data like {"ACC_NBR": "0001234", "BAL_CURR": 15023, "STAT_CD": "A"}. The ACL transforms this into {"accountNumber": "1234", "balance": 150.23, "status": "active"} so the mobile app never sees mainframe-speak.

Third-Party Payment Processor: Your app integrates with a payment provider that has an eccentric API. Their API returns amounts in cents as strings, uses different error codes than your system, and has field names like txn_ref_id. Your ACL translates all of this so your application works with clean objects like {amount: 15.99, reference: "abc123"}.

Multiple External Services: Your shipping service integrates with FedEx, UPS, and DHL, each with completely different APIs and data formats. Your ACL presents a unified “Carrier” interface to your application: getRate(), createShipment(), trackPackage(). Your app doesn’t know or care which carrier it’s talking to - the ACL handles all the translation.

Acquired Company Systems: When companies merge, their systems need to communicate. Company A uses customer IDs like “CUST-12345” while Company B uses UUIDs. Company A stores addresses as single strings while Company B uses structured objects. The ACL lets both systems work together without either having to change.

Government Regulatory APIs: Submitting regulatory filings often requires interacting with antiquated government systems that use XML formats from 2005 with strange namespaces. Your modern REST API internal system uses JSON. The ACL handles the XML/JSON conversion and maps your clean domain concepts to the government’s required format.

Analogy

The Embassy Translator: When foreign diplomats meet, they don’t expect each other to learn their language. Instead, translators convert between languages in real-time, handling not just words but cultural nuances and diplomatic protocols. An ACL is your embassy translator - it converts between your application’s “language” (modern data models) and the foreign system’s “language” (legacy formats), handling all the weird quirks so neither side has to change.

The Power Adapter for Travel: When you travel internationally, you bring a power adapter. Your laptop has a standard plug, and you’re not going to rewire it for every country. The adapter translates between your device’s expectations and whatever outlet exists in that country. Similarly, your application has clean interfaces, and the ACL adapts them to whatever weird API exists externally.

The Universal Remote: You buy a universal remote that works with your TV, sound system, and streaming box - each device has different protocols and commands. The remote translates your simple buttons (Volume Up, Play) into the specific signals each device needs. Your ACL does the same - providing a clean interface to your application while handling the peculiarities of each external system.

The Kitchen Translator: Imagine a chef who only speaks French working in an Italian restaurant. A kitchen translator takes the French chef’s instructions and converts them to Italian for the staff, and converts their responses back to French. The chef doesn’t need to learn Italian; they just cook. Your application doesn’t need to learn legacy system quirks; the ACL handles translation.

Code Example


// Legacy external API response
const legacyResponse = {
  CUST_ID: "12345",
  CUST_NM: "John Doe",
  ADDR_LN_1: "123 Main St",
  STAT_CD: "A"
};

// Anti-Corruption Layer
class CustomerACL {
  toDomain(legacyCustomer) {
    return {
      id: legacyCustomer.CUST_ID,
      name: legacyCustomer.CUST_NM,
      address: {
        street: legacyCustomer.ADDR_LN_1
      },
      status: this.mapStatus(legacyCustomer.STAT_CD)
    };
  }

  mapStatus(statusCode) {
    const mapping = {
      'A': 'active',
      'I': 'inactive',
      'P': 'pending'
    };
    return mapping[statusCode] || 'unknown';
  }

  toLegacy(domainCustomer) {
    // Translate domain model back to legacy format
    return {
      CUST_ID: domainCustomer.id,
      CUST_NM: domainCustomer.name,
      // ... reverse mapping
    };
  }
}

Standards & RFCs

Standards & RFCs
2)- Patterns of Enterprise Application Architecture (Martin Fowler) - Related integration patterns