diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md new file mode 100644 index 000000000..78650bc0c --- /dev/null +++ b/anti-corruption-layer/README.md @@ -0,0 +1,160 @@ +--- +title: Anti-corruption layer +category: Architectural +language: en +tag: + - Cloud distributed + - Decoupling + - Microservices +--- + +## 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. + +## 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. + +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. + +### 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. + +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; + private String customer; + private String item; + private String qty; + private String price; +} +``` +#### Domain model of the `Modern` system +```java +public class ModernOrder { + private String id; + private Customer customer; + + private Shipment shipment; + + private String extra; +} +public class Customer { + private String address; +} +public class Shipment { + private String item; + private String qty; + private String price; +} +``` +#### Anti-corruption layer +```java +public class AntiCorruptionLayer { + + @Autowired + private ModernShop modernShop; + + @Autowired + private LegacyShop legacyShop; + + public Optional findOrderInModernSystem(String id) { + return modernShop.findOrder(id).map(o -> /* map to legacyOrder*/); + } + + public Optional findOrderInLegacySystem(String id) { + return legacyShop.findOrder(id).map(o -> /* map to modernOrder*/); + } + +} +``` +#### 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. +```java +public class LegacyShop { + @Autowired + private AntiCorruptionLayer acl; + + public void placeOrder(LegacyOrder legacyOrder) throws ShopException { + + String id = legacyOrder.getId(); + + Optional orderInModernSystem = acl.findOrderInModernSystem(id); + + if (orderInModernSystem.isPresent()) { + // order is already in the modern system + } else { + // place order in the current system + } + } +} +``` + +### 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. + +## 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) + +## Credits + +* [Domain-Driven Design. Eric Evans](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215) \ No newline at end of file diff --git a/anti-corruption-layer/etc/anti-corruption-layer.urm.puml b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml new file mode 100644 index 000000000..6b1d1cdb9 --- /dev/null +++ b/anti-corruption-layer/etc/anti-corruption-layer.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.corruption { +class LegacyShop { + private Store store; + private AntiCorruptionLayer acl; +} + +class ModernShop { + private Store store; + private AntiCorruptionLayer acl; +} + +class AntiCorruptionLayer{ + private LegacyShop legacyShop; + private ModernShop modernShop; + + + } +LegacyShop ---> "findOrderInModernSystem" AntiCorruptionLayer +ModernShop ---> "findOrderInLegacySystem" AntiCorruptionLayer +AntiCorruptionLayer ..|> ModernShop +AntiCorruptionLayer ..|> LegacyShop +} + +@enduml \ No newline at end of file diff --git a/anti-corruption-layer/pom.xml b/anti-corruption-layer/pom.xml new file mode 100644 index 000000000..711e006b7 --- /dev/null +++ b/anti-corruption-layer/pom.xml @@ -0,0 +1,72 @@ + + + + + java-design-patterns + com.iluwatar + 1.26.0-SNAPSHOT + + 4.0.0 + anti-corruption-layer + jar + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.corruption.App + + + + + + + + + diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java new file mode 100644 index 000000000..3f53c54c0 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java @@ -0,0 +1,42 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2023 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.corruption; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 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. + */ +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java new file mode 100644 index 000000000..87b7f7d00 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java @@ -0,0 +1,28 @@ +/** + * 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. + * + *

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. + */ +package com.iluwatar.corruption; diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java new file mode 100644 index 000000000..9f693cef8 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java @@ -0,0 +1,44 @@ +package com.iluwatar.corruption.system; + +import com.iluwatar.corruption.system.legacy.LegacyShop; +import com.iluwatar.corruption.system.modern.Customer; +import com.iluwatar.corruption.system.modern.ModernOrder; +import com.iluwatar.corruption.system.modern.Shipment; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * The class represents an anti-corruption layer. + * The main purpose of the class is to provide a layer between the modern and legacy systems. + * The class is responsible for converting the data from one system to another + * decoupling the systems to each other + * + *

