docs: update Anti-corruption layer docs

This commit is contained in:
Ilkka Seppälä
2024-03-24 15:10:47 +02:00
parent 965af11cb0
commit cc1854f0a0
2 changed files with 65 additions and 55 deletions
+64 -54
View File
@@ -1,63 +1,51 @@
---
title: Anti-corruption layer
category: Architectural
category: Integration
language: en
tag:
- Cloud distributed
- Architecture
- Decoupling
- Microservices
- Isolation
---
## Also known as
* ACL
* Interface layer
* Translation layer
## Intent
Implement a façade or adapter layer between different subsystems that don't share the same semantics.
This layer translates requests that one subsystem makes to the other subsystem.
Use this pattern to ensure that an application's design is not limited by dependencies on outside subsystems.
This pattern was first described by Eric Evans in Domain-Driven Design.
Implement a façade or adapter layer between different subsystems that don't share the same semantics. It translates between different data formats and systems, ensuring that the integration between systems does not lead to corruption of business logic or data integrity.
## Explanation
### Context and problem
Most applications rely on other systems for some data or functionality.
For example, when a legacy application is migrated to a modern system,
it may still need existing legacy resources.
New features must be able to call the legacy system.
This is especially true of gradual migrations,
where different features of a larger application are moved to a modern system over time.
Often these legacy systems suffer from quality issues such as convoluted data schemas or obsolete APIs.
The features and technologies used in legacy systems can vary widely from more modern systems.
To interoperate with the legacy system, the new application may need to support outdated infrastructure, protocols, data models, APIs,
or other features that you wouldn't otherwise put into a modern application.
Most applications rely on other systems for some data or functionality. For example, when a legacy application is migrated to a modern system, it may still need existing legacy resources. New features must be able to call the legacy system. This is especially true of gradual migrations, where different features of a larger application are moved to a modern system over time.
Maintaining access between new and legacy systems can force the new system to adhere to at least some of the legacy system's APIs or other semantics.
When these legacy features have quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern application.
Similar issues can arise with any external system that your development team doesn't control, not just legacy systems.
Often these legacy systems suffer from quality issues such as convoluted data schemas or obsolete APIs. The features and technologies used in legacy systems can vary widely from more modern systems. To interoperate with the legacy system, the new application may need to support outdated infrastructure, protocols, data models, APIs, or other features that you wouldn't otherwise put into a modern application.
Maintaining access between new and legacy systems can force the new system to adhere to at least some of the legacy system's APIs or other semantics. When these legacy features have quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern application. Similar issues can arise with any external system that your development team doesn't control, not just legacy systems.
### Solution
Isolate the different subsystems by placing an anti-corruption layer between them.
This layer translates communications between the two systems, allowing one system to remain unchanged
while the other can avoid compromising its design and technological approach.
Isolate the different subsystems by placing an anti-corruption layer between them. This layer translates communications between the two systems, allowing one system to remain unchanged while the other can avoid compromising its design and technological approach.
### Programmatic example
#### Introduction
The example shows why the anti-corruption layer is needed.
Here are 2 shop-ordering systems: `Legacy` and `Modern`. \
(
*It is important to state the separation is very conditional, and is drawn for learning purposes*.
*In reality the pattern does not depend on the so-called ageness but rather relies on the different domain models.*)
The aforementioned systems have different domain models and have to operate simultaneously.
Since they work independently the orders can come either from the `Legacy` or `Modern` system.
Therefore, the system that receives the legacyOrder needs to check if the legacyOrder is valid and not present in the other system.
Then it can place the legacyOrder in its own system.
Here are 2 shop-ordering systems: `Legacy` and `Modern`.
But for that, the system needs to know the domain model of the other system and to avoid that,
the anti-corruption layer(ACL) is introduced.
The ACL is a layer that translates the domain model of the `Legacy` system to the domain model of the `Modern` system and vice versa.
Also, it hides all other operations with the other system, uncoupling the systems.
The aforementioned systems have different domain models and have to operate simultaneously. Since they work independently the orders can come either from the `Legacy` or `Modern` system. Therefore, the system that receives the legacyOrder needs to check if the legacyOrder is valid and not present in the other system. Then it can place the legacyOrder in its own system.
But for that, the system needs to know the domain model of the other system and to avoid that, the anti-corruption layer(ACL) is introduced. The ACL is a layer that translates the domain model of the `Legacy` system to the domain model of the `Modern` system and vice versa. Also, it hides all other operations with the other system, uncoupling the systems.
#### Domain model of the `Legacy` system
```java
public class LegacyOrder {
private String id;
@@ -67,7 +55,9 @@ public class LegacyOrder {
private String price;
}
```
#### Domain model of the `Modern` system
```java
public class ModernOrder {
private String id;
@@ -86,7 +76,9 @@ public class Shipment {
private String price;
}
```
#### Anti-corruption layer
```java
public class AntiCorruptionLayer {
@@ -106,10 +98,11 @@ public class AntiCorruptionLayer {
}
```
#### The connection
Wherever the `Legacy` or `Modern` system needs to communicate with the counterpart
the ACL needs to be used to avoid corrupting the current domain model.
The example below shows how the `Legacy` system places an order with a validation from the `Modern` system.
Wherever the `Legacy` or `Modern` system needs to communicate with the counterpart the ACL needs to be used to avoid corrupting the current domain model. The example below shows how the `Legacy` system places an order with a validation from the `Modern` system.
```java
public class LegacyShop {
@Autowired
@@ -130,31 +123,48 @@ public class LegacyShop {
}
```
### Issues and considerations
- The anti-corruption layer may add latency to calls made between the two systems.
- The anti-corruption layer adds an additional service that must be managed and maintained.
- Consider how your anti-corruption layer will scale.
- Consider whether you need more than one anti-corruption layer. You may want to decompose functionality into multiple services using different technologies or languages, or there may be other reasons to partition the anti-corruption layer.
- Consider how the anti-corruption layer will be managed in relation with your other applications or services. How will it be integrated into your monitoring, release, and configuration processes?
- Make sure transaction and data consistency are maintained and can be monitored.
- Consider whether the anti-corruption layer needs to handle all communication between different subsystems, or just a subset of features.
- If the anti-corruption layer is part of an application migration strategy, consider whether it will be permanent, or will be retired after all legacy functionality has been migrated.
- This pattern is illustrated with distinct subsystems above, but can apply to other service architectures as well, such as when integrating legacy code together in a monolithic architecture.
## Applicability
Use this pattern when:
- A migration is planned to happen over multiple stages, but integration between new and legacy systems needs to be maintained.
- Two or more subsystems have different semantics, but still need to communicate.
This pattern may not be suitable if there are no significant semantic differences between new and legacy systems.
* A migration is planned to happen over multiple stages, but integration between new and legacy systems needs to be maintained
* Two or more subsystems have different semantics, but still need to communicate
* When integrating with legacy systems or external systems where direct integration might pollute the domain model of the new system
* In scenarios where different subsystems within a larger system use different data formats or structures
* When there is a need to ensure loose coupling between different subsystems or external services to facilitate easier maintenance and scalability
## Tutorials
* [Microsoft - Anti-Corruption Layer](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer)
* [Amazon - Anti-Corruption Layer](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html)
## Known Uses
* Microservices architectures where individual services must communicate without being tightly coupled to each others data schemas
* Enterprise systems integration, especially when integrating modern systems with legacy systems
* In bounded contexts within Domain-Driven Design (DDD) to maintain the integrity of a domain model when interacting with external systems or subsystems
## Consequences
Benefits:
* Protects the integrity of the domain model by providing a clear boundary
* Promotes loose coupling between systems, making the system more resilient to changes in external systems
* Facilitates cleaner and more maintainable code by isolating integration code from business logic
Trade-offs:
* Introduces additional complexity and potential performance overhead due to the translation process
* Requires extra effort in design and implementation to ensure the layer is effective without becoming a bottleneck
* Can lead to duplication of models if not carefully managed
## Related Patterns
* [Facade](https://java-design-patterns.com/patterns/facade/): The Anti-Corruption Layer can be seen as a specialized form of the Facade pattern that is used to isolate different subsystems
* [Adapter](https://java-design-patterns.com/patterns/adapter/): The Anti-Corruption Layer can be implemented using the Adapter pattern to translate between different data formats or structures
* [Gateway](https://java-design-patterns.com/patterns/gateway/): The Anti-Corruption Layer can be used as a Gateway to external systems to provide a unified interface
## Credits
* [Domain-Driven Design. Eric Evans](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215)
* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3vptcJz)
* [Implementing Domain-Driven Design](https://amzn.to/3ISOSRA)
@@ -1,7 +1,7 @@
package com.iluwatar.corruption.system;
/**
* The class represents an general exception for the shop.
* The class represents a general exception for the shop.
*/
public class ShopException extends Exception {
public ShopException(String message) {