PeakLab
Back to glossary

Anti-Corruption Layer (ACL)

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

Updated on March 30, 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.

Let's talk about your project

Need expert help on this topic?

Our team supports you from strategy to production. Let's chat 30 min about your project.

The money is already on the table.

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

Web development, automation & AI agency

[email protected]
Newsletter

Get our tech and business tips delivered straight to your inbox.

Follow us
Crédit d'Impôt Innovation - PeakLab agréé CII

© PeakLab 2026