It allows using one system a domain model of the other system + * without changing the domain model of the system. + */ +@Service +public class AntiCorruptionLayer { + + @Autowired + private LegacyShop legacyShop; + + + /** + * The method converts the order from the legacy system to the modern system. + * @param id the id of the order + * @return the order in the modern system + */ + public Optional findOrderInLegacySystem(String id) { + + return legacyShop.findOrder(id).map(o -> + new ModernOrder( + o.getId(), + new Customer(o.getCustomer()), + new Shipment(o.getItem(), o.getQty(), o.getPrice()), + "" + ) + ); + } + +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java new file mode 100644 index 000000000..f7424980d --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java @@ -0,0 +1,25 @@ +package com.iluwatar.corruption.system; + +import java.util.HashMap; +import java.util.Optional; + +/** + * The class represents a data store for the modern system. + * @param the type of the value stored in the data store + */ +public abstract class DataStore { + private final HashMap inner; + + public DataStore() { + inner = new HashMap<>(); + } + + public Optional get(String key) { + return Optional.ofNullable(inner.get(key)); + } + + public Optional put(String key, V value) { + return Optional.ofNullable(inner.put(key, value)); + + } +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java new file mode 100644 index 000000000..b545ee610 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java @@ -0,0 +1,25 @@ +package com.iluwatar.corruption.system; + +/** + * The class represents an general exception for the shop. + */ +public class ShopException extends Exception { + public ShopException(String message) { + super(message); + } + + /** + * Throws an exception that the order is already placed but has an incorrect data. + * + * @param lhs the incoming order + * @param rhs the existing order + * @return the exception + * @throws ShopException the exception + */ + public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException { + throw new ShopException("The order is already placed but has an incorrect data:\n" + + "Incoming order: " + lhs + "\n" + + "Existing order: " + rhs); + } + +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java new file mode 100644 index 000000000..a114302eb --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java @@ -0,0 +1,19 @@ +package com.iluwatar.corruption.system.legacy; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The class represents an order in the legacy system. + * The class is used by the legacy system to store the data. + */ +@Data +@AllArgsConstructor +public class LegacyOrder { + private String id; + private String customer; + + private String item; + private int qty; + private int price; +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java new file mode 100644 index 000000000..fe5a88be8 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java @@ -0,0 +1,28 @@ +package com.iluwatar.corruption.system.legacy; + +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * The class represents a legacy shop system. The main purpose is to place the order from the + * customers. + */ +@Service +public class LegacyShop { + @Autowired private LegacyStore store; + + /** + * Places the order in the legacy system. If the order is already present in the modern system, + * then the order is placed only if the data is the same. If the order is not present in the + * modern system, then the order is placed in the legacy system. + */ + public void placeOrder(LegacyOrder legacyOrder) { + store.put(legacyOrder.getId(), legacyOrder); + } + + /** Finds the order in the legacy system. */ + public Optional findOrder(String orderId) { + return store.get(orderId); + } +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java new file mode 100644 index 000000000..85d0f64d6 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java @@ -0,0 +1,13 @@ +package com.iluwatar.corruption.system.legacy; + +import com.iluwatar.corruption.system.DataStore; +import org.springframework.stereotype.Service; + +/** + * The class represents a data store for the legacy system. + * The class is used by the legacy system to store the data. + */ +@Service +public class LegacyStore extends DataStore { +} + diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java new file mode 100644 index 000000000..d76792c48 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java @@ -0,0 +1,13 @@ +package com.iluwatar.corruption.system.modern; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The class represents a customer in the modern system. + */ +@Data +@AllArgsConstructor +public class Customer { + private String address; +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java new file mode 100644 index 000000000..6a561f361 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java @@ -0,0 +1,20 @@ +package com.iluwatar.corruption.system.modern; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The class represents an order in the modern system. + */ +@Data +@AllArgsConstructor +public class ModernOrder { + private String id; + private Customer customer; + + private Shipment shipment; + + private String extra; + + +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java new file mode 100644 index 000000000..9cb589868 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java @@ -0,0 +1,47 @@ +package com.iluwatar.corruption.system.modern; + +import com.iluwatar.corruption.system.AntiCorruptionLayer; +import com.iluwatar.corruption.system.ShopException; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * The class represents a modern shop system. + * The main purpose of the class is to place orders and find orders. + */ +@Service +public class ModernShop { + @Autowired + private ModernStore store; + + @Autowired + private AntiCorruptionLayer acl; + + /** + * Places the order in the modern system. + * If the order is already present in the legacy system, then no need to place it again. + */ + public void placeOrder(ModernOrder order) throws ShopException { + + String id = order.getId(); + // check if the order is already present in the legacy system + Optional orderInObsoleteSystem = acl.findOrderInLegacySystem(id); + + if (orderInObsoleteSystem.isPresent()) { + var legacyOrder = orderInObsoleteSystem.get(); + if (!order.equals(legacyOrder)) { + throw ShopException.throwIncorrectData(legacyOrder.toString(), order.toString()); + } + } else { + store.put(id, order); + } + } + + /** + * Finds the order in the modern system. + */ + public Optional findOrder(String orderId) { + return store.get(orderId); + } +} diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java new file mode 100644 index 000000000..8ccc00a95 --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java @@ -0,0 +1,12 @@ +package com.iluwatar.corruption.system.modern; + +import com.iluwatar.corruption.system.DataStore; +import org.springframework.stereotype.Service; + +/** + * The class represents a data store for the modern system. + */ +@Service +public class ModernStore extends DataStore { +} + diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java new file mode 100644 index 000000000..d4570f47a --- /dev/null +++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java @@ -0,0 +1,16 @@ +package com.iluwatar.corruption.system.modern; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * The class represents a shipment in the modern system. + * The class is used by the modern system to store the data. + */ +@Data +@AllArgsConstructor +public class Shipment { + private String item; + private int qty; + private int price; +} diff --git a/anti-corruption-layer/src/main/resources/application.properties b/anti-corruption-layer/src/main/resources/application.properties new file mode 100644 index 000000000..e69de29bb diff --git a/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java new file mode 100644 index 000000000..0883c8511 --- /dev/null +++ b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java @@ -0,0 +1,87 @@ +package com.iluwatar.corruption.system; + +import com.iluwatar.corruption.system.legacy.LegacyOrder; +import com.iluwatar.corruption.system.legacy.LegacyShop; +import com.iluwatar.corruption.system.modern.Customer; +import com.iluwatar.corruption.system.modern.ModernOrder; +import com.iluwatar.corruption.system.modern.ModernShop; +import com.iluwatar.corruption.system.modern.Shipment; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class AntiCorruptionLayerTest { + + @Autowired + private LegacyShop legacyShop; + + @Autowired + private ModernShop modernShop; + + + /** + * Test the anti-corruption layer. + * Main intention is to demonstrate how the anti-corruption layer works. + *

+ * The 2 shops (modern and legacy) should operate independently and in the same time synchronize the data. + * To avoid corrupting the domain models of the 2 shops, we use an anti-corruption layer + * that transforms one model to another under the hood. + * + */ + @Test + public void antiCorruptionLayerTest() throws ShopException { + + // a new order comes to the legacy shop. + LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1); + // place the order in the legacy shop. + legacyShop.placeOrder(legacyOrder); + // the order is placed as usual since there is no other orders with the id in the both systems. + Optional legacyOrderWithIdOne = legacyShop.findOrder("1"); + assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne); + + // a new order (or maybe just the same order) appears in the modern shop. + ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), ""); + + // the system places it, but it checks if there is an order with the same id in the legacy shop. + modernShop.placeOrder(modernOrder); + + Optional modernOrderWithIdOne = modernShop.findOrder("1"); + // there is no new order placed since there is already an order with the same id in the legacy shop. + assertTrue(modernOrderWithIdOne.isEmpty()); + + } + /** + * Test the anti-corruption layer. + * Main intention is to demonstrate how the anti-corruption layer works. + *

+ * This test tests the anti-corruption layer from the rule the orders should be the same in the both systems. + * + */ + @Test(expected = ShopException.class) + public void antiCorruptionLayerWithExTest() throws ShopException { + + // a new order comes to the legacy shop. + LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1); + // place the order in the legacy shop. + legacyShop.placeOrder(legacyOrder); + // the order is placed as usual since there is no other orders with the id in the both systems. + Optional legacyOrderWithIdOne = legacyShop.findOrder("1"); + assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne); + + // a new order but with the same id and different data appears in the modern shop + ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), ""); + + // the system rejects the order since there are 2 orders with contradiction there. + modernShop.placeOrder(modernOrder); + + + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3305d23a4..32cbbaa15 100644 --- a/pom.xml +++ b/pom.xml @@ -209,6 +209,7 @@ optimistic-offline-lock crtp log-aggregation + anti-corruption-layer health-check notification single-table-inheritance