mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-14 12:16:46 +00:00
deps: Refactor dependencies (#3224)
* remove spring dep move junit, logging, mockito under dep mgmt * upgrade anti-corruption-layer deps * async method invocation * balking, bloc * bridge to bytecode * caching * callback - cqrs * component - health check * hexagonal - metadata mapping * rest of the patterns * remove checkstyle, take spotless into use
This commit is contained in:
1 parent
371439aeaa
commit
0ca162a55c
1863 files changed
+14403
-17632
No files matched your search
@@ -45,8 +45,8 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -28,9 +28,8 @@ 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.
|
||||
* 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 {
|
||||
|
||||
@@ -23,30 +23,26 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
* 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;
|
||||
+16
-18
@@ -33,36 +33,34 @@ 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
|
||||
* 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
|
||||
*
|
||||
* <p>It allows using one system a domain model of the other system
|
||||
* without changing the domain model of the system.
|
||||
* <p>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;
|
||||
|
||||
@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<ModernOrder> 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()),
|
||||
""
|
||||
)
|
||||
);
|
||||
return legacyShop
|
||||
.findOrder(id)
|
||||
.map(
|
||||
o ->
|
||||
new ModernOrder(
|
||||
o.getId(),
|
||||
new Customer(o.getCustomer()),
|
||||
new Shipment(o.getItem(), o.getQty(), o.getPrice()),
|
||||
""));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The class represents a data store for the modern system.
|
||||
*
|
||||
* @param <V> the type of the value stored in the data store
|
||||
*/
|
||||
public abstract class DataStore<V> {
|
||||
@@ -44,6 +45,5 @@ public abstract class DataStore<V> {
|
||||
|
||||
public Optional<V> put(String key, V value) {
|
||||
return Optional.ofNullable(inner.put(key, value));
|
||||
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -24,9 +24,7 @@
|
||||
*/
|
||||
package com.iluwatar.corruption.system;
|
||||
|
||||
/**
|
||||
* The class represents a general exception for the shop.
|
||||
*/
|
||||
/** The class represents a general exception for the shop. */
|
||||
public class ShopException extends Exception {
|
||||
public ShopException(String message) {
|
||||
super(message);
|
||||
@@ -41,9 +39,12 @@ public class ShopException extends 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);
|
||||
throw new ShopException(
|
||||
"The order is already placed but has an incorrect data:\n"
|
||||
+ "Incoming order: "
|
||||
+ lhs
|
||||
+ "\n"
|
||||
+ "Existing order: "
|
||||
+ rhs);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -28,8 +28,8 @@ 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.
|
||||
* The class represents an order in the legacy system. The class is used by the legacy system to
|
||||
* store the data.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
|
||||
+3
-5
@@ -28,10 +28,8 @@ 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.
|
||||
* 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<LegacyOrder> {
|
||||
}
|
||||
|
||||
public class LegacyStore extends DataStore<LegacyOrder> {}
|
||||
+1
-3
@@ -27,9 +27,7 @@ package com.iluwatar.corruption.system.modern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* The class represents a customer in the modern system.
|
||||
*/
|
||||
/** The class represents a customer in the modern system. */
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Customer {
|
||||
|
||||
+1
-5
@@ -27,9 +27,7 @@ package com.iluwatar.corruption.system.modern;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* The class represents an order in the modern system.
|
||||
*/
|
||||
/** The class represents an order in the modern system. */
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ModernOrder {
|
||||
@@ -39,6 +37,4 @@ public class ModernOrder {
|
||||
private Shipment shipment;
|
||||
|
||||
private String extra;
|
||||
|
||||
|
||||
}
|
||||
+7
-11
@@ -31,20 +31,18 @@ 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.
|
||||
* 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 ModernStore store;
|
||||
|
||||
@Autowired
|
||||
private AntiCorruptionLayer acl;
|
||||
@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.
|
||||
* 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 {
|
||||
|
||||
@@ -62,9 +60,7 @@ public class ModernShop {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the order in the modern system.
|
||||
*/
|
||||
/** Finds the order in the modern system. */
|
||||
public Optional<ModernOrder> findOrder(String orderId) {
|
||||
return store.get(orderId);
|
||||
}
|
||||
|
||||
+2
-6
@@ -27,10 +27,6 @@ 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.
|
||||
*/
|
||||
/** The class represents a data store for the modern system. */
|
||||
@Service
|
||||
public class ModernStore extends DataStore<ModernOrder> {
|
||||
}
|
||||
|
||||
public class ModernStore extends DataStore<ModernOrder> {}
|
||||
+2
-2
@@ -28,8 +28,8 @@ 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.
|
||||
* The class represents a shipment in the modern system. The class is used by the modern system to
|
||||
* store the data.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
|
||||
+53
-68
@@ -24,88 +24,73 @@
|
||||
*/
|
||||
package com.iluwatar.corruption.system;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
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 java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest
|
||||
public class AntiCorruptionLayerTest {
|
||||
|
||||
@Autowired
|
||||
private LegacyShop legacyShop;
|
||||
@Autowired private LegacyShop legacyShop;
|
||||
|
||||
@Autowired
|
||||
private ModernShop modernShop;
|
||||
@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.
|
||||
*/
|
||||
@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<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
||||
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
||||
|
||||
/**
|
||||
* Test the anti-corruption layer.
|
||||
* Main intention is to demonstrate how the anti-corruption layer works.
|
||||
* <p>
|
||||
* 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 (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);
|
||||
|
||||
// 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<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
||||
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
||||
Optional<ModernOrder> 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());
|
||||
}
|
||||
|
||||
// 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<ModernOrder> 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.
|
||||
* <p>
|
||||
* 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<LegacyOrder> 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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Test the anti-corruption layer when a conflict occurs between systems. This test ensures that
|
||||
* an exception is thrown when conflicting orders are placed.
|
||||
*/
|
||||
@Test
|
||||
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<LegacyOrder> 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.
|
||||
assertThrows(ShopException.class, () -> modernShop.placeOrder(modernOrder));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user