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:
Ilkka Seppälä authored and GitHub committed 2025-03-29 19:34:27 +02:00
1 parent 371439aeaa
commit 0ca162a55c
1863 files changed
+14403 -17632

No files matched your search

+2 -2
View File
@@ -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;
@@ -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));
}
}
@@ -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);
}
}
@@ -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
@@ -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> {}
@@ -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 {
@@ -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;
}
@@ -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);
}
@@ -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> {}
@@ -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
@@ -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));
}
}