image de chargement
Back to glossary

Anti-Corruption Layer (ACL)

Architectural pattern that isolates a system from external inconsistencies by translating incompatible models and protocols.

Updated on January 8, 2026

The Anti-Corruption Layer (ACL) is a strategic Domain-Driven Design pattern that creates a translation layer between your business domain and external legacy or third-party systems. It preserves the integrity of your domain model by preventing contamination from inappropriate concepts, data structures, or logic originating from other systems.

Fundamentals

  • Domain model isolation: shields your bounded context from unsuitable abstractions of external systems
  • Bidirectional translation: converts outgoing requests and incoming responses according to each system's needs
  • Architectural decoupling: enables independent evolution of your domain without being constrained by legacy systems
  • Single responsibility: centralizes all adaptation logic in a dedicated, testable layer

Benefits

  • Domain model purity preservation without technical compromises imposed by external systems
  • Progressive migration facilitation by allowing old and new systems to coexist
  • Technical debt reduction by preventing obsolete models from propagating into new code
  • Improved testability by isolating external dependencies behind a controlled interface
  • Increased flexibility to replace or modify external systems without impacting the business core

Practical Example

Consider a modern e-commerce application that must integrate a legacy ERP system using numeric product codes, while the new domain uses UUID identifiers and a different categorization structure:

anti-corruption-layer.ts
// Modern domain model (our system)
interface Product {
  id: string; // UUID
  name: string;
  category: ProductCategory;
  pricing: Money;
}

interface ProductCategory {
  slug: string;
  hierarchy: string[];
}

// Legacy ERP system model
interface LegacyProductDTO {
  product_code: number;
  product_name: string;
  cat_id: number;
  price_cents: number;
  currency_code: string;
}

// Anti-Corruption Layer
class ERPProductAdapter {
  private categoryMapping: Map<number, ProductCategory>;

  constructor(private erpClient: ERPClient) {
    this.categoryMapping = this.initializeCategoryMapping();
  }

  async fetchProduct(productId: string): Promise<Product> {
    // Convert UUID -> legacy numeric code
    const legacyCode = this.convertToLegacyCode(productId);
    
    // Call legacy system
    const legacyProduct = await this.erpClient.getProduct(legacyCode);
    
    // Translate to our domain model
    return this.toDomainModel(legacyProduct);
  }

  private toDomainModel(legacy: LegacyProductDTO): Product {
    return {
      id: this.convertToUUID(legacy.product_code),
      name: legacy.product_name,
      category: this.categoryMapping.get(legacy.cat_id) ?? this.defaultCategory(),
      pricing: {
        amount: legacy.price_cents / 100,
        currency: legacy.currency_code
      }
    };
  }

  async saveProduct(product: Product): Promise<void> {
    // Translate our model to legacy format
    const legacyDTO = this.toLegacyDTO(product);
    await this.erpClient.updateProduct(legacyDTO);
  }

  private toLegacyDTO(product: Product): LegacyProductDTO {
    return {
      product_code: this.convertToLegacyCode(product.id),
      product_name: product.name,
      cat_id: this.findLegacyCategoryId(product.category),
      price_cents: Math.round(product.pricing.amount * 100),
      currency_code: product.pricing.currency
    };
  }
}

Implementation

  1. Identify boundaries: map bounded contexts and locate external systems that threaten your model's integrity
  2. Define domain interface: create pure abstractions reflecting business needs without external technical considerations
  3. Create adapters: develop translation components that convert between your model and the external system's model
  4. Implement mapping: establish transformation rules for data, identifiers, and business concepts
  5. Handle inconsistencies: define strategies for imperfect translations (defaults, enrichment, validation)
  6. Test exhaustively: verify bidirectional conversions and edge cases with unit and integration tests
  7. Monitor translations: instrument the ACL to detect anomalies and track integration performance

Architecture Advice

The Anti-Corruption Layer should not become a God Object. If translations become complex, decompose the ACL into specialized adapters (ProductAdapter, OrderAdapter, etc.) with clearly defined responsibilities. Use the Facade pattern to provide a unified interface when necessary.

  • AutoMapper / Mapster: object-to-object mapping libraries to automate simple conversions
  • NestJS Interceptors: mechanism to implement ACLs declaratively in NestJS applications
  • GraphQL DataLoader: useful pattern for optimizing translations with batching and caching
  • OpenAPI Generator: automatic generation of typed clients for external APIs to encapsulate
  • Wiremock / MSW: mocking tools to test ACL without depending on external systems

The Anti-Corruption Layer represents a strategic investment that protects your most valuable software asset: a coherent and expressive domain model. By accepting translation complexity in a dedicated layer, you preserve the simplicity and maintainability of your business core, facilitating long-term evolution and progressive migration of legacy systems.

Themoneyisalreadyonthetable.

In 1 hour, discover exactly how much you're losing and how to recover it.