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();
}
}