Anti-Corruption Layer (ACL)

How to protect your domain from contamination by external, legacy, or third-party systems through a translation layer that isolates incompatible models.

What problem it solves

When your system needs to integrate with an external system (legacy, third-party, or from another team), the data models and business concepts are usually very different. Without protection, the external system’s model contaminates your domain:

Sin ACL:
  Tu Servicio de Pedidos
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ createOrder(legacyCustomerCode,  β”‚
  β”‚   legacyProductSKU,              β”‚  ← Conceptos del sistema legacy
  β”‚   legacyPriceFormat,             β”‚     infiltrados en tu dominio
  β”‚   legacyTaxCalculation)          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  Sistema Legacy ERP (modelo incompatible)

The concrete problems are:

  • Model contamination: Your domain adopts names, structures, and rules from the external system
  • Tight coupling: Changes in the external system require changes in your domain
  • Growing complexity: Your code mixes business logic with translation logic
  • Hard to test: You cannot test your domain without simulating the external system

How it works

The Anti-Corruption Layer is a translation layer that sits between your system and the external system. It translates concepts, formats, and protocols in both directions, keeping your domain clean.

Con ACL:
  Tu Servicio de Pedidos
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ createOrder(customerId,      β”‚
  β”‚   productId,                 β”‚  ← Tu modelo de dominio limpio
  β”‚   price,                     β”‚
  β”‚   tax)                       β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚    Anti-Corruption Layer     β”‚
  β”‚                              β”‚
  β”‚  customerId β†’ legacyCustCode β”‚
  β”‚  productId β†’ legacySKU       β”‚  ← TraducciΓ³n bidireccional
  β”‚  price β†’ legacyPriceFormat   β”‚
  β”‚  tax β†’ legacyTaxCalc         β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             β–Ό
  Sistema Legacy ERP

ACL components

ComponentResponsibilityExample
TranslatorConverts data between modelsLegacyCustomer β†’ Customer
AdapterAdapts the external system’s interfaceREST β†’ SOAP, JSON β†’ XML
FacadeSimplifies the external system’s API5 legacy calls β†’ 1 clean method
MapperMaps fields and values between modelsstatus: "A" β†’ status: "active"

Translation example

Tu modelo (dominio limpio):
  Order {
    id: "ORD-001"
    customer: { id: "C-123", name: "MarΓ­a GarcΓ­a" }
    items: [{ product: "Laptop", quantity: 1, price: 1500 }]
    status: "confirmed"
  }

Modelo del sistema legacy:
  LEGACY_ORDER {
    ORDER_NUM: "00000001"
    CUST_CODE: "CUST00123"
    CUST_NM: "GARCIA, MARIA"
    LINE_ITEMS: "LAPTOP|1|150000"  (precio en centavos, pipe-separated)
    STAT_CD: "CF"
  }

El ACL traduce entre ambos modelos:
  OrderACLTranslator.toExternal(order) β†’ LEGACY_ORDER
  OrderACLTranslator.fromExternal(legacyOrder) β†’ Order

Placement of the ACL

The ACL can be implemented at different levels:

As an independent service:

Tu Servicio ──► ACL Service ──► Sistema Externo

As a module within your service:

Tu Servicio
  β”œβ”€β”€ Domain Layer (limpio)
  β”œβ”€β”€ Application Layer
  └── ACL Module ──► Sistema Externo

As part of the API Gateway:

API Gateway (con ACL) ──► Sistema Legacy

Advantages

  • Clean domain: Your business model is not contaminated with external concepts
  • Decoupling: Changes in the external system are absorbed in the ACL, not in your domain
  • Isolated testing: You can test your domain without the external system (mock the ACL)
  • Gradual migration: You can replace the external system without changing your domain
  • Clarity: Translation logic is centralized and explicit
  • Multiple integrations: You can have different ACLs for different external systems

Trade-offs / Disadvantages

  • Additional code: The ACL is code that must be written, tested, and maintained
  • Latency: One more processing layer on each call
  • Change synchronization: When the external system changes its API, the ACL must be updated
  • Mapping complexity: Some mappings are complex (data that has no direct equivalent)
  • Risk of a bloated ACL: If the ACL grows too much, it can become a problem in itself
  • Double maintenance: Changes in your model or in the external one require updating the ACL

When to use it

  • Integration with legacy systems that have incompatible data models
  • Consuming third-party APIs with models different from your domain
  • Gradual migration from a monolith to microservices
  • When multiple bounded contexts need to communicate without contaminating their models
  • Integration with systems that use different protocols or formats (SOAP, XML, flat files)
  • When the external system changes frequently and you want to isolate the impact

When to avoid it

  • Integration with systems that share the same domain model
  • Simple external APIs where translation is trivial (a mapping of 2-3 fields)
  • Prototypes where development speed matters more than model purity
  • When the external system is temporary and will be replaced soon

Common technologies and implementations

CategoryOptions
Design patternsAdapter, Facade, Translator, Mapper
Mapping librariesMapStruct (Java), AutoMapper (.NET), class-transformer (TS)
IntegrationApache Camel, Spring Integration, MuleSoft
API TranslationGraphQL as an ACL, API Gateway with transformations

Relationship with other patterns

  • DDD (Bounded Context): The ACL protects the boundaries between bounded contexts
  • API Gateway: Can implement ACL functions for external systems
  • BFF: The BFF acts as an ACL between the frontend and the microservices
  • Shared Kernel: An alternative to the ACL when two contexts deliberately share a common model
  • Microservices: Each microservice can have its own ACL for external integrations

Next steps

The Anti-Corruption Layer is essential for maintaining the integrity of your domain. To understand how to handle failures in communication with external systems, explore the Retry pattern. To share models in a controlled way between contexts, review the Shared Kernel.