Bird
Raised Fist0
Microservicessystem_design~7 mins

Anti-corruption layer in Microservices - System Design Guide

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Problem Statement
When integrating a new microservice with an existing legacy system, direct communication can cause the new service to inherit the legacy system's design flaws and complexity. This leads to tight coupling, making the new service fragile and hard to evolve independently.
Solution
An anti-corruption layer acts as a translator and adapter between the new microservice and the legacy system. It isolates the new service from legacy models by converting legacy data and operations into a clean, consistent interface that the new service can use without adopting legacy design issues.
Architecture
┌───────────────┐       ┌───────────────────────┐       ┌───────────────┐
│ New Microservice│──────▶│ Anti-Corruption Layer │──────▶│ Legacy System  │
└───────────────┘       └───────────────────────┘       └───────────────┘

This diagram shows the new microservice communicating only with the anti-corruption layer, which in turn interacts with the legacy system, preventing direct coupling.

Trade-offs
✓ Pros
Prevents legacy system design flaws from spreading into new services.
Enables independent evolution of new microservices without legacy constraints.
Improves maintainability by isolating legacy complexity.
Facilitates gradual migration or replacement of legacy systems.
✗ Cons
Adds extra development and maintenance effort for the translation layer.
Can introduce latency due to additional processing steps.
Requires deep understanding of both legacy and new system models.
Use when integrating new microservices with complex or poorly designed legacy systems where direct coupling would hinder maintainability or evolution, especially in systems with high complexity or long-term migration plans.
Avoid when legacy systems are simple, well-designed, or when integration is minimal and unlikely to cause coupling issues, or when project timelines do not allow for the extra layer development.
Real World Examples
Amazon
Used anti-corruption layers to integrate new microservices with their legacy monolith, allowing independent service evolution without inheriting legacy constraints.
Netflix
Applied anti-corruption layers to isolate new streaming services from legacy billing systems, enabling faster feature development without legacy system dependencies.
Uber
Implemented anti-corruption layers to connect new dispatch microservices with legacy databases, preventing legacy data model pollution.
Code Example
The before code shows the new service tightly coupled to the legacy data format, using legacy keys directly. The after code introduces an anti-corruption layer that translates legacy data into a clean, consistent model. The new service interacts only with this layer, preventing legacy model pollution.
Microservices
### Before: Direct legacy coupling
class LegacySystem:
    def get_user_data(self, user_id):
        # Returns data in legacy format
        return {'usr_id': user_id, 'nm': 'John Doe', 'addr': '123 St'}

class NewService:
    def __init__(self, legacy):
        self.legacy = legacy

    def get_user_profile(self, user_id):
        data = self.legacy.get_user_data(user_id)
        # Directly uses legacy keys
        return f"User {data['nm']} lives at {data['addr']}"


### After: Using Anti-Corruption Layer
class LegacySystem:
    def get_user_data(self, user_id):
        return {'usr_id': user_id, 'nm': 'John Doe', 'addr': '123 St'}

class AntiCorruptionLayer:
    def __init__(self, legacy):
        self.legacy = legacy

    def get_user_profile(self, user_id):
        legacy_data = self.legacy.get_user_data(user_id)
        # Translate legacy data to clean model
        return {
            'id': legacy_data['usr_id'],
            'name': legacy_data['nm'],
            'address': legacy_data['addr']
        }

class NewService:
    def __init__(self, acl):
        self.acl = acl

    def get_user_profile(self, user_id):
        profile = self.acl.get_user_profile(user_id)
        return f"User {profile['name']} lives at {profile['address']}"
OutputSuccess
Alternatives
Strangler Fig Pattern
Gradually replaces legacy system functionality by building new services around it, rather than isolating it with a translation layer.
Use when: Choose when planning a phased migration to fully replace legacy systems over time.
Facade Pattern
Provides a simplified interface to a complex subsystem but does not necessarily translate or isolate legacy models.
Use when: Choose when simplifying access to legacy systems without needing to prevent model corruption.
Summary
Anti-corruption layers prevent legacy system design flaws from affecting new microservices by translating legacy models.
They isolate new services from legacy complexity, enabling independent evolution and maintainability.
This pattern is essential when integrating with complex or poorly designed legacy systems to avoid tight coupling.

Practice

(1/5)
1. What is the main purpose of an Anti-corruption layer in microservices architecture?
easy
A. To translate and isolate differences between two systems to prevent corruption
B. To speed up database queries between microservices
C. To store user session data securely
D. To monitor network traffic between services

Solution

  1. Step 1: Understand the role of the anti-corruption layer

    The anti-corruption layer acts as a translator between two systems with different models or rules.
  2. Step 2: Identify its main goal

    Its goal is to prevent the internal system from being affected or corrupted by external system differences.
  3. Final Answer:

    To translate and isolate differences between two systems to prevent corruption -> Option A
  4. Quick Check:

    Anti-corruption layer = Translation and isolation [OK]
