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
+8
View File
@@ -34,6 +34,14 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>domain-model</artifactId>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
@@ -32,7 +32,6 @@ import javax.sql.DataSource;
import org.h2.jdbcx.JdbcDataSource;
import org.joda.money.Money;
/**
* Domain Model pattern is a more complex solution for organizing domain logic than Transaction
* Script and Table Module. It provides an object-oriented way of dealing with complicated logic.
@@ -42,10 +41,10 @@ import org.joda.money.Money;
* is that in Table Module a single class encapsulates all the domain logic for all records stored
* in table when in Domain Model every single class represents only one record in underlying table.
*
* <p>In this example, we will use the Domain Model pattern to implement buying of products
* by customers in a Shop. The main method will create a customer and a few products.
* Customer will do a few purchases, try to buy product which are too expensive for him,
* return product which he bought to return money.</p>
* <p>In this example, we will use the Domain Model pattern to implement buying of products by
* customers in a Shop. The main method will create a customer and a few products. Customer will do
* a few purchases, try to buy product which are too expensive for him, return product which he
* bought to return money.
*/
public class App {
@@ -80,11 +79,7 @@ public class App {
var customerDao = new CustomerDaoImpl(dataSource);
var tom =
Customer.builder()
.name("Tom")
.money(Money.of(USD, 30))
.customerDao(customerDao)
.build();
Customer.builder().name("Tom").money(Money.of(USD, 30)).customerDao(customerDao).build();
tom.save();
@@ -36,9 +36,8 @@ import lombok.extern.slf4j.Slf4j;
import org.joda.money.Money;
/**
* This class organizes domain logic of customer.
* A single instance of this class
* contains both the data and behavior of a single customer.
* This class organizes domain logic of customer. A single instance of this class contains both the
* data and behavior of a single customer.
*/
@Slf4j
@Getter
@@ -51,9 +50,7 @@ public class Customer {
@NonNull private String name;
@NonNull private Money money;
/**
* Save customer or update if customer already exist.
*/
/** Save customer or update if customer already exist. */
public void save() {
try {
Optional<Customer> customer = customerDao.findByName(name);
@@ -117,9 +114,7 @@ public class Customer {
}
}
/**
* Print customer's purchases.
*/
/** Print customer's purchases. */
public void showPurchases() {
Optional<String> purchasesToShow =
purchases.stream()
@@ -133,9 +128,7 @@ public class Customer {
}
}
/**
* Print customer's money balance.
*/
/** Print customer's money balance. */
public void showBalance() {
LOGGER.info(name + " balance: " + money);
}
@@ -27,9 +27,7 @@ package com.iluwatar.domainmodel;
import java.sql.SQLException;
import java.util.Optional;
/**
* DAO interface for customer transactions.
*/
/** DAO interface for customer transactions. */
public interface CustomerDao {
Optional<Customer> findByName(String name) throws SQLException;
@@ -32,9 +32,7 @@ import java.util.Optional;
import javax.sql.DataSource;
import org.joda.money.Money;
/**
* Implementations for database operations of Customer.
*/
/** Implementations for database operations of Customer. */
public class CustomerDaoImpl implements CustomerDao {
private final DataSource dataSource;
@@ -40,9 +40,8 @@ import lombok.extern.slf4j.Slf4j;
import org.joda.money.Money;
/**
* This class organizes domain logic of product.
* A single instance of this class
* contains both the data and behavior of a single product.
* This class organizes domain logic of product. A single instance of this class contains both the
* data and behavior of a single product.
*/
@Slf4j
@Getter
@@ -59,9 +58,7 @@ public class Product {
@NonNull private Money price;
@NonNull private LocalDate expirationDate;
/**
* Save product or update if product already exist.
*/
/** Save product or update if product already exist. */
public void save() {
try {
Optional<Product> product = productDao.findByName(name);
@@ -75,16 +72,14 @@ public class Product {
}
}
/**
* Calculate sale price of product with discount.
*/
/** Calculate sale price of product with discount. */
public Money getSalePrice() {
return price.minus(calculateDiscount());
}
private Money calculateDiscount() {
if (ChronoUnit.DAYS.between(LocalDate.now(), expirationDate)
< DAYS_UNTIL_EXPIRATION_WHEN_DISCOUNT_ACTIVE) {
< DAYS_UNTIL_EXPIRATION_WHEN_DISCOUNT_ACTIVE) {
return price.multipliedBy(DISCOUNT_RATE, RoundingMode.DOWN);
}
@@ -27,9 +27,7 @@ package com.iluwatar.domainmodel;
import java.sql.SQLException;
import java.util.Optional;
/**
* DAO interface for product transactions.
*/
/** DAO interface for product transactions. */
public interface ProductDao {
Optional<Product> findByName(String name) throws SQLException;
@@ -33,9 +33,7 @@ import java.util.Optional;
import javax.sql.DataSource;
import org.joda.money.Money;
/**
* Implementations for database transactions of Product.
*/
/** Implementations for database transactions of Product. */
public class ProductDaoImpl implements ProductDao {
private final DataSource dataSource;
@@ -24,10 +24,10 @@
*/
package com.iluwatar.domainmodel;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
/** Tests that Domain Model example runs without errors. */
final class AppTest {
@@ -35,5 +35,4 @@ final class AppTest {
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
@@ -24,18 +24,18 @@
*/
package com.iluwatar.domainmodel;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import javax.sql.DataSource;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.*;
class CustomerDaoImplTest {
@@ -62,7 +62,12 @@ class CustomerDaoImplTest {
// setup objects
customerDao = new CustomerDaoImpl(dataSource);
customer = Customer.builder().name("customer").money(Money.of(CurrencyUnit.USD,100.0)).customerDao(customerDao).build();
customer =
Customer.builder()
.name("customer")
.money(Money.of(CurrencyUnit.USD, 100.0))
.customerDao(customerDao)
.build();
product =
Product.builder()
@@ -24,89 +24,90 @@
*/
package com.iluwatar.domainmodel;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class CustomerTest {
private CustomerDao customerDao;
private Customer customer;
private Product product;
private CustomerDao customerDao;
private Customer customer;
private Product product;
@BeforeEach
void setUp() {
customerDao = mock(CustomerDao.class);
@BeforeEach
void setUp() {
customerDao = mock(CustomerDao.class);
customer = Customer.builder()
.name("customer")
.money(Money.of(CurrencyUnit.USD, 100.0))
.customerDao(customerDao)
.build();
customer =
Customer.builder()
.name("customer")
.money(Money.of(CurrencyUnit.USD, 100.0))
.customerDao(customerDao)
.build();
product = Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.now().plusDays(10))
.productDao(mock(ProductDao.class))
.build();
}
product =
Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.now().plusDays(10))
.productDao(mock(ProductDao.class))
.build();
}
@Test
void shouldSaveCustomer() throws SQLException {
when(customerDao.findByName("customer")).thenReturn(Optional.empty());
@Test
void shouldSaveCustomer() throws SQLException {
when(customerDao.findByName("customer")).thenReturn(Optional.empty());
customer.save();
customer.save();
verify(customerDao, times(1)).save(customer);
verify(customerDao, times(1)).save(customer);
when(customerDao.findByName("customer")).thenReturn(Optional.of(customer));
when(customerDao.findByName("customer")).thenReturn(Optional.of(customer));
customer.save();
customer.save();
verify(customerDao, times(1)).update(customer);
}
verify(customerDao, times(1)).update(customer);
}
@Test
void shouldAddProductToPurchases() {
product.setPrice(Money.of(USD, 200.0));
@Test
void shouldAddProductToPurchases() {
product.setPrice(Money.of(USD, 200.0));
customer.buyProduct(product);
customer.buyProduct(product);
assertEquals(customer.getPurchases(), new ArrayList<>());
assertEquals(customer.getMoney(), Money.of(USD,100));
assertEquals(customer.getPurchases(), new ArrayList<>());
assertEquals(customer.getMoney(), Money.of(USD, 100));
product.setPrice(Money.of(USD, 100.0));
product.setPrice(Money.of(USD, 100.0));
customer.buyProduct(product);
customer.buyProduct(product);
assertEquals(new ArrayList<>(Arrays.asList(product)), customer.getPurchases());
assertEquals(Money.zero(USD), customer.getMoney());
}
assertEquals(new ArrayList<>(Arrays.asList(product)), customer.getPurchases());
assertEquals(Money.zero(USD), customer.getMoney());
}
@Test
void shouldRemoveProductFromPurchases() {
customer.setPurchases(new ArrayList<>(Arrays.asList(product)));
@Test
void shouldRemoveProductFromPurchases() {
customer.setPurchases(new ArrayList<>(Arrays.asList(product)));
customer.returnProduct(product);
customer.returnProduct(product);
assertEquals(new ArrayList<>(), customer.getPurchases());
assertEquals(Money.of(USD, 200), customer.getMoney());
assertEquals(new ArrayList<>(), customer.getPurchases());
assertEquals(Money.of(USD, 200), customer.getMoney());
customer.returnProduct(product);
customer.returnProduct(product);
assertEquals(new ArrayList<>(), customer.getPurchases());
assertEquals(Money.of(USD, 200), customer.getMoney());
}
assertEquals(new ArrayList<>(), customer.getPurchases());
assertEquals(Money.of(USD, 200), customer.getMoney());
}
}
@@ -24,104 +24,104 @@
*/
package com.iluwatar.domainmodel;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import javax.sql.DataSource;
import org.joda.money.Money;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.*;
class ProductDaoImplTest {
public static final String INSERT_PRODUCT_SQL =
"insert into PRODUCTS values('product', 100, DATE '2021-06-27')";
public static final String SELECT_PRODUCTS_SQL =
"select name, price, expiration_date from PRODUCTS";
public static final String INSERT_PRODUCT_SQL =
"insert into PRODUCTS values('product', 100, DATE '2021-06-27')";
public static final String SELECT_PRODUCTS_SQL =
"select name, price, expiration_date from PRODUCTS";
private DataSource dataSource;
private ProductDao productDao;
private Product product;
private DataSource dataSource;
private ProductDao productDao;
private Product product;
@BeforeEach
void setUp() throws SQLException {
// create schema
dataSource = TestUtils.createDataSource();
@BeforeEach
void setUp() throws SQLException {
// create schema
dataSource = TestUtils.createDataSource();
TestUtils.deleteSchema(dataSource);
TestUtils.createSchema(dataSource);
TestUtils.deleteSchema(dataSource);
TestUtils.createSchema(dataSource);
// setup objects
productDao = new ProductDaoImpl(dataSource);
// setup objects
productDao = new ProductDaoImpl(dataSource);
product =
Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.parse("2021-06-27"))
.productDao(productDao)
.build();
product =
Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.parse("2021-06-27"))
.productDao(productDao)
.build();
}
@AfterEach
void tearDown() throws SQLException {
TestUtils.deleteSchema(dataSource);
}
@Test
void shouldFindProductByName() throws SQLException {
var product = productDao.findByName("product");
assertTrue(product.isEmpty());
TestUtils.executeSQL(INSERT_PRODUCT_SQL, dataSource);
product = productDao.findByName("product");
assertTrue(product.isPresent());
assertEquals("product", product.get().getName());
assertEquals(Money.of(USD, 100), product.get().getPrice());
assertEquals(LocalDate.parse("2021-06-27"), product.get().getExpirationDate());
}
@Test
void shouldSaveProduct() throws SQLException {
productDao.save(product);
try (var connection = dataSource.getConnection();
var statement = connection.createStatement();
ResultSet rs = statement.executeQuery(SELECT_PRODUCTS_SQL)) {
assertTrue(rs.next());
assertEquals(product.getName(), rs.getString("name"));
assertEquals(product.getPrice(), Money.of(USD, rs.getBigDecimal("price")));
assertEquals(product.getExpirationDate(), rs.getDate("expiration_date").toLocalDate());
}
@AfterEach
void tearDown() throws SQLException {
TestUtils.deleteSchema(dataSource);
}
assertThrows(SQLException.class, () -> productDao.save(product));
}
@Test
void shouldFindProductByName() throws SQLException {
var product = productDao.findByName("product");
@Test
void shouldUpdateProduct() throws SQLException {
TestUtils.executeSQL(INSERT_PRODUCT_SQL, dataSource);
assertTrue(product.isEmpty());
product.setPrice(Money.of(USD, 99.0));
TestUtils.executeSQL(INSERT_PRODUCT_SQL, dataSource);
productDao.update(product);
product = productDao.findByName("product");
try (var connection = dataSource.getConnection();
var statement = connection.createStatement();
ResultSet rs = statement.executeQuery(SELECT_PRODUCTS_SQL)) {
assertTrue(product.isPresent());
assertEquals("product", product.get().getName());
assertEquals(Money.of(USD, 100), product.get().getPrice());
assertEquals(LocalDate.parse("2021-06-27"), product.get().getExpirationDate());
}
@Test
void shouldSaveProduct() throws SQLException {
productDao.save(product);
try (var connection = dataSource.getConnection();
var statement = connection.createStatement();
ResultSet rs = statement.executeQuery(SELECT_PRODUCTS_SQL)) {
assertTrue(rs.next());
assertEquals(product.getName(), rs.getString("name"));
assertEquals(product.getPrice(), Money.of(USD, rs.getBigDecimal("price")));
assertEquals(product.getExpirationDate(), rs.getDate("expiration_date").toLocalDate());
}
assertThrows(SQLException.class, () -> productDao.save(product));
}
@Test
void shouldUpdateProduct() throws SQLException {
TestUtils.executeSQL(INSERT_PRODUCT_SQL, dataSource);
product.setPrice(Money.of(USD, 99.0));
productDao.update(product);
try (var connection = dataSource.getConnection();
var statement = connection.createStatement();
ResultSet rs = statement.executeQuery(SELECT_PRODUCTS_SQL)) {
assertTrue(rs.next());
assertEquals(product.getName(), rs.getString("name"));
assertEquals(product.getPrice(), Money.of(USD, rs.getBigDecimal("price")));
assertEquals(product.getExpirationDate(), rs.getDate("expiration_date").toLocalDate());
}
assertTrue(rs.next());
assertEquals(product.getName(), rs.getString("name"));
assertEquals(product.getPrice(), Money.of(USD, rs.getBigDecimal("price")));
assertEquals(product.getExpirationDate(), rs.getDate("expiration_date").toLocalDate());
}
}
}
@@ -24,55 +24,56 @@
*/
package com.iluwatar.domainmodel;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Optional;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Optional;
import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ProductTest {
private ProductDao productDao;
private Product product;
private ProductDao productDao;
private Product product;
@BeforeEach
void setUp() {
productDao = mock(ProductDaoImpl.class);
@BeforeEach
void setUp() {
productDao = mock(ProductDaoImpl.class);
product = Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.now().plusDays(10))
.productDao(productDao)
.build();
}
product =
Product.builder()
.name("product")
.price(Money.of(USD, 100.0))
.expirationDate(LocalDate.now().plusDays(10))
.productDao(productDao)
.build();
}
@Test
void shouldSaveProduct() throws SQLException {
when(productDao.findByName("product")).thenReturn(Optional.empty());
@Test
void shouldSaveProduct() throws SQLException {
when(productDao.findByName("product")).thenReturn(Optional.empty());
product.save();
product.save();
verify(productDao, times(1)).save(product);
verify(productDao, times(1)).save(product);
when(productDao.findByName("product")).thenReturn(Optional.of(product));
when(productDao.findByName("product")).thenReturn(Optional.of(product));
product.save();
product.save();
verify(productDao, times(1)).update(product);
}
verify(productDao, times(1)).update(product);
}
@Test
void shouldGetSalePriceOfProduct() {
assertEquals(Money.of(USD, 100), product.getSalePrice());
@Test
void shouldGetSalePriceOfProduct() {
assertEquals(Money.of(USD, 100), product.getSalePrice());
product.setExpirationDate(LocalDate.now().plusDays(2));
product.setExpirationDate(LocalDate.now().plusDays(2));
assertEquals(Money.of(USD, 80), product.getSalePrice());
}
assertEquals(Money.of(USD, 80), product.getSalePrice());
}
}
@@ -24,13 +24,13 @@
*/
package com.iluwatar.domainmodel;
import org.h2.jdbcx.JdbcDataSource;
import javax.sql.DataSource;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.h2.jdbcx.JdbcDataSource;
public class TestUtils {
public static void executeSQL( String sql, DataSource dataSource) throws SQLException {
public static void executeSQL(String sql, DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.executeUpdate(sql);