Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
Spring Modulith
https://github.com/spring-projects/spring-modulith
Admin
Modular applications with Spring Boot
Tokens:
237,083
Snippets:
1,348
Trust Score:
9.7
License:
Apache-2.0
Update:
2 weeks ago
Context
Chat
Benchmark
78.8
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### Project structure for e-commerce example Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/introducing-spring-modulith.adoc Shows the package layout for the example e-commerce application with order and inventory modules. The `order.internal` sub-package is considered internal and inaccessible to other modules. ```text □ Example └─ □ src/main/java ├─ □ example | └─ ○ Application.java | ├─ □ example.inventory | ├─ ○ InventoryManagement.java | └─ - InventoryInternal.java | ├─ □ example.order | └─ ○ OrderManagement.java └─ □ example.order.internal └─ ○ OrderInternal.java ``` -------------------------------- ### Example Package Structure for Simple Application Module Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows a typical package structure for a simple application module. The main package 'example' contains the application class, and 'example.inventory' is an application module package. Public types (green) are part of the API, while package-private types (red) are hidden. ```text icon:cubes[] Example ╰─ icon:folder[] src/main/java ├─ icon:cube[] example <1> │ ╰─ icon:plus-circle[role=green] Application.java ╰─ icon:cube[] example.inventory <2> ├─ icon:plus-circle[role=green] InventoryManagement.java ╰─ icon:minus-circle[role=red] SomethingInventoryInternal.java ``` -------------------------------- ### Defining a stimulus as starting point of the Scenario (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Shows how to start a scenario with an event publication or a bean invocation. Both happen within a transaction callback, requiring a new transaction and no rollback of state changes. ```java // Start with an event publication scenario.publish(new MyApplicationEvent(…)).… // Start with a bean invocation scenario.stimulate(() -> someBean.someMethod(…)).… ``` ```kotlin // Start with an event publication scenario.publish(MyApplicationEvent(…)).… // Start with a bean invocation scenario.stimulate(Runnable { someBean.someMethod(…) }).… ``` -------------------------------- ### Example application module structure Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows a typical Spring Modulith application module layout with packages like example.inventory and example.order. The example illustrates nested modules and access rules, where nested code can access parent module internals and exposed types from top-level modules. ```text example.order ╰─ src/main/java │ ├─ example │ ╰─ Application.java │ │ -> Inventory │ ├─ example.inventory │ ├─ InventoryManagement.java │ ╰─ SomethingInventoryInternal.java ├─ example.inventory.internal │ ╰─ SomethingInventoryInternal.java │ │ -> Inventory > Nested │ ├─ example.inventory.nested │ ├─ package-info.java // @ApplicationModule │ ╰─ NestedApi.java ├─ example.inventory.nested.internal │ ╰─ NestedInternal.java │ │ -> Order │ ╰─ example.order ├─ OrderManagement.java ╰─ SomethingOrderInternal.java ``` -------------------------------- ### 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 Shows the JSON structure returned by the application modules actuator, including module dependencies and types. ```json { "a": { "basePackage": "example.a", "displayName": "A", "dependencies": [] }, "b": { "basePackage": "example.b", "displayName": "B", "dependencies": [ { "target": "a", "types": [ "EVENT_LISTENER", "USES_COMPONENT" ] } ] } } ``` -------------------------------- ### Example test directory structure Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Shows the expected test directory structure for an application module's integration tests. The test class is located under src/test/java in the module's package. ```text □ Example └─ □ src/test/java └─ □ example.order └─ OrderModuleIntegrationTests.java ``` -------------------------------- ### Module slicing with @ModuleSlicing and @DataJpaTest Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Example of a repository integration test using @ModuleSlicing and @DataJpaTest to test a module's repository in isolation. ```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() { … } } ``` -------------------------------- ### JobRunr outbox example startup and event externalization logs Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-examples/spring-modulith-example-outbox/readme.adoc Log output showing JobRunr infrastructure bootstrap, database migrations, registration of the JobRunr domain event outbox externalization to Kafka, and the processing of an OrderCompleted event. The logs include timestamps, thread names, and log levels, and illustrate the sequence from configuration to event externalization. ```text 18:15:13.978 D - main : Configuring event externalization to export events annotated with @Externalized in packages: [example] … 18:15:14.479 I - main : Creating JobRunr JsonMapper using Jackson 3 18:15:14.507 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v000__create_migrations_table.sql} 18:15:14.510 D - main : Trying to lock migrations table... 18:15:14.510 D - main : Successfully locked the migrations table. 18:15:14.510 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v001__create_job_table.sql} 18:15:14.512 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v002__create_recurring_job_table.sql} 18:15:14.513 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v003__create_background_job_server_table.sql} 18:15:14.515 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v004__create_job_stats_view.sql} 18:15:14.520 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v005__update_job_stats_view.sql} 18:15:14.522 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v006__alter_table_jobs_add_recurringjob.sql} 18:15:14.529 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v007__alter_table_backgroundjobserver_add_delete_config.sql} 18:15:14.533 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v008__alter_table_jobs_increase_jobAsJson_size.sql} 18:15:14.534 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/h2/migrations/v009__change_jobrunr_job_counters_to_jobrunr_metadata.sql} 18:15:14.536 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/h2/migrations/v010__change_job_stats.sql} 18:15:14.537 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v011__change_sqlserver_text_to_varchar.sql} 18:15:14.537 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v012__change_oracle_alter_jobrunr_metadata_column_size.sql} 18:15:14.538 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v013__alter_table_recurring_job_add_createdAt.sql} 18:15:14.539 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v014__improve_job_stats.sql} 18:15:14.540 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/common/migrations/v015__alter_table_backgroundjobserver_add_name.sql} 18:15:14.541 I - main : Running migration MigrationByPath{path=/org/jobrunr/storage/sql/h2/migrations/v016__alter_jobs_stats_add_awaiting_jobs.sql} 18:15:14.543 D - main : Removing lock on migrations table... 18:15:14.543 D - main : The lock has been removed from migrations table. … 18:15:14.726 D - main : Registering JobRunr domain event outbox externalization to Kafka. … 18:15:14.750 I - main : Triggering order completion… … 18:15:15.767 D - r-pool-1 : Submitted BackgroundJobPerformer for job 019d304a-ffc8-7931-b6e8-a3cfeb56b2ef to executor service 18:15:15.767 D - r-pool-1 : Found 1 enqueued jobs to process. … 18:15:15.770 D - b-worker : Externalizing event of type class example.order.OrderCompleted to order.OrderCompleted. 18:15:15.773 I - b-worker : Sending message GenericMessage [payload=OrderCompleted[orderId=OrderIdentifier[id=ca2a4c1a-e258-42cb-8d15-2cc79c6922ea]], headers={kafka_topic=order.OrderCompleted, id=95a5d720-f6b9-4a76-2a5a-2431d87e4a50, timestamp=1774631715772}]. 18:15:15.773 D - b-worker : Job(id=019d304a-ffc8-7931-b6e8-a3cfeb56b2ef, jobName='org.springframework.modulith.events.jobrunr.JobRunrExternalizationTransport.externalize(OrderCompleted[orderId=OrderIdentifier[id=ca2a4c1a-e258-42cb-8d15-2cc79c6922ea]])') processing succeeded 18:15:15.775 D - b-worker : Can onboard 256 new work (occupiedWorkerCount = 0; workerCount = 256). 18:15:15.776 D - b-worker : Found 0 enqueued jobs to process. … ``` -------------------------------- ### Using @ModuleSlicing for slice tests Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Example of using @ModuleSlicing to combine vertical slicing with Boot's horizontal slicing. This is the foundation of @ApplicationModuleTest. ```java package example.order; @ModuleSlicing class OrderSliceTests { // Individual test cases go here } ``` -------------------------------- ### Log output of an application module integration test bootstrap Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Example log output when running an @ApplicationModuleTest with DEBUG logging for org.springframework.modulith. Shows module details and reconfiguration of auto-configuration and entity scan packages. ```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.**] ``` -------------------------------- ### Namastack run output Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-examples/spring-modulith-example-outbox/readme.adoc Expected console output when running the test application with the Namastack profile. The log lines show event externalization setup, outbox registration, event scheduling, and processing. ```text 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> ``` -------------------------------- ### Excluding Packages from Module Inspection (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Kotlin equivalent of the Java exclusion example. It uses the same 'JavaClass.Predicates.resideInAPackage' to exclude a package from module inspection, ensuring consistent behavior across languages. ```kotlin ApplicationModules.of(Application::class.java, JavaClass.Predicates.resideInAPackage("com.example.db")).verify() ``` -------------------------------- ### DEBUG log output for @ApplicationModuleTest bootstrap Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/introducing-spring-modulith.adoc Example DEBUG log output when running an @ApplicationModuleTest with logging for org.springframework.modulith.test at DEBUG. It reports the bootstrapped module, its logical structure, and the reconfiguration of auto-configuration and entity scan packages. ```text … - 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 … … - Re-configuring auto-configuration and entity scan packages to: example.order. ``` -------------------------------- ### Customizing Scenario execution (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Adds a custom condition factory to the Scenario, e.g., to set a timeout. The customize method is called in the setup chain. ```java scenario.publish(new MyApplicationEvent(…)) **.customize(conditionFactory -> conditionFactory.atMost(Duration.ofSeconds(2)))** .andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) .toArriveAndVerify(event -> …); ``` -------------------------------- ### Using the Spring Modulith BOM in Gradle Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/index.adoc Defines the Spring Modulith BOM in a Gradle build using dependency management. Replace {projectVersion} with the desired version. This is a required setup step to use Spring Modulith features. ```groovy dependencyManagement { imports { mavenBom 'org.springframework.modulith:spring-modulith-bom:{projectVersion}' } } ``` -------------------------------- ### Generate aggregating document using Documenter in Kotlin Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Kotlin equivalent of the Java example: call Documenter.writeAggregatingDocument() in a test function. Requires an ApplicationModules instance created from the application class. The aggregating document includes existing component diagrams and canvases; if none exist, no output file is produced. ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeAggregatingDocument() } } ``` -------------------------------- ### Declaring an Application Module as Open (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Kotlin example of declaring an application module as open using the @ApplicationModule annotation with type = Type.OPEN on a class in the module's root package, since Kotlin lacks package-info.java. This allows access to internal types from other modules and includes all sub-package types in the unnamed named interface. ```kotlin package example.inventory import org.springframework.modulith.ApplicationModule import org.springframework.modulith.PackageInfo @ApplicationModule( type = Type.OPEN ) @PackageInfo class ModuleMetadata {} ``` -------------------------------- ### Explicitly configuring module dependencies (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Java example of explicitly configuring module dependencies using the @ApplicationModule annotation with allowedDependencies = "order" on the package-info.java file. This restricts the module to only access code from the 'order' module and unassigned code. ```java @org.springframework.modulith.ApplicationModule( allowedDependencies = "order" ) package example.inventory; ``` -------------------------------- ### Generate application module canvases (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Call Documenter.writeModuleCanvases() to generate application module canvases. The example shows a test class that uses ApplicationModules.of(Application.class) and writes the canvases. ```java class DocumentationTests { ApplicationModules modules = ApplicationModules.of(Application.class); @Test void writeDocumentationSnippets() { new Documenter(modules) .writeModuleCanvases(); } } ``` -------------------------------- ### Declaring an Application Module as Open (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Java example of declaring an application module as open using the @ApplicationModule annotation with type = Type.OPEN on the package-info.java file. This allows access to internal types from other modules and includes all sub-package types in the unnamed named interface. ```java @org.springframework.modulith.ApplicationModule( type = Type.OPEN ) package example.inventory; ``` -------------------------------- ### Generate application module canvases (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Call Documenter.writeModuleCanvases() to generate application module canvases. The example shows a test class that uses ApplicationModules.of(Application::class.java) and writes the canvases. ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeModuleCanvases() } } ``` -------------------------------- ### Explicitly configuring module dependencies (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Kotlin example of explicitly configuring module dependencies using the @ApplicationModule annotation with allowedDependencies = "order" on a class in the module's root package. This restricts the module to only access code from the 'order' module and unassigned code. ```kotlin package example.inventory import org.springframework.modulith.ApplicationModule @ApplicationModule(allowedDependencies = "order") class ModuleMetadata {} ``` -------------------------------- ### Using the Spring Modulith BOM in Maven Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/index.adoc Defines the Spring Modulith BOM in a Maven POM for dependency management. Replace {projectVersion} with the desired version. This is a required setup step to use Spring Modulith features. ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.modulith</groupId> <artifactId>spring-modulith-bom</artifactId> <version>{projectVersion}</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> ``` -------------------------------- ### OrderManagement component with direct dependency (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Shows a Spring service that directly invokes a method on another module's bean, creating coupling. Use this as the starting point to refactor toward event-driven interaction. ```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); } } ``` -------------------------------- ### Configure EventExternalizationConfiguration in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/events.adoc Creates a Spring bean that customizes event externalization. Starts with a default instance, then customizes event selection, mapping, headers, and route key. The select(…) call disables the application base package filter, so only the annotation is considered. ```java @Configuration class ExternalizationConfiguration { @Bean fun 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() } } ---- ``` -------------------------------- ### Using the Spring Modulith Insight starter Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Adds the Spring Modulith Insight starter to enable actuator and observability support. Requires additional dependencies for monitoring tools like Zipkin or Wavefront. ```xml <dependency> <groupId>org.springframework.modulith</groupId> <artifactId>spring-modulith-starter-insight</artifactId> <version>{projectVersion}</version> <scope>runtime</scope> </dependency> ``` ```groovy dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-starter-insight:{projectVersion}' } ``` -------------------------------- ### Create ApplicationModules model and write documentation snippets Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Creates an ApplicationModules model from the application class and verifies its structure. Then uses Documenter to write Asciidoctor snippets (component diagrams, application module canvas) to target/modulith-docs. ```java class ApplicationTests { @Test void writeDocumentationSnippets() { var modules = ApplicationModules.of(Application.class).verify(); <1> new Documenter(modules) <2> .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml(); } } ``` -------------------------------- ### Build Spring Modulith with Maven wrapper Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Compiles, tests, and builds the project using the Maven wrapper. ```bash ./mvnw -B ``` -------------------------------- ### Package arrangement for SPI named interface Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows the package structure for an application module with a named interface 'spi'. The 'example.order.spi' package contains a package-info.java and SomeSpiInterface, while 'example.order.internal' is internal. This arrangement allows other modules to depend on the named interface. ```text icon:cubes[] Example ╰─ icon:folder[] src/main/java ├─ icon:cube[] example │ ╰─ icon:plus-circle[role=green] Application.java ├─ … ├─ icon:cube[] example.order │ ╰─ icon:plus-circle[role=green] OrderManagement.java ├─ **icon:cube[] example.order.spi** │ ├— icon:coffee[] package-info.java │ ╰─ icon:plus-circle[role=green] SomeSpiInterface.java ╰─ icon:cube[] example.order.internal ╰─ icon:minus-circle[role=red] SomethingOrderInternal.java ``` -------------------------------- ### Maven release:prepare command Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/build-and-releases.adoc Run this Maven command on the version branch to prepare a release. It uses the prepare-release profile and sets release and development versions, commit comments, and the tag. The command is executed with ./mvnw release:prepare. ```bash ./mvnw release:prepare \ -Pprepare-release \ -DreleaseVersion="${version}" \ -DdevelopmentVersion="${devVersion}" \ -DscmReleaseCommitComment="${ticketId} - Release version ${version}." \ -DscmDevelopmentCommitComment="${ticketId} - Prepare next development iteration." \ -Dtag="${version}" ``` -------------------------------- ### Advanced Application Module Structure with Internal Sub-package Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Illustrates an advanced application module where 'example.order' is an API package and 'example.order.internal' is an internal sub-package. Public types in the API package are accessible, while internal types are not meant to be referenced from other modules. ```text icon:cubes[] Example ╰─ icon:folder[] src/main/java ├─ icon:cube[] example │ ╰─ icon:plus-circle[role=green] Application.java ├─ icon:cube[] example.inventory │ ├─ icon:plus-circle[role=green] InventoryManagement.java │ ╰─ icon:minus-circle[role=red] SomethingInventoryInternal.java ├─ **icon:cube[] example.order** │ ╰─ icon:plus-circle[role=green] OrderManagement.java ╰─ icon:cube[] example.order.internal ╰─ icon:plus-circle[role=green] SomethingOrderInternal.java ``` -------------------------------- ### Registering a ScenarioCustomizer (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Kotlin version of the ScenarioCustomizer implementation, using a class and overriding the customizer method. ```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<ConditionFactory> { return UnaryOperator { conditionFactory -> … } } } } ``` -------------------------------- ### package-info.java in example.order.spi (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Declares the named interface 'spi' in the example.order.spi package. The Java version uses @NamedInterface on the package-info.java file; the Kotlin version uses @PackageInfo and @NamedInterface on a ModuleMetadata class. This allows other modules to depend on the named interface. ```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 {} ``` -------------------------------- ### Using the Spring Modulith actuator support Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Adds the Spring Modulith actuator dependency and Spring Boot actuator starter. Required to expose the modulith actuator resource. ```xml <dependency> <groupId>org.springframework.modulith</groupId> <artifactId>spring-modulith-actuator</artifactId> <version>{projectVersion}</version> <scope>runtime</scope> </dependency> <!-- Spring Boot actuator starter required to enable actuators in general --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>…</version> <scope>runtime</scope> </dependency> ``` ```groovy dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-actuator:{projectVersion}' } <!-- Spring Boot actuator starter required to enable actuators in general --> dependencies { runtimeOnly 'org.springframework.boot:spring-boot-starter-actuator' } ``` -------------------------------- ### Print application module arrangement to console Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Writes the individual modules contained in the application module model to the console. The output shows the analyzed arrangement of the application modules. ```java modules.forEach(System.out::println); ``` ```kotlin modules.forEach { println(it) } ``` -------------------------------- ### Package arrangement for application modules Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Shows the expected package structure where business modules are direct sub-packages of the application's main package. The root package is the application root and sub-packages are application modules. ```text □ Example └─ □ src/main/java ├─ □ example <1> | └─ Application.java ├─ □ example.inventory <2> | └─ … └─ □ example.order <2> └─ … ``` -------------------------------- ### Error message when module depends on non-exposed type Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/introducing-spring-modulith.adoc Example error message produced when `InventoryManagement` introduces a dependency on `OrderInternal`. The message indicates the module 'inventory' depends on non-exposed type `….internal.OrderInternal` within module 'order'. ```text - Module 'inventory' depends on non-exposed type ….internal.OrderInternal within module 'order'! InventoryManagement declares constructor InventoryManagement(InventoryInternal, OrderInternal) in (InventoryManagement.java:0) ``` -------------------------------- ### Registering a ScenarioCustomizer (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Implements ScenarioCustomizer and registers it as a JUnit extension to globally customize all Scenario instances in a test class. ```java @ExtendWith(MyCustomizer.class) class MyTests { @Test void myTestCase(Scenario scenario) { // scenario will be pre-customized with logic defined in MyCustomizer } static class MyCustomizer implements ScenarioCustomizer { @Override Function<ConditionFactory, ConditionFactory> getDefaultCustomizer(Method method, ApplicationContext context) { return conditionFactory -> …; } } } ``` -------------------------------- ### Customizing Scenario execution (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Kotlin version of the customized Scenario, using a trailing lambda for the customizer. ```kotlin scenario.publish(MyApplicationEvent(…)) **.customize { it.atMost(Duration.ofSeconds(2)) }** .andWaitForEventOfType(SomeOtherEvent::class.java) .matching { event -> … } .toArriveAndVerify { event -> … } ``` -------------------------------- ### Scenario definition with event publication (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Kotlin version of the Scenario definition, using trailing lambdas for matching and verification. ```kotlin scenario.publish(new MyApplicationEvent(…)) .andWaitForEventOfType(SomeOtherEvent::class.java) .matching { event -> … } .toArriveAndVerify { event -> … } ``` -------------------------------- ### Scenario expecting state change (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Kotlin version of the state-change scenario, using trailing lambdas. ```kotlin scenario.publish(MyApplicationEvent(…)) .andWaitForStateChange { someBean.someMethod(…) } .andVerify { result -> … } ``` -------------------------------- ### Implement application module initializer in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/runtime.adoc A Java class that implements the ApplicationModuleInitializer interface in the Spring Modulith runtime. Mark the bean with @Component to have the initializer run on the application startup according to the module dependency order. ```java @Component class MyInitializer implements ApplicationModuleInitializer { @Override public void initialize() { // Initialization code goes here } } ``` -------------------------------- ### Add spring-modulith-observability-core dependency (Gradle) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Gradle dependency to activate Spring Modulith observability support. Add unless already using spring-modulith-starter-insight. ```groovy dependencies { runtimeOnly 'org.springframework.modulith:spring-modulith-observability-core:{projectVersion}' } ``` -------------------------------- ### An application module integration test class (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Place a JUnit test class in an application module package and annotate it with @ApplicationModuleTest to bootstrap only that module. The Java and Kotlin versions are equivalent. ```java package example.order; @ApplicationModuleTest class OrderIntegrationTests { // Individual test cases go here } ``` ```kotlin package example.order @ApplicationModuleTest class OrderIntegrationTests { // Individual test cases go here } ``` -------------------------------- ### Expecting an event being published as operation result (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Shows how to set up a completion criteria by expecting an event of a particular type, optionally with a matcher. The execution will block until the event is published or the default timeout is reached. ```java ….andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) // Use some predicate here .… ``` ```kotlin ….andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) // Use some predicate here .… ``` -------------------------------- ### Add spring-modulith-observability-core dependency (Maven) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/production-ready.adoc Maven dependency to activate Spring Modulith observability support. Add unless already using spring-modulith-starter-insight. ```xml <dependency> <groupId>org.springframework.modulith</groupId> <artifactId>spring-modulith-observability-core</artifactId> <version>{projectVersion}</version> <scope>runtime</scope> </dependency> ``` -------------------------------- ### Generate C4 component diagrams with Documenter (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Use this Java test to generate C4 component diagrams for all application modules and individual module diagrams. The first call writes a diagram of all modules, the second writes diagrams for each module and its direct dependencies. ```java class DocumentationTests { ApplicationModules modules = ApplicationModules.of(Application.class); @Test void writeDocumentationSnippets() { new Documenter(modules) .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml(); } } ``` -------------------------------- ### Using the asterisk to declare allowed dependencies to all declared named interfaces (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows how to use the asterisk wildcard to allow dependencies to all declared named interfaces. The Java version uses @ApplicationModule with allowedDependencies = "order :: *" on the package declaration; the Kotlin version uses @ApplicationModule and @PackageInfo on a ModuleMetadata class. ```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 {} ``` -------------------------------- ### Scenario definition with event publication (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Defines a complete Scenario that publishes an event, waits for a specific event type, matches it, and verifies arrival. Use when testing event-driven behavior. ```java scenario.publish(new MyApplicationEvent(…)) .andWaitForEventOfType(SomeOtherEvent.class) .matching(event -> …) .toArriveAndVerify(event -> …); ``` -------------------------------- ### Triggering the verification with toArrive Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Shows the terminal operation to execute the event-based Scenario, allowing optional access to the expected event or result object. ```java // Executes the scenario ….toArrive(…) ``` -------------------------------- ### Generate C4 component diagrams with Documenter (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Kotlin version of the test that generates C4 component diagrams for all application modules and individual module diagrams. ```kotlin class DocumentationTests { private val modules = ApplicationModules.of(Application::class.java) @Test fun writeDocumentationSnippets() { Documenter(modules) .writeModulesAsPlantUml() .writeIndividualModulesAsPlantUml() } } ``` -------------------------------- ### C4 component diagram of all modules (PlantUML) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc PlantUML source for the C4 component diagram showing all application modules and their relationships, generated by the first Documenter call. ```plantuml top to bottom direction !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml Container_Boundary("Modulith.Application_boundary", "Application") { Component(Modulith.Application.core, "core", "Module", "", $tags="") Component(Modulith.Application.catalog, "catalog", "Module", "", $tags="") Component(Modulith.Application.inventory, "inventory", "Module", "", $tags="") Component(Modulith.Application.order, "order", "Module", "", $tags="") Component(Modulith.Application.customer, "customer", "Module", "", $tags="") } Rel_D(Modulith.Application.order, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.customer, "uses", $tags="") Rel_D(Modulith.Application.catalog, Modulith.Application.core, "depends on", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.order, "listens to", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.catalog, "uses", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.order, "uses", $tags="") Rel_D(Modulith.Application.inventory, Modulith.Application.core, "uses", $tags="") Rel_D(Modulith.Application.order, Modulith.Application.catalog, "depends on", $tags="") SHOW_LEGEND() ``` -------------------------------- ### Customize Verification with VerificationOptions and JMoleculesArchitectureRules Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/verification.adoc Customizes the verification by adding a jMolecules Architecture rule for Hexagonal Architecture in strict mode. The VerificationOptions instance replaces the default verification with the one set up, and the verify() method is called with those options. ```java var hexagonal = JMoleculesArchitectureRules.ensureHexagonal(VerificationDepth.STRICT); <1> var options = VerificationOptions.defaults().withAdditionalVerifications(hexagonal); <2> ApplicationModules.of(…).verify(options); <3> ``` -------------------------------- ### Configure UML style for diagrams (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Use DiagramOptions.defaults().withStyle(DiagramStyle.UML) to switch the default component diagram style to UML. This is the Java variant shown in the tabs. ```java DiagramOptions.defaults() .withStyle(DiagramStyle.UML); ``` -------------------------------- ### Implement application module initializer in Kotlin Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/runtime.adoc A Kotlin class that implements the ApplicationModuleInitializer interface in the Spring Modulith runtime. The bean is declared with @Component and its initialize() method runs on startup following the dependency order. ```kotlin @Component class MyInitializer : ApplicationModuleInitializer { override fun initialize() { // Initialization code goes here } } ``` -------------------------------- ### Defining allowed dependencies to dedicated named interfaces (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows how to declare allowed dependencies to a named interface using the 'order :: spi' syntax. The Java version uses @ApplicationModule on the package declaration; the Kotlin version uses @ApplicationModule and @PackageInfo on a ModuleMetadata class. This allows the inventory module to depend on the 'spi' named interface. ```java @org.springframework.modulith.ApplicationModule( allowedDependencies = "order :: spi" ) package example.inventory; ``` ```kotlin package example.inventory import org.springframework.modulith.ApplicationModule import org.springframework.modulith.PackageInfo @ApplicationModule( allowedDependencies = "order :: spi" ) @PackageInfo class ModuleMetadata {} ``` -------------------------------- ### Customizing the Application Modules Arrangement (Java and Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Shows how to use the @Modulithic annotation on the main Spring Boot application class to customize the application module arrangement. The Java version uses @Modulithic and @SpringBootApplication on the class; the Kotlin version uses @Modulithic and @SpringBootApplication with a runApplication call in the main function. ```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<String>) { runApplication<MyApplication>(*args) } ``` -------------------------------- ### Clone the Spring Modulith repository Source: https://github.com/spring-projects/spring-modulith/blob/main/readme.adoc Clones the Spring Modulith repository from GitHub and changes into the project directory. ```bash git clone git@github.com:spring-projects/spring-modulith.git cd spring-modulith ``` -------------------------------- ### Verify Application Module Structure with ApplicationModules.verify() Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/verification.adoc Calls the verify() method on an ApplicationModules instance to check that the code arrangement adheres to the intended constraints. The verification rules include no cycles on the application module level, efferent module access via API packages only, and optionally explicit allowed dependencies via @ApplicationModule(allowedDependencies = …). ```java ApplicationModules.of(Application.class).verify(); ``` ```kotlin ApplicationModules.of(Application::class.java).verify() ``` -------------------------------- ### Add Spring Modulith test starter dependency Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/testing.adoc Add the spring-modulith-starter-test dependency with test scope to enable @ApplicationModuleTest and related testing support. ```xml <dependency> <groupId>org.springframework.modulith</groupId> <artifactId>spring-modulith-starter-test</artifactId> <scope>test</scope> </dependency> ``` -------------------------------- ### Excluding Packages from Module Inspection (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Use this Java snippet to exclude a package (e.g., 'com.example.db') from application module inspection. The predicate 'resideInAPackage' matches all files in the given package. This is useful for ignoring infrastructure or database-related classes. ```java ApplicationModules.of(Application.class, JavaClass.Predicates.resideInAPackage("com.example.db")).verify(); ``` -------------------------------- ### Custom ApplicationModuleDetectionStrategy (Java) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Implements ApplicationModuleDetectionStrategy to explicitly annotate modules and define module base packages. The getApplicationModuleDetectionStrategy method returns explicitlyAnnotated() and getModuleBasePackages returns a list of base packages. The returned package names are translated into ApplicationModuleSource instances. ```java @Override public ApplicationModuleDetectionStrategy getApplicationModuleDetectionStrategy() { return ApplicationModuleDetectionStrategy.explicitlyAnnotated(); } @Override public List<String> getModuleBasePackages() { return List.of("com.acme.module"); } } ``` -------------------------------- ### Verify modular structure with ApplicationModules Source: https://github.com/spring-projects/spring-modulith/blob/main/etc/introducing-spring-modulith.adoc Test case that creates an `ApplicationModules` instance and verifies the application's modular structure. If a module depends on a non-exposed type, the test fails with an error message and breaks the build. ```java class ModularityTests { @Test void verifyModularity() { ApplicationModules.of(Application.class).verify(); } } ``` -------------------------------- ### Implement custom ApplicationModuleDetectionStrategy in Java Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Provides a Java implementation of ApplicationModuleDetectionStrategy to customize module base package detection. The getModuleBasePackages method should return a stream of JavaPackage objects. ```java package example; class CustomApplicationModuleDetectionStrategy implements ApplicationModuleDetectionStrategy { @Override public Stream<JavaPackage> getModuleBasePackages(JavaPackage basePackage) { // Your module detection goes here } } ``` -------------------------------- ### Expected log output when running the test application Source: https://github.com/spring-projects/spring-modulith/blob/main/spring-modulith-examples/spring-modulith-example-kafka/readme.adoc Log output produced by running ./mvnw spring-boot:test-run. It shows the registration of domain event externalization, triggering order completion, publication registration, externalization, sending the message, and marking the publication completed. ```text 22:20:20.398 D - main : Registering domain event externalization to Kafka… <1> … 22:20:21.267 I - main : Triggering order completion… <2> 22:20:21.277 D - main : Registering publication of example.order.OrderCompleted for org.springframework.modulith.events.support.EventExternalizerModuleListener.externalize(java.lang.Object). <3> 22:20:21.325 D - task-1 : Externalizing event of type class example.order.OrderCompleted to RoutingTarget[value=order.OrderCompleted]. <4> 22:20:21.327 I - task-1 : Sending message {"orderId":{"id":"ef3521e8-d498-4539-8745-3a1c74bbe90d"}} to RoutingTarget[value=order.OrderCompleted]. <5> 22:20:21.376 D - task-1 : Marking publication of event example.order.OrderCompleted to listener org.springframework.modulith.events.support.EventExternalizerModuleListener.externalize(java.lang.Object) completed. <6> ``` -------------------------------- ### Implement custom ApplicationModuleSourceFactory Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Provides a Java implementation of ApplicationModuleSourceFactory that returns root packages for module detection. The getRootPackages method should return a list of package names. ```java package example; public class CustomApplicationModuleSourceFactory implements ApplicationModuleSourceFactory { @Override public List<String> getRootPackages() { return List.of("com.acme.toscan"); } } ``` -------------------------------- ### Create an application module model with ApplicationModules.of Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/fundamentals.adoc Creates an in-memory application module model from a Spring Boot application class using the ApplicationModules type from spring-modulith-core. The detected modules depend on the Java package structure underneath the package of the given class. ```java var modules = ApplicationModules.of(Application.class); ``` ```kotlin val modules = ApplicationModules.of(Application::class.java) ``` -------------------------------- ### Configure UML style for diagrams (Kotlin) Source: https://github.com/spring-projects/spring-modulith/blob/main/src/docs/antora/modules/ROOT/pages/documentation.adoc Use DiagramOptions.defaults().withStyle(DiagramStyle.UML) to switch the default component diagram style to UML. This is the Kotlin variant shown in the tabs. ```kotlin DiagramOptions.defaults() .withStyle(DiagramStyle.UML) ```