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ä
2025-03-29 19:34:27 +02:00
committed by GitHub
parent 371439aeaa
commit 0ca162a55c
1863 changed files with 14403 additions and 17632 deletions
@@ -39,16 +39,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Main entry point for the Monolithic E-commerce application.
* ------------------------------------------------------------------------
* Monolithic architecture is a software design pattern where all components
* of the application (presentation, business logic, and data access layers)
* are part of a single unified codebase and deployable unit.
* ------------------------------------------------------------------------
* This example implements a monolithic architecture by integrating
* user management, product management, and order placement within
* the same application, sharing common resources and a single database.
* ------------------------------------------------------------------------ Monolithic architecture
* is a software design pattern where all components of the application (presentation, business
* logic, and data access layers) are part of a single unified codebase and deployable unit.
* ------------------------------------------------------------------------ This example implements
* a monolithic architecture by integrating user management, product management, and order placement
* within the same application, sharing common resources and a single database.
*/
@SpringBootApplication
public class EcommerceApp implements CommandLineRunner {
@@ -56,18 +53,19 @@ public class EcommerceApp implements CommandLineRunner {
private final UserController userService;
private final ProductController productService;
private final OrderController orderService;
/**
* Initilizing controllers as services.
* */
public EcommerceApp(UserController userService, ProductController productService, OrderController orderService) {
/** Initilizing controllers as services. */
public EcommerceApp(
UserController userService, ProductController productService, OrderController orderService) {
this.userService = userService;
this.productService = productService;
this.orderService = orderService;
}
/**
* The main entry point for the Monolithic E-commerce application.
* Initializes the Spring Boot application and starts the embedded server.
*/
* The main entry point for the Monolithic E-commerce application. Initializes the Spring Boot
* application and starts the embedded server.
*/
public static void main(String... args) {
SpringApplication.run(EcommerceApp.class, args);
}
@@ -100,9 +98,8 @@ public class EcommerceApp implements CommandLineRunner {
}
}
}
/**
* Handles User Registration through user CLI inputs.
* */
/** Handles User Registration through user CLI inputs. */
protected void registerUser(Scanner scanner) {
log.info("Enter user details:");
log.info("Name: ");
@@ -116,9 +113,8 @@ public class EcommerceApp implements CommandLineRunner {
userService.registerUser(user);
log.info("User registered successfully!");
}
/**
* Handles the addition of products.
* */
/** Handles the addition of products. */
protected void addProduct(Scanner scanner) {
log.info("Enter product details:");
log.info("Name: ");
@@ -134,9 +130,8 @@ public class EcommerceApp implements CommandLineRunner {
productService.addProduct(product);
log.info("Product added successfully!");
}
/**
* Handles Order Placement through user CLI inputs.
*/
/** Handles Order Placement through user CLI inputs. */
protected void placeOrder(Scanner scanner) {
log.info("Enter order details:");
log.info("User ID: ");
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
package com.iluwatar.monolithic.controller;
import com.iluwatar.monolithic.exceptions.InsufficientStockException;
import com.iluwatar.monolithic.exceptions.NonExistentProductException;
import com.iluwatar.monolithic.exceptions.NonExistentUserException;
@@ -33,30 +34,39 @@ import com.iluwatar.monolithic.repository.OrderRepository;
import com.iluwatar.monolithic.repository.ProductRepository;
import com.iluwatar.monolithic.repository.UserRepository;
import org.springframework.stereotype.Service;
/**
* OrderController is a controller class for managing Order operations.
* */
/** OrderController is a controller class for managing Order operations. */
@Service
public class OrderController {
private final OrderRepository orderRepository;
private final UserRepository userRepository;
private final ProductRepository productRepository;
/**
* This function handles the initializing of the controller.
* */
public OrderController(OrderRepository orderRepository, UserRepository userRepository, ProductRepository productRepository) {
/** This function handles the initializing of the controller. */
public OrderController(
OrderRepository orderRepository,
UserRepository userRepository,
ProductRepository productRepository) {
this.orderRepository = orderRepository;
this.userRepository = userRepository;
this.productRepository = productRepository;
}
/**
* This function handles placing orders with all of its cases.
* */
public Order placeOrder(Long userId, Long productId, Integer quantity) {
final User user = userRepository.findById(userId).orElseThrow(() -> new NonExistentUserException("User with ID " + userId + " not found"));
final Product product = productRepository.findById(productId).orElseThrow(() -> new NonExistentProductException("Product with ID " + productId + " not found"));
/** This function handles placing orders with all of its cases. */
public Order placeOrder(Long userId, Long productId, Integer quantity) {
final User user =
userRepository
.findById(userId)
.orElseThrow(
() -> new NonExistentUserException("User with ID " + userId + " not found"));
final Product product =
productRepository
.findById(productId)
.orElseThrow(
() ->
new NonExistentProductException("Product with ID " + productId + " not found"));
if (product.getStock() < quantity) {
throw new InsufficientStockException("Not enough stock for product " + productId);
}
@@ -29,29 +29,22 @@ import com.iluwatar.monolithic.repository.ProductRepository;
import java.util.List;
import org.springframework.stereotype.Service;
/**
* ProductCon is a controller class for managing Product operations.
* */
/** ProductCon is a controller class for managing Product operations. */
@Service
public class ProductController {
private final ProductRepository productRepository;
/**
* Linking Controller to DB.
* */
/** Linking Controller to DB. */
public ProductController(ProductRepository productRepository) {
this.productRepository = productRepository;
}
/**
* Adds a product to the DB.
* */
/** Adds a product to the DB. */
public Product addProduct(Product product) {
return productRepository.save(product);
}
/**
* Returns all relevant Product.
* */
/** Returns all relevant Product. */
public List<Product> getAllProducts() {
return productRepository.findAll();
}
@@ -27,21 +27,18 @@ package com.iluwatar.monolithic.controller;
import com.iluwatar.monolithic.model.User;
import com.iluwatar.monolithic.repository.UserRepository;
import org.springframework.stereotype.Service;
/**
* UserController is a controller class for managing user operations.
*/
/** UserController is a controller class for managing user operations. */
@Service
public class UserController {
private final UserRepository userRepository;
/**
* Linking Controller to DB.
*/
/** Linking Controller to DB. */
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* Adds a user to the DB.
*/
/** Adds a user to the DB. */
public User registerUser(User user) {
return userRepository.save(user);
}
@@ -25,15 +25,14 @@
package com.iluwatar.monolithic.exceptions;
import java.io.Serial;
/**
* Custom Exception class for enhanced readability.
* */
/** Custom Exception class for enhanced readability. */
public class InsufficientStockException extends RuntimeException {
@Serial
private static final long serialVersionUID = 1005208208127745099L;
@Serial private static final long serialVersionUID = 1005208208127745099L;
/**
* Exception Constructor that is readable through code and provides the message inputted into it.
* */
*/
public InsufficientStockException(String message) {
super(message);
}
@@ -25,15 +25,14 @@
package com.iluwatar.monolithic.exceptions;
import java.io.Serial;
/**
* Custom Exception class for enhanced readability.
* */
/** Custom Exception class for enhanced readability. */
public class NonExistentProductException extends RuntimeException {
@Serial
private static final long serialVersionUID = -593425162052345565L;
@Serial private static final long serialVersionUID = -593425162052345565L;
/**
* Exception Constructor that is readable through code and provides the message inputted into it.
* */
*/
public NonExistentProductException(String msg) {
super(msg);
}
@@ -25,15 +25,14 @@
package com.iluwatar.monolithic.exceptions;
import java.io.Serial;
/**
* Custom Exception class for enhanced readability.
* */
/** Custom Exception class for enhanced readability. */
public class NonExistentUserException extends RuntimeException {
@Serial
private static final long serialVersionUID = -7660909426227843633L;
@Serial private static final long serialVersionUID = -7660909426227843633L;
/**
* Exception Constructor that is readable through code and provides the message inputted into it.
* */
*/
public NonExistentUserException(String msg) {
super(msg);
}
@@ -33,9 +33,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Represents a Database in which Order are stored.
*/
/** Represents a Database in which Order are stored. */
@Entity
@Data
@NoArgsConstructor
@@ -45,11 +43,9 @@ public class Order {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private User user;
@ManyToOne private User user;
@ManyToOne
private Product product;
@ManyToOne private Product product;
private Integer quantity;
@@ -32,9 +32,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Represents a database of products.
*/
/** Represents a database of products. */
@Entity
@Data
@NoArgsConstructor
@@ -52,4 +50,3 @@ public class Product {
private Integer stock;
}
@@ -33,9 +33,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Represents a Product entity for the database.
*/
/** Represents a Product entity for the database. */
@Entity
@Data
@NoArgsConstructor
@@ -26,8 +26,6 @@ package com.iluwatar.monolithic.repository;
import com.iluwatar.monolithic.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* This interface allows JpaRepository to generate queries for the required tables.
*/
public interface OrderRepository extends JpaRepository<Order, Long> {
}
/** This interface allows JpaRepository to generate queries for the required tables. */
public interface OrderRepository extends JpaRepository<Order, Long> {}
@@ -26,8 +26,6 @@ package com.iluwatar.monolithic.repository;
import com.iluwatar.monolithic.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* This interface allows JpaRepository to generate queries for the required tables.
*/
public interface ProductRepository extends JpaRepository<Product, Long> {
}
/** This interface allows JpaRepository to generate queries for the required tables. */
public interface ProductRepository extends JpaRepository<Product, Long> {}
@@ -26,12 +26,12 @@ package com.iluwatar.monolithic.repository;
import com.iluwatar.monolithic.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* This interface allows JpaRepository to generate queries for the required tables.
*/
/** This interface allows JpaRepository to generate queries for the required tables. */
public interface UserRepository extends JpaRepository<User, Long> {
/**
* Utilizes JpaRepository functionalities to generate a function which looks up in the User table using emails.
* */
* Utilizes JpaRepository functionalities to generate a function which looks up in the User table
* using emails.
*/
User findByEmail(String email);
}
}
@@ -24,6 +24,9 @@
*/
package com.iluwatar.monolithic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import com.iluwatar.monolithic.controller.OrderController;
import com.iluwatar.monolithic.controller.ProductController;
import com.iluwatar.monolithic.controller.UserController;
@@ -36,33 +39,25 @@ import com.iluwatar.monolithic.model.User;
import com.iluwatar.monolithic.repository.OrderRepository;
import com.iluwatar.monolithic.repository.ProductRepository;
import com.iluwatar.monolithic.repository.UserRepository;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Optional;
import java.util.Scanner;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.Scanner;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class MonolithicAppTest {
@Mock
private UserController userService;
@Mock private UserController userService;
@Mock
private ProductController productService;
@Mock private ProductController productService;
@Mock
private OrderController orderService;
@Mock private OrderController orderService;
private EcommerceApp ecommerceApp;
@@ -96,16 +91,20 @@ class MonolithicAppTest {
when(mockUserRepository.findById(1L)).thenReturn(Optional.empty());
OrderController orderCon = new OrderController(mockOrderRepo, mockUserRepository, mockProductRepository);
OrderController orderCon =
new OrderController(mockOrderRepo, mockUserRepository, mockProductRepository);
Exception exception = assertThrows(NonExistentUserException.class, () -> {
orderCon.placeOrder(1L, 1L, 5);
});
Exception exception =
assertThrows(
NonExistentUserException.class,
() -> {
orderCon.placeOrder(1L, 1L, 5);
});
assertEquals("User with ID 1 not found", exception.getMessage());
}
@Test
@Test
void testPlaceOrderProductNotFound() {
UserRepository mockUserRepository = mock(UserRepository.class);
ProductRepository mockProductRepository = mock(ProductRepository.class);
@@ -116,24 +115,27 @@ class MonolithicAppTest {
when(mockProductRepository.findById(1L)).thenReturn(Optional.empty());
OrderController orderCon = new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
OrderController orderCon =
new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
Exception exception = assertThrows(NonExistentProductException.class, () -> {
orderCon.placeOrder(1L, 1L, 5);
});
Exception exception =
assertThrows(
NonExistentProductException.class,
() -> {
orderCon.placeOrder(1L, 1L, 5);
});
assertEquals("Product with ID 1 not found", exception.getMessage());
}
@Test
void testOrderConstructor(){
void testOrderConstructor() {
OrderRepository mockOrderRepository = mock(OrderRepository.class);
UserRepository mockUserRepository = mock(UserRepository.class);
ProductRepository mockProductRepository = mock(ProductRepository.class);
OrderController orderCon = new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
OrderController orderCon =
new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
assertNotNull(orderCon);
}
@@ -169,13 +171,15 @@ class MonolithicAppTest {
System.setIn(new ByteArrayInputStream(simulatedInput.getBytes(StandardCharsets.UTF_8)));
doThrow(new RuntimeException("Product out of stock"))
.when(orderService).placeOrder(anyLong(), anyLong(), anyInt());
.when(orderService)
.placeOrder(anyLong(), anyLong(), anyInt());
ecommerceApp.placeOrder(new Scanner(System.in, StandardCharsets.UTF_8));
verify(orderService, times(1)).placeOrder(anyLong(), anyLong(), anyInt());
assertTrue(outputStream.toString().contains("Error placing order: Product out of stock"));
}
@Test
void testPlaceOrderInsufficientStock() {
UserRepository mockUserRepository = mock(UserRepository.class);
@@ -184,16 +188,22 @@ class MonolithicAppTest {
User mockUser = new User(1L, "John Doe", "john@example.com", "password123");
when(mockUserRepository.findById(1L)).thenReturn(Optional.of(mockUser));
Product mockProduct = new Product(1L, "Laptop", "High-end gaming laptop", 1500.00, 2); // Only 2 in stock
Product mockProduct =
new Product(1L, "Laptop", "High-end gaming laptop", 1500.00, 2); // Only 2 in stock
when(mockProductRepository.findById(1L)).thenReturn(Optional.of(mockProduct));
OrderController orderCon = new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
OrderController orderCon =
new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository);
Exception exception = assertThrows(InsufficientStockException.class, () -> {
orderCon.placeOrder(1L, 1L, 5);
});
Exception exception =
assertThrows(
InsufficientStockException.class,
() -> {
orderCon.placeOrder(1L, 1L, 5);
});
assertEquals("Not enough stock for product 1", exception.getMessage());
}
}
@Test
void testProductConAddProduct() {
ProductRepository mockProductRepository = mock(ProductRepository.class);
@@ -217,7 +227,8 @@ class MonolithicAppTest {
@Test
void testRun() {
String simulatedInput = """
String simulatedInput =
"""
1
John Doe
john@example.com
@@ -232,15 +243,24 @@ class MonolithicAppTest {
1
2
4
"""; // Exit
"""; // Exit
System.setIn(new ByteArrayInputStream(simulatedInput.getBytes(StandardCharsets.UTF_8)));
ByteArrayOutputStream outputTest = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputTest, true, StandardCharsets.UTF_8));
when(userService.registerUser(any(User.class))).thenReturn(new User(1L, "John Doe", "john@example.com", "password123"));
when(productService.addProduct(any(Product.class))).thenReturn(new Product(1L, "Laptop", "Gaming Laptop", 1200.50, 10));
when(orderService.placeOrder(anyLong(), anyLong(), anyInt())).thenReturn(new Order(1L, new User(1L, "John Doe", "john@example.com","password123" ), new Product(1L, "Laptop", "Gaming Laptop", 1200.50, 10), 5, 6002.50));
when(userService.registerUser(any(User.class)))
.thenReturn(new User(1L, "John Doe", "john@example.com", "password123"));
when(productService.addProduct(any(Product.class)))
.thenReturn(new Product(1L, "Laptop", "Gaming Laptop", 1200.50, 10));
when(orderService.placeOrder(anyLong(), anyLong(), anyInt()))
.thenReturn(
new Order(
1L,
new User(1L, "John Doe", "john@example.com", "password123"),
new Product(1L, "Laptop", "Gaming Laptop", 1200.50, 10),
5,
6002.50));
ecommerceApp.run();
@@ -255,9 +275,5 @@ class MonolithicAppTest {
assertTrue(output.contains("Add Product"));
assertTrue(output.contains("Place Order"));
assertTrue(output.contains("Exiting the application. Goodbye!"));
}
}
}