Hint: Think: 'translator' between systems to avoid confusion [OK]
Common Mistakes:
  • Confusing it with caching or monitoring layers
  • Thinking it speeds up queries directly
  • Assuming it stores user data
2. Which of the following is the correct way to implement an anti-corruption layer between two microservices?
easy
A. Directly expose the legacy system's database schema to the new service
B. Allow the new system to write directly to the legacy system's tables
C. Use the same data model in both systems without changes
D. Create a translation interface that maps legacy data to the new system's model

Solution

  1. Step 1: Review implementation best practices

    An anti-corruption layer should translate and map data between systems, not share schemas directly.
  2. Step 2: Identify the correct approach

    Creating a translation interface that maps legacy data to the new system's model isolates differences and protects both systems.
  3. Final Answer:

    Create a translation interface that maps legacy data to the new system's model -> Option D
  4. Quick Check:

    Translation interface = Correct implementation [OK]
Hint: Map legacy data to new model, never share schemas directly [OK]
Common Mistakes:
  • Exposing legacy database schema directly
  • Using identical data models without translation
  • Allowing direct writes to legacy tables
3. Given the following pseudo-code for an anti-corruption layer translating legacy user data, what will be the output?
legacyUser = {"fullName": "Jane Doe", "age": 30}

function translateUser(legacy) {
  return {
    name: legacy.fullName,
    isAdult: legacy.age >= 18
  }
}

newUser = translateUser(legacyUser)
console.log(newUser)
medium
A. {"name": "Jane Doe", "isAdult": false}
B. {"fullName": "Jane Doe", "isAdult": true}
C. {"name": "Jane Doe", "isAdult": true}
D. {"name": "Jane Doe"}

Solution

  1. Step 1: Analyze the translation function

    The function creates a new object with 'name' from 'fullName' and 'isAdult' as true if age >= 18.
  2. Step 2: Apply the function to the legacy user

    legacyUser has fullName 'Jane Doe' and age 30, so isAdult is true.
  3. Final Answer:

    {"name": "Jane Doe", "isAdult": true} -> Option C
  4. Quick Check:

    Translate fullName and check age >= 18 = true [OK]
Hint: Check property mapping and age condition carefully [OK]
Common Mistakes:
  • Using legacy property names in output
  • Incorrectly evaluating age condition
  • Missing one of the output properties
4. A developer wrote this anti-corruption layer code snippet but it causes errors when legacy data is missing some fields:
function translateOrder(legacyOrder) {
  return {
    id: legacyOrder.orderId,
    total: legacyOrder.amount.value,
    status: legacyOrder.status.toUpperCase()
  }
}
What is the main issue and how to fix it?
medium
A. The function should return legacyOrder directly without changes
B. The code assumes nested fields exist; add checks to handle missing or undefined fields
C. Use lowercase for status instead of toUpperCase()
D. Remove the id field to avoid errors

Solution

  1. Step 1: Identify the error cause

    The code accesses nested fields like legacyOrder.amount.value without checking if amount exists, causing errors if missing.
  2. Step 2: Fix by adding safety checks

    Use conditional checks or optional chaining to safely access nested fields and avoid runtime errors.
  3. Final Answer:

    The code assumes nested fields exist; add checks to handle missing or undefined fields -> Option B
  4. Quick Check:

    Missing field checks cause errors = add safety checks [OK]
Hint: Always check nested fields exist before accessing [OK]
Common Mistakes:
  • Ignoring null or undefined nested objects
  • Returning legacy data without translation
  • Changing case without reason
  • Removing necessary fields
5. You need to integrate a legacy billing system with your new microservice. The legacy system uses different currency codes and date formats. How should you design the anti-corruption layer to handle this integration effectively?
hard
A. Build a translation layer that converts legacy currency codes to standard ISO codes and normalizes date formats before passing data to the new service
B. Modify the legacy system to use the new system's currency codes and date formats directly
C. Ignore currency and date differences and pass data as-is to the new service
D. Store all legacy data in the new system without any translation

Solution

  1. Step 1: Identify integration challenges

    Legacy system uses different currency codes and date formats, which can cause data misinterpretation.
  2. Step 2: Design translation in anti-corruption layer

    Create a layer that converts legacy currency codes to standard ISO codes and normalizes date formats to the new system's expected format.
  3. Final Answer:

    Build a translation layer that converts legacy currency codes to standard ISO codes and normalizes date formats before passing data to the new service -> Option A
  4. Quick Check:

    Translate legacy formats to standard before integration [OK]
Hint: Translate legacy formats to standard before integration [OK]
Common Mistakes:
  • Trying to change legacy system directly
  • Passing data without translation
  • Storing legacy data without normalization