### Basic Integration Test Setup with @DataJpaTest Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Demonstrates a basic JUnit 5 integration test setup using `@DataJpaTest` and `@ModuleSlicing` for testing a specific module's repository. ```java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.modulith.test.ModuleSlicing; @ModuleSlicing @DataJpaTest class SomeModuleRepositoryIntegrationTests { @Autowired SomeRepository repository; @Test void someTest() { … } } ``` -------------------------------- ### Example Response for Application Modules Actuator Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc An example JSON response from the application modules actuator endpoint. ```APIDOC ## Example Response ### Application Modules Actuator ```json { "a": { "basePackage": "example.a", "displayName": "A", "dependencies": [] }, "b": { "basePackage": "example.b", "displayName": "B", "dependencies": [ { "target": "a", "types": [ "EVENT_LISTENER", "USES_COMPONENT" ] } ] } } ``` ``` -------------------------------- ### Defining a Stimulus: Event Publication Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Illustrates how to start a `Scenario` by publishing an application event. This action is performed within a transaction callback. ```java // Start with an event publication scenario.publish(new MyApplicationEvent(…)).… ``` ```kotlin // Start with an event publication scenario.publish(MyApplicationEvent(…)).… ``` -------------------------------- ### Example Application Modules Actuator Response Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc This JSON structure represents the response from the application modules actuator, detailing module base packages, display names, and dependencies. ```json { "a": { "basePackage": "example.a", "displayName": "A", "dependencies": [] }, "b": { "basePackage": "example.b", "displayName": "B", "dependencies": [ { "target": "a", "types": [ "EVENT_LISTENER", "USES_COMPONENT" ] } ] } } ``` -------------------------------- ### Service with Direct Dependency Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Example of a service class with a direct dependency on another service, leading to tight coupling. ```java @Service @RequiredArgsConstructor public class OrderManagement { private final InventoryManagement inventory; @Transactional public void complete(Order order) { // State transition on the order aggregate go here // Invoke related functionality inventory.updateStockFor(order); } } ``` ```kotlin @Service class OrderManagement(val inventory: InventoryManagement) { @Transactional fun complete(order: Order) { inventory.updateStockFor(order) } } ``` -------------------------------- ### Observability Dependency Setup Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Add the necessary runtime dependency to enable Spring Modulith's observability support. For customizing metrics, include the API artifact as well. ```APIDOC ## Dependency Management ### Maven ```xml org.springframework.modulith spring-modulith-observability-core {projectVersion} runtime ``` ### Gradle ```gradle dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-observability-core:{projectVersion}' } ``` For compile-time customization of metrics: ```xml org.springframework.modulith spring-modulith-observability-api {projectVersion} compile ``` ```gradle dependencies { compileOnly 'org.springframework.modulith:spring-modulith-observability-api:{projectVersion}' } ``` ``` -------------------------------- ### Access Modulith Actuator HTTP Resource Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Example response from the actuator endpoint showing the modulith resource. ```json GET http://localhost:8080/actuator { "_links": { "self": { "href": "http://localhost:8080/actuator", "templated": false }, "health-path": { "href": "http://localhost:8080/actuator/health/{*path}", "templated": true }, "health": { "href": "http://localhost:8080/actuator/health", "templated": false }, "modulith": { <1> "href": "http://localhost:8080/actuator/modulith", "templated": false } } } ``` -------------------------------- ### Create Application Module Model (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use `ApplicationModules.of()` to create an in-memory representation of your application's module arrangement. Point it to your Spring Boot application class to start the analysis. ```java var modules = ApplicationModules.of(Application.class); ``` -------------------------------- ### Transactional Event Listener with @Async Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Example of a transactional event listener using @Async and @Transactional(propagation = Propagation.REQUIRES_NEW). ```kotlin @Component class InventoryManagement { @Async @Transactional(propagation = Propagation.REQUIRES_NEW) @TransactionalEventListener fun on(event: OrderCompleted) { /* … */ } } ``` -------------------------------- ### Implement Custom ApplicationModuleDetectionStrategy (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Provide a custom implementation of `ApplicationModuleDetectionStrategy` to define your own module detection logic. This Kotlin example shows the basic structure. ```kotlin package example class CustomApplicationModuleDetectionStrategy : ApplicationModuleDetectionStrategy { override fun getModuleBasePackages(basePackage: JavaPackage): Stream { // Your module detection goes here } } ``` -------------------------------- ### Implement Custom ApplicationModuleDetectionStrategy (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Provide a custom implementation of `ApplicationModuleDetectionStrategy` to define your own module detection logic. This Java example shows the basic structure. ```java package example; class CustomApplicationModuleDetectionStrategy implements ApplicationModuleDetectionStrategy { @Override public Stream getModuleBasePackages(JavaPackage basePackage) { // Your module detection goes here } } ``` -------------------------------- ### Register ScenarioCustomizer in Kotlin Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Registers a ScenarioCustomizer as a JUnit extension for global scenario customization in Kotlin. This ensures uniform test setup across a test class. ```kotlin @ExtendWith(MyCustomizer::class) class MyTests { @Test fun myTestCase(scenario: Scenario) { // scenario will be pre-customized with logic defined in MyCustomizer } class MyCustomizer : ScenarioCustomizer { override fun getDefaultCustomizer(method: Method, context: ApplicationContext): UnaryOperator { return UnaryOperator { conditionFactory -> … } } } } ``` -------------------------------- ### Prepare a release with Maven Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/build-and-releases.adoc Execute this command on the version branch to prepare a release, specifying the version, development version, and SCM commit comments. ```bash ./mvnw release:prepare \ -Pprepare-release \ -DreleaseVersion="${version}" \ -DdevelopmentVersion="${devVersion}" \ -DscmReleaseCommitComment="${ticketId} - Release version ${version}." \ -DscmDevelopmentCommitComment="${ticketId} - Prepare next development iteration." \ -Dtag="${version}" ``` -------------------------------- ### Spring Modulith Starter Modules Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/appendix.adoc Overview of available Spring Modulith starter POMs and their included dependencies. ```APIDOC ## Starter Modules - **spring-modulith-starter-core**: Includes core, api, apt, moments, and runtime support. - **spring-modulith-starter-insight**: Includes actuator and observability support. - **spring-modulith-starter-jdbc**: Includes core and JDBC event support. - **spring-modulith-starter-jpa**: Includes core and JPA event support. - **spring-modulith-starter-mongodb**: Includes core and MongoDB event support. - **spring-modulith-starter-neo4j**: Includes core and Neo4j event support. - **spring-modulith-starter-test**: Includes documentation and testing utilities. ``` -------------------------------- ### Print Application Module Arrangement (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Iterate over the `ApplicationModules` object and print each module's details to the console using `forEach` and `System.out::println`. ```java modules.forEach(System.out::println); ``` -------------------------------- ### Print Application Module Arrangement (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use the `forEach` lambda to print the details of each module within the `ApplicationModules` object to the console. ```kotlin modules.forEach { println(it) } ``` -------------------------------- ### Enable Spring Modulith Insight Starter Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Add the Insight starter to enable both actuator and observability support for application modules. ```xml org.springframework.modulith spring-modulith-starter-insight {projectVersion} runtime ``` ```gradle dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-starter-insight:{projectVersion}' } ``` -------------------------------- ### Declare Spring Modulith Namastack Starter Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Add the Namastack starter dependency to enable advanced outbox features for relational databases. ```xml org.springframework.modulith spring-modulith-starter-namastack ``` ```gradle dependencies { implementation 'org.springframework.modulith:spring-modulith-starter-namastack' } ``` -------------------------------- ### Build Spring Modulith Project Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Command to compile, test, and build the project using the Maven wrapper. ```bash ./mvnw -B ``` -------------------------------- ### Application Module Listener Shortcut Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Demonstrates using @ApplicationModuleListener as a shortcut for declaring module integration via events. ```java @Component class InventoryManagement { @ApplicationModuleListener void on(OrderCompleted event) { /* … */ } } ``` ```kotlin @Component class InventoryManagement { @ApplicationModuleListener fun on(event: OrderCompleted) { /* … */ } } ``` -------------------------------- ### Implement Custom Application Module Initializer (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/runtime.adoc Implement the `ApplicationModuleInitializer` interface in Java to define custom initialization logic for a module. The execution order follows the module dependency structure. ```java @Component class MyInitializer implements ApplicationModuleInitializer { @Override public void initialize() { // Initialization code goes here } } ``` -------------------------------- ### Configure Traditional UML Component Diagrams Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Adjust DiagramOptions to switch from C4 to traditional UML style component diagrams. ```java DiagramOptions.defaults() ``` -------------------------------- ### Add Spring Modulith Test Starter Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Include the `spring-modulith-starter-test` dependency in your project to enable integration testing capabilities. ```xml org.springframework.modulith spring-modulith-starter-test test ``` -------------------------------- ### Implement Custom Application Module Initializer (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/runtime.adoc Implement the `ApplicationModuleInitializer` interface in Kotlin to define custom initialization logic for a module. The execution order follows the module dependency structure. ```kotlin @Component class MyInitializer : ApplicationModuleInitializer { override fun initialize() { // Initialization code goes here } } ``` -------------------------------- ### Verify Event Arrival Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Executes a scenario and defines assertions on the received event. Use this to confirm specific events are published and meet certain criteria. ```kotlin ….toArriveAndVerify(event -> …) ``` -------------------------------- ### Expecting an Event with Matchers Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Shows how to define an expectation for a specific event type to be published as the result of a scenario, with optional matching criteria. ```java ….andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) // Use some predicate here .… ``` ```kotlin ….andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) // Use some predicate here .… ``` -------------------------------- ### Generate Documentation Snippets Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Use the Documenter class to generate PlantUML diagrams from your application module model. ```java class ApplicationTests { @Test void writeDocumentationSnippets() { var modules = ApplicationModules.of(Application.class).verify(); <1> new Documenter(modules) <2> .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml(); } } ``` -------------------------------- ### Define a Complete Scenario in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Publishes an event, waits for another event of a specific type, matches it based on a condition, and verifies its arrival. This is for end-to-end event flow testing. ```java scenario.publish(new MyApplicationEvent(…)) .andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) .toArriveAndVerify(event -> …); ``` -------------------------------- ### Create Application Module Model (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use `ApplicationModules.of()` with the application class reference to create an in-memory model of your application's modules in Kotlin. ```kotlin val modules = ApplicationModules.of(Application::class.java) ``` -------------------------------- ### Verify Application Module Structure (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/verification.adoc Call `ApplicationModules.of(Application.class).verify()` to check if the code arrangement adheres to intended constraints. This is useful for enforcing architectural rules. ```java ApplicationModules.of(Application.class).verify(); ``` -------------------------------- ### Generate Application Module Component Diagrams Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Use the Documenter class to produce C4 component diagrams for the entire system or individual modules. ```java class DocumentationTests { ApplicationModules modules = ApplicationModules.of(Application.class); @Test void writeDocumentationSnippets() { new Documenter(modules) .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml(); } } ``` ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml() } } ``` -------------------------------- ### Observability Metrics - All Events Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Details on the `module.events.published` metric and its low cardinality keys. ```APIDOC ## Observability Metrics ### The All Events Metric - `module.events.published` – a counter summarizing all event publications. #### Low Cardinality Keys |=== |Name | Description |`module.event.type`|Type of the emitted event. |`module.identifier`|The identifier of the module. |`module.name`|Name of the module. |=== ``` -------------------------------- ### Generate Aggregating Document with Documenter (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc This Kotlin code snippet demonstrates how to generate an aggregating document using `Documenter.writeAggregatingDocument()`. It requires `ApplicationModules` to be initialized. ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeAggregatingDocument() } } ``` -------------------------------- ### Define a Complete Scenario in Kotlin Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Publishes an event, waits for another event of a specific type, matches it using a lambda, and verifies its arrival. This is the Kotlin equivalent for end-to-end event flow testing. ```kotlin scenario.publish(new MyApplicationEvent(…)) .andWaitForEventOfType(SomeOtherEvent::class.java) .matching { event -> … } .toArriveAndVerify { event -> … } ``` -------------------------------- ### Enable Spring Modulith Actuator Support Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Add the actuator dependency and the Spring Boot actuator starter to expose module information. ```xml org.springframework.modulith spring-modulith-actuator {projectVersion} runtime org.springframework.boot spring-boot-starter-actuator runtime ``` ```gradle dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-actuator:{projectVersion}' } dependencies { runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator' } ``` -------------------------------- ### Clone Spring Modulith Repository Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Commands to clone the source code repository. ```bash git clone git@github.com:spring-projects/spring-modulith.git cd spring-modulith ``` -------------------------------- ### Configure Spring Modulith Dependencies Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Add the BOM and test starter to your Maven project to enable Spring Modulith features. ```xml org.springframework.modulith spring-modulith-bom 2.0.5 import pom org.springframework.modulith spring-modulith-starter-test test ``` -------------------------------- ### Customize Verification with VerificationOptions Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/verification.adoc Configure custom verification rules, such as jMolecules Architecture rules, using `VerificationOptions`. This allows for fine-grained control over the verification process. ```java var hexagonal = JMoleculesArchitectureRules.ensureHexagonal(VerificationDepth.STRICT); var options = VerificationOptions.defaults().withAdditionalVerifications(hexagonal); ApplicationModules.of(…).verify(options); ``` -------------------------------- ### Declare Application Module as Open (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc In Kotlin, use the @ApplicationModule annotation with Type.OPEN on a ModuleMetadata class in the root package. This achieves the same effect as the Java package-info.java approach. ```kotlin package example.inventory import org.springframework.modulith.ApplicationModule import org.springframework.modulith.PackageInfo @ApplicationModule( type = Type.OPEN ) @PackageInfo class ModuleMetadata {} ``` -------------------------------- ### Verify Application Module Structure (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/verification.adoc Use `ApplicationModules.of(Application::class.java).verify()` in Kotlin to ensure your application module structure follows defined rules. This method checks for architectural violations. ```kotlin ApplicationModules.of(Application::class.java).verify() ``` -------------------------------- ### Generate Aggregating Document with Documenter (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Use this Java code to generate an aggregating document by calling `Documenter.writeAggregatingDocument()`. Ensure you have initialized `ApplicationModules`. ```java class DocumentationTests { ApplicationModules modules = ApplicationModules.of(Application.class); @Test void writeDocumentationSnippets() { new Documenter(modules) .writeAggregatingDocument(); } } ``` -------------------------------- ### Generate Application Module Canvases Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Uses the Documenter class to generate documentation canvases for application modules. ```java class DocumentationTests { ApplicationModules modules = ApplicationModules.of(Application.class); @Test void writeDocumentationSnippets() { new Documenter(modules) .writeModuleCanvases(); } } ``` ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeModuleCanvases() } } ``` -------------------------------- ### Executing the Scenario Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Provides the terminal operation to trigger the execution of the event-based `Scenario` and verify its outcome. ```java // Executes the scenario ….toArrive(…) ``` -------------------------------- ### Configure Diagram Style Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Sets the diagram style to UML for module relationship visualization. ```java .withStyle(DiagramStyle.UML); ``` ```kotlin DiagramOptions.defaults() .withStyle(DiagramStyle.UML) ``` -------------------------------- ### Observability Metrics - Individual Events Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Details on the `module.events.published.$moduleIdentifier.$simpleEventTypeName` metric and its low cardinality keys. ```APIDOC ### The Individual Events Metric - `module.events.published.$moduleIdentifier.$simpleEventTypeName` - a counter for the individual event that can be further enriched with domain-specific values. #### Low Cardinality Keys |=== |Name | Description |`module.identifier`|The identifier of the module. |`module.name`|Name of the module. |=== ``` -------------------------------- ### Implement Custom ApplicationModuleSourceFactory Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Implement `ApplicationModuleSourceFactory` to define root packages for scanning, specify a detection strategy, and list explicit module base packages. ```java package example; public class CustomApplicationModuleSourceFactory implements ApplicationModuleSourceFactory { @Override public List getRootPackages() { return List.of("com.acme.toscan"); } @Override public ApplicationModuleDetectionStrategy getApplicationModuleDetectionStrategy() { return ApplicationModuleDetectionStrategy.explicitlyAnnotated(); } @Override public List getModuleBasePackages() { return List.of("com.acme.module"); } } ``` -------------------------------- ### Render UML Component Diagrams Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc PlantUML configuration for rendering module relationships as component diagrams. ```plantuml skinparam { shadowing false arrowColor #707070 actorBorderColor #707070 componentBorderColor #707070 rectangleBorderColor #707070 noteBackgroundColor #ffffff noteBorderColor #707070 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 componentStyle uml1 } package "Application" <> { component 4 <> #dddddd [ com.acme.commerce.catalog ] component 3 <> #dddddd [ com.acme.commerce.core ] component 7 <> #dddddd [ com.acme.commerce.customer ] component 5 <> #dddddd [ com.acme.commerce.inventory ] component 6 <> #dddddd [ com.acme.commerce.order ] } 4 .[#707070].> 3 : depends on 5 .[#707070].> 4 : uses 5 .[#707070].> 3 : uses 5 .[#707070].> 6 : uses 5 .[#707070].> 6 : listens to 6 .[#707070].> 4 : depends on 6 .[#707070].> 3 : depends on 6 .[#707070].> 7 : uses ``` ```plantuml skinparam { shadowing false arrowColor #707070 actorBorderColor #707070 componentBorderColor #707070 rectangleBorderColor #707070 noteBackgroundColor #ffffff noteBorderColor #707070 defaultTextAlignment center wrapWidth 200 maxMessageSize 100 componentStyle uml1 } package "Application" <> { component 4 <> #dddddd [ com.acme.commerce.catalog ] component 3 <> #dddddd [ com.acme.commerce.core ] component 7 <> #dddddd [ com.acme.commerce.customer ] component 6 <> #dddddd [ com.acme.commerce.order ] } 4 .[#707070].> 3 : depends on 6 .[#707070].> 4 : depends on 6 .[#707070].> 3 : depends on 6 .[#707070].> 7 : uses ``` -------------------------------- ### Programmatically Configure Event Externalization Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Customize event selection, mapping, headers, and routing keys using the EventExternalizationConfiguration API. ```java @Configuration class ExternalizationConfiguration { @Bean EventExternalizationConfiguration eventExternalizationConfiguration() { return EventExternalizationConfiguration.externalizing() // <1> .select(EventExternalizationConfiguration.annotatedAsExternalized()) // <2> .mapping(SomeEvent.class, event -> …) // <3> .headers(event -> …) // <4> .routeKey(WithKeyProperty.class, WithKeyProperty::getKey) // <5> .build(); } } ``` ```kotlin @Configuration class ExternalizationConfiguration { @Bean fun eventExternalizationConfiguration(): EventExternalizationConfiguration { EventExternalizationConfiguration.externalizing() // <1> .select(EventExternalizationConfiguration.annotatedAsExternalized()) // <2> .mapping(SomeEvent::class.java) { event -> … } // <3> .headers() { event -> … } // <4> .routeKey(WithKeyProperty::class.java, WithKeyProperty::getKey) // <5> .build() } } ``` -------------------------------- ### Spring Boot Compatibility Matrix Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/appendix.adoc This section provides a compatibility matrix for Spring Modulith versions against Spring Boot and jMolecules versions. ```APIDOC ## Spring Boot Compatibility This section provides a compatibility matrix for Spring Modulith versions against Spring Boot and jMolecules versions. |=== |Spring Modulith Version |Spring Boot Version + (compiled against) |Spring Boot Version + (examples tested against) |jMolecules + (compiled) |jMolecules + (tested) |2.0 (snapshot) |4.0 SNAPSHOT |4.0 SNAPSHOT and milestones |2023.2 |2023.2, 2025.0 RC2 |1.4 |3.5 |3.1, 3.2, 3.3, 3.4, 3.5 |2023.2 |2023.2, 2025.0 RC2 |1.3 |3.4 |3.1, 3.2, 3.3, 3.4, 3.5 |2023.1 |2023.1, 2023.2, 2025.0 RC2 |1.2 |3.3 |3.1, 3.2, 3.3, 3.4 |2023.1 |2023.1, 2023.2, 2025.0 RC2 |1.1 |3.2 |3.1, 3.2, 3.3, 3.4 |2023.1 |2023.1, 2023.2, 2025.0 RC2 |=== ``` -------------------------------- ### Namastack Outbox Event Externalization Output Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-examples/spring-modulith-example-outbox/readme.adoc Shows the log output when using Namastack for outbox event externalization. It highlights application bootstrap configuration, outbox registration, event publication, persistence to the outbox table, and asynchronous processing to Kafka. ```log 14:53:40.959 D - main : Configuring event externalization to export events annotated with @Externalized in packages: [example, ...] <1> … 14:53:42.225 D - main : Registering domain event outbox externalization to Kafka… <2> 14:53:42.227 D - main : Registering domain event externalization via outbox… <3> … 14:53:42.247 I - main : Triggering order completion… <4> 14:53:42.251 D - main : Scheduling event of type example.order.OrderCompleted to outbox for target order.OrderCompleted. <5> … 14:53:44.494 D - eduler-1 : Found 1 record keys to process <6> … 14:53:44.767 D - eduler-1 : Finished processing 1 record keys <7> ``` -------------------------------- ### Defining a Stimulus: Bean Invocation Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Demonstrates initiating a `Scenario` by invoking a Spring bean's method. This is also executed within a transaction callback. ```java // Start with a bean invocation scenario.stimulate(() -> someBean.someMethod(…)).… ``` ```kotlin // Start with a bean invocation scenario.stimulate(Runnable { someBean.someMethod(…) }).… ``` -------------------------------- ### Declare Application Module as Open (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use the @ApplicationModule annotation with Type.OPEN on package-info.java to make an application module open. This allows broader access to internal types from other modules. ```java @org.springframework.modulith.ApplicationModule( type = Type.OPEN ) package example.inventory; ``` -------------------------------- ### Application Module Integration Test Bootstrap Log Output Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Observe detailed log output when running `@ApplicationModuleTest` to understand how the Spring Boot bootstrap is customized for the specific application module. ```text . ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \/ ___)| |_)| | | | | |\__, | ) ) ) ) ' |____| .__|_| |_|_| |\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.0.0-SNAPSHOT) … - Bootstrapping @ApplicationModuleTest for example.order in mode STANDALONE (class example.Application)… … - ====================================================================================================== … - ## example.order ## … - > Logical name: order … - > Base package: example.order … - > Direct module dependencies: none … - > Spring beans: … - + ….OrderManagement … - + ….internal.OrderInternal … - Starting OrderIntegrationTests using Java 17.0.3 … … - No active profile set, falling back to 1 default profile: "default" … - pass:quotes[**Re-configuring auto-configuration and entity scan packages to: example.order.**] ``` -------------------------------- ### Publishing Application Event via ApplicationEventPublisher Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Demonstrates publishing an application event using Spring's ApplicationEventPublisher to decouple modules. ```java @Service @RequiredArgsConstructor public class OrderManagement { private final ApplicationEventPublisher events; private final OrderInternal dependency; @Transactional public void complete(Order order) { // State transition on the order aggregate go here events.publishEvent(new OrderCompleted(order.getId())); } } ``` ```kotlin @Service class OrderManagement(val events: ApplicationEventPublisher, val dependency: OrderInternal) { @Transactional fun complete(order: Order) { events.publishEvent(OrderCompleted(order.id)) } } ``` -------------------------------- ### Declare Spring Modulith JobRunr Starter (Gradle) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Add the Spring Modulith JobRunr starter dependency to your Gradle project to enable JobRunr support for event externalization. ```gradle dependencies { implementation 'org.springframework.modulith:spring-modulith-starter-jobrunr' } ``` -------------------------------- ### Using the Scenario API in a JUnit 5 Test Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Shows how to declare the `Scenario` API as a test method parameter in tests annotated with `@ApplicationModuleTest`. ```java @ApplicationModuleTest class SomeApplicationModuleTest { @Test public void someModuleIntegrationTest(Scenario scenario) { // Use the Scenario API to define your integration test } } ``` ```kotlin @ApplicationModuleTest class SomeApplicationModuleTest { @Test fun someModuleIntegrationTest(scenario: Scenario) { // Use the Scenario API to define your integration test } } ``` -------------------------------- ### Simulated Kafka Operations for Testing Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-examples/spring-modulith-example-kafka/readme.adoc This bean simulates Kafka operations by logging messages, allowing tests to run without a live Kafka instance. It's declared in the test application context. ```java import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.ProducerFactory; import org.springframework.kafka.support.ProducerListener; import java.util.HashMap; @Configuration class KafkaConfiguration { @Bean public ProducerListener producerListener() { return new ProducerListener() { @Override public void onError(String topic, Integer partition, String key, String value, Throwable exception) { super.onError(topic, partition, key, value, exception); System.err.println("Error sending message to topic \"%s\": %s".formatted(topic, exception.getMessage())); } @Override public void onSend(String topic, Integer partition, String key, String value) { super.onSend(topic, partition, key, value); System.out.println("Sending message \"%s\" to topic \"%s\"".formatted(value, topic)); } }; } @Bean public KafkaTemplate kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } @Bean public ProducerFactory producerFactory() { return new DefaultKafkaProducerFactory<>(producerProps()); } private HashMap producerProps() { var props = new HashMap(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); return props; } } ``` -------------------------------- ### Observability Spans - Tag Keys Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Details on the tag keys used for module entry spans. ```APIDOC ## Observability Spans ### Module Entry Span #### Tag Keys |=== |Name | Description |`module.identifier`|The identifier of the module. |`module.invocation-type`|Type of invocation ("event listener" or "bean"). |`module.method`|Method executed on a module. |`module.name`|Name of the module. |=== ``` -------------------------------- ### Define a Named Interface in package-info Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Expose a package as a named interface to allow other modules to access its contents. ```java @org.springframework.modulith.NamedInterface("spi") package example.order.spi; ``` ```kotlin package example.order.spi import org.springframework.modulith.PackageInfo import org.springframework.modulith.NamedInterface @PackageInfo @NamedInterface("spi") class ModuleMetadata {} ``` -------------------------------- ### Declare Allowed Dependencies to All Named Interfaces Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use the asterisk wildcard to permit dependencies on all named interfaces of a target module. ```java @org.springframework.modulith.ApplicationModule( allowedDependencies = "order :: *" ) package example.inventory; ``` ```kotlin package example.inventory import org.springframework.modulith.ApplicationModule import org.springframework.modulith.PackageInfo @ApplicationModule( allowedDependencies = "order :: *" ) @PackageInfo class ModuleMetadata {} ``` -------------------------------- ### Declare Spring Modulith JobRunr Starter (Maven) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Add the Spring Modulith JobRunr starter dependency to your Maven project to enable JobRunr support for event externalization. ```xml org.springframework.modulith spring-modulith-starter-jobrunr ``` -------------------------------- ### Providing a Custom ModulithObservationConvention Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Configure a custom observation convention by providing a ModulithObservationConvention bean. This allows for fine-grained control over how observations are created. ```java @Configuration class ObservabilityConfiguration { @Bean ModulithObservationConvention observationConvention() { return new CustomModulithObservationConvention(); } } ``` -------------------------------- ### Customize Scenario Execution in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Customizes the execution of an individual scenario by setting a maximum duration for event arrival. This allows fine-tuning of test timeouts. ```java scenario.publish(new MyApplicationEvent(…)) .customize(conditionFactory -> conditionFactory.atMost(Duration.ofSeconds(2))) .andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) .toArriveAndVerify(event -> …); ``` -------------------------------- ### Configure Application Modules with @Modulithic Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Enable Spring Modulith features on the main application class. ```java package example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.modulith.Modulithic; @Modulithic @SpringBootApplication class MyApplication { public static void main(String... args) { SpringApplication.run(MyApplication.class, args); } } ``` ```kotlin package example import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.modulith.Modulithic @Modulithic @SpringBootApplication class MyApplication fun main(args: Array) { runApplication(*args) } ``` -------------------------------- ### Implement Custom ApplicationModuleDetectionStrategy Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use this strategy to define custom discovery logic for module base packages and named interfaces. The builder API allows recursive matching of specific package names like 'api'. ```java package example; class CustomApplicationModuleDetectionStrategy implements ApplicationModuleDetectionStrategy { @Override public Stream getModuleBasePackages(JavaPackage basePackage) { // Your module detection goes here } @Override NamedInterfaces detectNamedInterfaces(JavaPackage basePackage, ApplicationModuleInformation information) { return NamedInterfaces.builder() .recursive() .matching("api") .build(); } } ``` ```kotlin package example class CustomApplicationModuleDetectionStrategy : ApplicationModuleDetectionStrategy { override fun getModuleBasePackages(basePackage: JavaPackage): Stream { // Your module detection goes here } override fun detectNamedInterfaces(basePackage: JavaPackage, information: ApplicationModuleInformation): NamedInterfaces { return NamedInterfaces.builder() .recursive() .matching("api") .build() } } ``` -------------------------------- ### Customize Scenario Execution in Kotlin Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Customizes an individual scenario in Kotlin by applying a maximum duration for event arrival. This provides granular control over test timing. ```kotlin scenario.publish(MyApplicationEvent(…)) .customize { it.atMost(Duration.ofSeconds(2)) } .andWaitForEventOfType(SomeOtherEvent::class.java) .matching { event -> … } .toArriveAndVerify { event -> … } ``` -------------------------------- ### Create an Application Module Integration Test Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Annotate a JUnit test class with `@ApplicationModuleTest` within an application module's package to initiate module-specific integration testing. ```java package example.order; @ApplicationModuleTest class OrderIntegrationTests { // Individual test cases go here } ``` ```kotlin package example.order @ApplicationModuleTest class OrderIntegrationTests { // Individual test cases go here } ``` -------------------------------- ### Spring Modulith Configuration Properties Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/appendix.adoc This section details the various configuration properties available for Spring Modulith, including their default values and descriptions. ```APIDOC ## Spring Modulith Configuration Properties This section details the various configuration properties available for Spring Modulith, including their default values and descriptions. |=== |Property|Default value|Description |`spring.modulith.default-async-termination` |`true` |Whether to configure defaults for the async processing termination, namely to wait for task completion for 2 seconds. See `TaskExecutionProperties` for details. |`spring.modulith.detection-strategy` |none |The strategy to be applied to detect application modules. Can either be the class name of a custom implementation of `ApplicationModuleDetectionStrategy` or `direct-subpackages` (which is also the final fallback if nothing is configured) or `explicitly-annotated` to only select packages explicitly annotated with `@ApplicationModule` or jMolecules' `@Module`. See xref:fundamentals.adoc#customizing-modules[Customize Application Module Detection] for details. |`spring.modulith.events.completion-mode` |`update` a|How to mark an event publication as completed. The following values are supported: * `update` (default) -- Sets the completion date on the event publication entry. * `delete` -- Removes the event publication entry. Completed event publications are not available via `CompletedEventPublications`. * `archive` -- Removes the event publication entry from the primary database abstraction (table, collection or node) and creates one in a archive (a table, collection or node of same schema). For details, see xref:events.adoc#publication-registry.completion[Event Publication Completion]. |`spring.modulith.events.externalization.enabled` |`true` |Whether to enable event externalization. |`spring.modulith.events.externalization.mode` |`module-listener` |The mode of event externalization. See xref:events.adoc#externalization[Event externalizazion] for details. The following values are supported: * `module-listener` (default) -- Events are externalized through an `@ApplicationModuleListener`. * `outbox` -- Events are externalized through an actual outbox implementation. |`spring.modulith.events.externalization.serialize-externalization` |`false` |Whether to serialize event externalization to brokers. |`spring.modulith.events.jdbc.schema-initialization.enabled` |`true` |Whether to initialize the JDBC event publication schema. |`spring.modulith.events.jdbc.schema` | |The schema name for the event publication table. If not specified, the table will not be schema-qualified. |`spring.modulith.events.jdbc.use-legacy-structure` |`false` |Whether to use the legacy event publication database structure. |`spring.modulith.events.kafka.enable-json` |`true` |Whether to enable JSON support for `KafkaTemplate`. |`spring.modulith.events.mongodb.transaction-management.enabled` |`true` |Whether to automatically enable transactions for MongoDB. Requires the database to be run with a replica set. |`spring.modulith.events.neo4j.event-index.enabled` |`false` |Whether to create indexes on the Neo4j event publication event hash property. |`spring.modulith.events.rabbitmq.enable-json` |`true` |Whether to enable JSON support for `RabbitTemplate`. |`spring.modulith.events.registry-trigger-annotation` | |The fully-qualified class name of an annotation that is supposed to trigger an entry in the Event Publication Registry. By default, all methods (meta-)annotated with `@TransactionalEventListener` are considered. Set this to `@ApplicationModuleListener` if you only want to create entries in the Event Publication registry for methods annotated with that. |`spring.modulith.events.republish-outstanding-events-on-restart` |`false` |Whether to republish outstanding event publications on restarts of the application. Usually not recommended in multi-instance deployments as other instances might still be processing events. |`spring.modulith.events.staleness.check-interval` |`Duration.ofMinutes(1)` |The interval at which the xref:events.adoc#publication-registry.lifecycle.staleness[Staleness Monitor] runs to mark stale event publications as failed. |`spring.modulith.events.staleness.processing` |`Duration.ZERO` |After which duration an event publication in the processing state is considered stale. |=== ``` -------------------------------- ### Moments Configuration Properties Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-moments/readme.adoc Configuration properties available under the moduliths.moments namespace to customize the behavior of the Moments module. ```APIDOC ## Configuration Properties: moduliths.moments ### Description These properties allow customization of the Moments module behavior, including enabling/disabling the module, time machine functionality, and time zone settings. ### Properties - **enabled** (boolean) - Default: true - Whether to enable Moments in the application. - **enable-time-machine** (boolean) - Default: false - Set to true to expose TimeMachine bean to shift time. - **granularity** (string) - Default: hours - Granularity to publish events (e.g., hours, days). - **locale** (string) - Default: Locale.default() - Locale used to determine the start date of the week. - **quarter-start-month** (string) - Default: January - The month in which the first quarter starts. - **zone-id** (string) - Default: UTC - The time zone used to determine dates and times for events. ``` -------------------------------- ### Verify Application Modularity with JUnit Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/introducing-spring-modulith.adoc Use this test to verify the application's modular structure. It checks if code adheres to the defined module conventions and will fail the build if dependencies violate these rules. ```java class ModularityTests { @Test void verifyModularity() { ApplicationModules.of(Application.class).verify(); } } ``` -------------------------------- ### Wait for State Change in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Publishes an event and waits for a state change by invoking a method on a bean. Use this when the completion signal is a change in application state rather than an event. ```java scenario.publish(new MyApplicationEvent(…)) .andWaitForStateChange(() -> someBean.someMethod(…))) .andVerify(result -> …); ``` -------------------------------- ### Define a dynamic routing key via SpEL expression Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Use the @Externalized annotation with a $target::$key pattern to define dynamic routing keys using SpEL expressions. ```java @Externalized("customer-created::#{#this.getLastname()}") // <2> class CustomerCreated { String getLastname() { // <1> // … } } ``` ```kotlin @Externalized("customer-created::#{#this.getLastname()}") // <2> class CustomerCreated { fun getLastname(): String { // <1> // … } } ``` -------------------------------- ### Add Spring Modulith Runtime Dependency (Gradle) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/runtime.adoc Include the `spring-modulith-runtime` JAR in your project's Gradle dependencies to enable runtime support. ```xml dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-runtime' } ``` -------------------------------- ### Configure Explicit Module Dependencies (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Specify allowed dependencies for a module using the @ApplicationModule annotation with the 'allowedDependencies' attribute on package-info.java. This restricts module interactions. ```java @org.springframework.modulith.ApplicationModule( allowedDependencies = "order" ) package example.inventory; ```