Files

4.0 KiB

order-fulfillment

Companion code for Spring Modulith 2.1: Enforcing Module Boundaries Inside a Spring Boot Monolith on ankurm.com.

A 4-module Spring Boot monolith — order, inventory, shipping, notification — verified with Spring Modulith's ApplicationModules.verify(). Everything in the post was produced by actually running this code; see output/ for every captured transcript, in order.

Versions

Artifact Version
Spring Boot 4.1.1
Spring Modulith 2.1.1 (current GA; 2.2 exists only as milestones as of this writing — verified against Maven Central's maven-metadata.xml, not the project's own <release> tag, which has pointed at a milestone before)
JDK 25 (LTS)
H2 2.4.240 (in-memory, for the demo only)

Quickstart

mvn -pl order-fulfillment -am test

Runs the module-boundary verification, the documentation generator, and the full order → inventory → shipping → notification event-cascade integration test.

To run the app itself:

mvn -pl order-fulfillment -am spring-boot:run
curl -X POST "http://localhost:8080/orders?sku=WIDGET-1&quantity=3"
curl "http://localhost:8080/orders/1"

Modules

Module Public API Internal Listens for
order OrderManagement, OrderController internal.Order, internal.OrderRepository —
inventory InventoryManagement internal.Stock, internal.StockRepository, internal.StockSeeder OrderPlaced
shipping ShippingManagement internal.Shipment, internal.ShipmentRepository StockReserved
notification NotificationManagement internal.Notification, internal.NotificationRepository OrderPlaced, ShipmentScheduled

order has zero module dependencies — on purpose. See the Javadoc on OrderManagement for why, and output/03-cycle-via-public-api-still-failed.txt for what happens the first time that seems like a good idea to "fix" by adding one back.

Endpoints

Method Path Description
POST /orders?sku={sku}&quantity={n} Place an order
GET /orders/{id} Fetch an order

Captured output (output/)

Every number and every stack trace in the post traces to one of these, byte for byte:

File What it captures
00-table-name-collision.txt H2 rejecting CREATE TABLE order — ORDER is a reserved word
01-verify-violation-failed.txt ApplicationModules.verify() failing on a direct reference to inventory.internal.StockRepository — both a cycle and a non-exposed-type violation
02-verify-passed.txt The same test, green, after removing the dependency on inventory entirely
03-cycle-via-public-api-still-failed.txt Why routing the same call through InventoryManagement (the real public API) still fails — a cycle isn't a visibility problem
04-event-flow-missing-dependency.txt @ApplicationModuleTest(mode = ALL_DEPENDENCIES) bootstrapping zero other modules for order, because order has no outgoing dependencies, and the resulting event-cascade test timing out
05-redundant-transactional-failure.txt Stacking a plain @Transactional on top of @ApplicationModuleListener failing bean registration outright
06-event-flow-passed.txt The full order → inventory → shipping → notification cascade, fixed, with both notification hops firing
07-generated-docs.txt The real output of Documenter.writeModulesAsPlantUml().writeModuleCanvases()... — the PlantUML component diagram and per-module canvases this repo's own structure produces
08-javap-listener-meta-annotations.txt javap -v on the compiled @ApplicationModuleListener class, showing its real meta-annotations (@Async + @Transactional(REQUIRES_NEW) + @TransactionalEventListener)

No docs/ chapter directory in this repository — the intermediate and reference-depth material that would normally live there is in accordion sections inside the WordPress post itself.