diff --git a/spring-modulith-example/pom.xml b/spring-modulith-example/pom.xml index 64e4d12d..c7070933 100644 --- a/spring-modulith-example/pom.xml +++ b/spring-modulith-example/pom.xml @@ -72,6 +72,11 @@ + + org.jmolecules + jmolecules-ddd + + org.jmolecules jmolecules-events @@ -112,6 +117,12 @@ true + + org.projectlombok + lombok + provided + + diff --git a/spring-modulith-example/src/main/asciidoc/index.adoc b/spring-modulith-example/src/main/asciidoc/index.adoc new file mode 100644 index 00000000..bab583d6 --- /dev/null +++ b/spring-modulith-example/src/main/asciidoc/index.adoc @@ -0,0 +1,16 @@ += Spring Modulith Example Documentation +:modulith-docs: ../../../target/spring-modulith-docs + +== Overview + +plantuml::{modulith-docs}/components.puml[format="svg"] + +== Inventory + +plantuml::{modulith-docs}/module-inventory.puml[format="svg"] +include::{modulith-docs}/module-inventory.adoc[] + +== Orders + +plantuml::{modulith-docs}/module-order.puml[format="svg"] +include::{modulith-docs}/module-order.adoc[] diff --git a/spring-modulith-example/src/main/java/example/inventory/InventoryManagement.java b/spring-modulith-example/src/main/java/example/inventory/InventoryManagement.java index 8ed3b72e..26476c50 100644 --- a/spring-modulith-example/src/main/java/example/inventory/InventoryManagement.java +++ b/spring-modulith-example/src/main/java/example/inventory/InventoryManagement.java @@ -16,6 +16,7 @@ package example.inventory; import example.order.OrderCompleted; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,15 +29,12 @@ import org.springframework.stereotype.Service; * @author Oliver Drotbohm */ @Service -public class InventoryManagement { +@RequiredArgsConstructor +class InventoryManagement { private static final Logger LOG = LoggerFactory.getLogger(InventoryManagement.class); - final InventoryInternal dependency; - - InventoryManagement(InventoryInternal dependency) { - this.dependency = dependency; - } + private final InventoryInternal dependency; @ApplicationModuleListener void on(OrderCompleted event) throws InterruptedException { diff --git a/spring-modulith-example/src/main/java/example/inventory/package-info.java b/spring-modulith-example/src/main/java/example/inventory/package-info.java index 548d9a37..bf3e64df 100644 --- a/spring-modulith-example/src/main/java/example/inventory/package-info.java +++ b/spring-modulith-example/src/main/java/example/inventory/package-info.java @@ -4,4 +4,5 @@ * * @see example.inventory.InventoryInternal */ +@org.springframework.lang.NonNullApi package example.inventory; diff --git a/spring-modulith-example/src/main/java/example/order/Order.java b/spring-modulith-example/src/main/java/example/order/Order.java index c310caa8..d0bc7088 100644 --- a/spring-modulith-example/src/main/java/example/order/Order.java +++ b/spring-modulith-example/src/main/java/example/order/Order.java @@ -15,18 +15,20 @@ */ package example.order; +import example.order.Order.OrderIdentifier; +import lombok.Getter; + import java.util.UUID; +import org.jmolecules.ddd.types.AggregateRoot; +import org.jmolecules.ddd.types.Identifier; + /** * @author Oliver Drotbohm */ -public class Order { +public class Order implements AggregateRoot { - private OrderIdentifier id = new OrderIdentifier(UUID.randomUUID()); + private @Getter OrderIdentifier id = new OrderIdentifier(UUID.randomUUID()); - public OrderIdentifier getId() { - return id; - } - - public static record OrderIdentifier(UUID id) {} + public static record OrderIdentifier(UUID id) implements Identifier {} } diff --git a/spring-modulith-example/src/main/java/example/order/OrderManagement.java b/spring-modulith-example/src/main/java/example/order/OrderManagement.java index 5bbe1693..89eb98d6 100644 --- a/spring-modulith-example/src/main/java/example/order/OrderManagement.java +++ b/spring-modulith-example/src/main/java/example/order/OrderManagement.java @@ -16,6 +16,8 @@ package example.order; import example.order.internal.OrderInternal; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @@ -25,16 +27,11 @@ import org.springframework.transaction.annotation.Transactional; * @author Oliver Drotbohm */ @Service +@RequiredArgsConstructor public class OrderManagement { - final ApplicationEventPublisher events; - final OrderInternal dependency; - - OrderManagement(ApplicationEventPublisher events, OrderInternal dependency) { - - this.events = events; - this.dependency = dependency; - } + private final @NonNull ApplicationEventPublisher events; + private final @NonNull OrderInternal dependency; @Transactional public void complete(Order order) { diff --git a/spring-modulith-example/src/main/java/example/order/internal/OrderInternal.java b/spring-modulith-example/src/main/java/example/order/internal/OrderInternal.java index 1a9db4e3..c07580b2 100644 --- a/spring-modulith-example/src/main/java/example/order/internal/OrderInternal.java +++ b/spring-modulith-example/src/main/java/example/order/internal/OrderInternal.java @@ -18,7 +18,8 @@ package example.order.internal; import org.springframework.stereotype.Component; /** - * Some order-internal application component. + * Some order-internal application component. Referring to it from the inventory module would be captured by + * {@link example.ModularityTests}. * * @author Oliver Drotbohm */ diff --git a/spring-modulith-example/src/main/java/example/order/package-info.java b/spring-modulith-example/src/main/java/example/order/package-info.java index 9cd4120e..376d865b 100644 --- a/spring-modulith-example/src/main/java/example/order/package-info.java +++ b/spring-modulith-example/src/main/java/example/order/package-info.java @@ -4,4 +4,5 @@ * * @see example.ModularityTests */ +@org.springframework.lang.NonNullApi package example.order; diff --git a/spring-modulith-example/src/test/java/example/order/EventPublicationRegistryTests.java b/spring-modulith-example/src/test/java/example/order/EventPublicationRegistryTests.java index 55d74752..4c7cf931 100644 --- a/spring-modulith-example/src/test/java/example/order/EventPublicationRegistryTests.java +++ b/spring-modulith-example/src/test/java/example/order/EventPublicationRegistryTests.java @@ -18,12 +18,15 @@ package example.order; import static org.assertj.core.api.Assertions.*; import example.order.EventPublicationRegistryTests.FailingAsyncTransactionalEventListener; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Import; import org.springframework.modulith.ApplicationModuleListener; import org.springframework.modulith.events.EventPublicationRegistry; import org.springframework.modulith.test.ApplicationModuleTest; +import org.springframework.modulith.test.Scenario; import org.springframework.test.annotation.DirtiesContext; /** @@ -35,37 +38,41 @@ import org.springframework.test.annotation.DirtiesContext; @ApplicationModuleTest @Import(FailingAsyncTransactionalEventListener.class) @DirtiesContext +@RequiredArgsConstructor class EventPublicationRegistryTests { private final OrderManagement orders; private final EventPublicationRegistry registry; - - /** - * @param orders - * @param registry - */ - EventPublicationRegistryTests(OrderManagement orders, EventPublicationRegistry registry) { - this.orders = orders; - this.registry = registry; - } + private final FailingAsyncTransactionalEventListener listener; @Test - void leavesPublicationIncompleteForFailingListener() throws Exception { + void leavesPublicationIncompleteForFailingListener(Scenario scenario) throws Exception { var order = new Order(); - orders.complete(order); - - Thread.sleep(40); - - assertThat(registry.findIncompletePublications()).hasSize(1); + scenario.stimulate(() -> orders.complete(order)) + .andWaitForStateChange(() -> listener.getEx()) + .andVerify(__ -> { + assertThat(registry.findIncompletePublications()).hasSize(1); + }); } static class FailingAsyncTransactionalEventListener { + @Getter Exception ex; + @ApplicationModuleListener void foo(OrderCompleted event) { - throw new IllegalStateException(); + + var exception = new IllegalStateException("¯\\_(ツ)_/¯"); + + try { + + throw exception; + + } finally { + this.ex = exception; + } } } } diff --git a/spring-modulith-example/src/test/java/example/order/OrderIntegrationTests.java b/spring-modulith-example/src/test/java/example/order/OrderIntegrationTests.java index d8884dc0..f4facd2a 100644 --- a/spring-modulith-example/src/test/java/example/order/OrderIntegrationTests.java +++ b/spring-modulith-example/src/test/java/example/order/OrderIntegrationTests.java @@ -15,42 +15,29 @@ */ package example.order; -import static org.assertj.core.api.Assertions.*; +import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; import org.springframework.modulith.test.ApplicationModuleTest; -import org.springframework.modulith.test.AssertablePublishedEvents; +import org.springframework.modulith.test.Scenario; /** * @author Oliver Drotbohm */ @ApplicationModuleTest +@RequiredArgsConstructor class OrderIntegrationTests { private final OrderManagement orders; - OrderIntegrationTests(OrderManagement orders) { - this.orders = orders; - } - @Test - void publishesOrderCompletion(AssertablePublishedEvents events) { + void publishesOrderCompletion(Scenario scenario) { var reference = new Order(); - orders.complete(reference); - - // Verification - - var matchingMapped = events.ofType(OrderCompleted.class) - .matching(OrderCompleted::orderId, reference.getId()); - - assertThat(matchingMapped).hasSize(1); - - // AssertJ - - assertThat(events) - .contains(OrderCompleted.class) - .matching(OrderCompleted::orderId, reference.getId()); + scenario.stimulate(() -> orders.complete(reference)) + .andWaitForEventOfType(OrderCompleted.class) + .matchingMappedValue(OrderCompleted::orderId, reference.getId()) + .toArrive(); } }