Introduction
Ever had to integrate with a messy legacy API or third-party service that returns weird data, uses inconsistent naming, or breaks every other release?
Directly wiring that into your business logic is like letting strangers walk into your house with muddy shoes — your clean domain model gets dirty real quick.
This is where the Anti-Corruption Layer (ACL) comes in.
It acts as a translator + shield, protecting your core system from external chaos while keeping your domain language pure.
Why Use an Anti-Corruption Layer?
Without an ACL, you get:
Tight Coupling: API response shapes leak into your codebase
Fragile Logic: Every API change becomes a breaking change
Spaghetti Mapping: dto.title, dto.price, dto.cust_id all over the place
Maintenance Hell: Switching providers later means rewriting everything
With ACL, you get a single, well-defined place that adapts external data into your internal language and shields your domain from breaking changes.
Visualizing the Problem
Imagine you’re building an e-commerce platform and pulling product data from a third-party API:
{
"id": 42,
"title": "Winter Jacket",
"price": "199.99",
"images": ["https://cdn.shop.com/img/123.png"],
"category": { "name": "clothing" }
}
If you pass this straight to your Redux store or domain logic, you’re forever coupled to title, price (as a string!), and category.name.
If tomorrow they change title → productName or price → { “amount”: 199.99, “currency”: “USD” }, everything breaks.
How ACL Works
The ACL sits between your domain and the external system, doing three key things:
Adapters: Make the actual API calls
Mappers: Translate raw API responses (DTOs) into clean domain objects
Facades/Services: Provide a single, easy-to-use interface for your domain code
Example ACL Flow
// mappers/productMapper.ts
export function mapProductDtoToDomain(dto: any): Product {
return {
id: dto.id,
name: dto.title,
price: parseFloat(dto.price),
imageUrl: dto.images?.[0] ?? null,
category: dto.category?.name ?? "Uncategorized",
};
}
// productsService.ts
import { mapProductDtoToDomain } from "./mappers/productMapper";
import { ProductsAdapter } from "./adapters/productsAdapter";
export class ProductsService {
constructor(private adapter = new ProductsAdapter()) {}
async getProducts() {
const raw = await this.adapter.fetchProducts();
return raw.map(mapProductDtoToDomain);
}
}
Your Redux slice, controller, or business logic now only consumes ProductsService.getProducts(), and it always receives clean, predictable objects.
Example:
You know how old systems are usually a pain to deal with?
Let me give you a real example — one I’ve actually seen in practice.
Imagine a bank that’s been using this super old COBOL(Common Business-Oriented Language) -based loan system for decades. It works fine, but the data it spits out is a nightmare — weird field names like LN_STAT_CD for loan status, and status codes like 02 that mean “approved,” 03 means “rejected,” and so on.
Now, the bank wants to build a shiny new online loan portal. The problem is, if you just connect this new system directly to the old one, all of that messy naming and business logic leaks straight into your new code. Soon, your nice, clean architecture is just as ugly and hard to maintain as the old system.
That’s where the Anti-Corruption Layer (ACL) comes in.
Instead of letting the new system talk to the old one directly, you put a translation layer in between.
Here’s what happens:
- The new loan portal sends a simple, clean request like
GET /loans/123. - The ACL takes that, figures out how to ask the old COBOL system for that loan, gets the weird raw data back, and then translates it into something nice and human-readable like:
{
"id": "123",
"status": "Approved",
"loanType": "Home Loan",
"approvedDate": "2024-09-14"
}
Now your portal team doesn’t need to care about what LN_STAT_CD=02 means — they just work with a clean object model.
The beauty of this is that if the legacy system changes tomorrow (say 02 becomes A), you don’t need to fix your entire portal. You just update the translation logic in the ACL.
This keeps your new system clean, future-proof, and not “polluted” by all the legacy mess.
Reference
Anti-corruption Layer pattern - Anti-corruption Layer pattern - Azure Architecture Center | Microsoft Learn
Anti Corruption Layer Pattern(Medium) - https://medium.com/solutions-architecture-patterns/anti-corruption-layer-pattern-bd75e1f2be7f
Anti-corruption layer pattern - Anti-corruption layer pattern - AWS Prescriptive Guidance