Overview
Spring Modulith Order Platform is a production-oriented order management backend. It is deployed as one Spring Boot application while keeping customers, pricing, orders, payments and notifications as explicit business modules.
Spring Modulith verifies allowed module dependencies and named public interfaces. Inside each module, a pragmatic ports-and-adapters approach keeps domain rules independent, lets application services orchestrate use cases and confines JPA and Spring Data details to infrastructure adapters.
Scope: this guide covers operation and architecture. The generated OpenAPI documentation remains the authoritative REST contract.
Prerequisites
The Maven wrapper is versioned with the repository, so a separate Maven installation is not required.
- Java
- JDK 21 available through
JAVA_HOMEand the command line. - Containers
- Docker Engine or Docker Desktop with Docker Compose v2.
- Maven
./mvnwon Unix-like systems or.\mvnw.cmdon Windows.- Port 5432
- PostgreSQL host port; configurable through
DB_PORT. - Port 8080
- Default HTTP port for the Spring Boot application.
Local setup
-
Start PostgreSQL
Launch the PostgreSQL 17 service and wait for its health check.
docker compose up -d -
Verify the project
Generate the API contract, compile, run unit and PostgreSQL integration tests, verify module boundaries and assemble all documentation under
target/pages../mvnw clean verify -
Run the application
Start the verified application against the Compose database. The API listens on port
8080../mvnw spring-boot:run
On Windows PowerShell, replace ./mvnw with .\mvnw.cmd. The local database name, username and password are all order_platform.
API workflow
The realistic happy path crosses every business module while preserving the distinction between synchronous validation and event-driven follow-up work.
- Create customer Registers an active customer and returns its UUID.
- Request quote Prices a basket from the seeded catalog.
- Create order Validates the customer and products, then submits the order.
- Authorize payment Matches the order total and authorizes its pending payment.
- Inspect notifications Lists intents created from order and payment events.
The seed catalog includes SKU-COFFEE-MUG at 14.99 EUR, SKU-NOTEBOOK at 19.99 EUR, SKU-DESK-LAMP at 49.99 EUR and SKU-BACKPACK at 89.00 EUR.
API examples
These requests match the field names, paths and payloads in the source OpenAPI contract. Run them after the application starts. The examples use POSIX line continuation. In PowerShell, invoke curl.exe and run each request on one line or replace each trailing backslash with a backtick.
1. Create a customer
A successful request returns 201 Created. Keep the response id for order creation.
curl -i -X POST http://localhost:8080/customers \
-H "Content-Type: application/json" \
-d '{
"email": "ada.lovelace@example.com",
"fullName": "Ada Lovelace"
}'
2. Quote the basket
This seeded basket returns a total of 49.97 EUR.
curl -i -X POST http://localhost:8080/pricing/quote \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "productCode": "SKU-COFFEE-MUG", "quantity": 2 },
{ "productCode": "SKU-NOTEBOOK", "quantity": 1 }
]
}'
3. Create the order
Replace <customer-id> with the UUID returned by the first request. A successful request returns 201 Created and an order id.
curl -i -X POST http://localhost:8080/orders \
-H "Content-Type: application/json" \
-d '{
"customerId": "<customer-id>",
"items": [
{ "productCode": "SKU-COFFEE-MUG", "quantity": 2 },
{ "productCode": "SKU-NOTEBOOK", "quantity": 1 }
]
}'
4. Authorize payment
Replace <order-id> with the submitted order UUID. The amount and currency must match the order total.
curl -i -X POST http://localhost:8080/payments/authorize \
-H "Content-Type: application/json" \
-d '{
"orderId": "<order-id>",
"amount": {
"amount": 49.97,
"currency": "EUR"
}
}'
5. Inspect notifications
The list contains records for the committed order and authorized payment.
curl -i http://localhost:8080/notifications
Validation, missing resources, duplicate customers and business-rule failures use RFC 7807 application/problem+json responses.
Architecture
The modules are direct packages below com.example.orderplatform. Each module uses only the api, application, domain and infrastructure packages it needs.
OrderCreatedEvent and authorizes the pending amount.
Dependency boundaries
- Named
apiinterfaces are the only synchronous cross-module surface. - Domain events handle meaningful post-commit collaboration without a message broker.
- Domain packages remain independent of Spring, JPA, generated API types and infrastructure.
- Application services orchestrate use cases through outbound ports where persistence is a real technical boundary.
- JPA entities, Spring Data repositories and persistence adapters remain private to
infrastructure.
Testing and quality
./mvnw clean verify is the project quality gate and the only Maven build executed by CI.
- Domain unit tests cover business rules and value objects without framework infrastructure.
- Application service tests verify use-case orchestration, conflicts and module API collaboration.
- Integration tests use Testcontainers with PostgreSQL for REST behavior, Flyway migrations, persistence and event consumers.
- Architecture tests run
ApplicationModules.verify()and ArchUnit rules for module and package boundaries. - JaCoCo generates coverage for non-generated production code.
- Javadoc and OpenAPI are generated and checked before Pages assembly.
- Artifact checks fail the build when a required Pages entry point or shared site asset is missing.
H2 is intentionally excluded. Using PostgreSQL in local development and integration tests keeps migration and persistence behavior aligned.
Generated documentation
Maven assembles the complete static site under target/pages. GitHub Pages publishes that directory without running the Spring Boot application or PostgreSQL.
target/pages/
|-- index.html
|-- assets/
|-- user-guide/index.html
|-- openapi/index.html
|-- openapi/openapi.json
|-- javadoc/index.html
`-- jacoco/index.html
Everything under target is generated output and must remain outside Git.
Troubleshooting
- Docker or PostgreSQL is not running
- Start Docker, run
docker compose up -d, then inspectdocker compose psanddocker compose logs postgres. - Port 5432 is occupied
- Use
DB_PORT=15432 docker compose up -don Unix or set$env:DB_PORT="15432"first in PowerShell. Pointspring.datasource.urlto the selected host port. - Port 8080 is occupied
- Set
SERVER_PORT=8081before starting Spring Boot, or stop the process already bound to port 8080. - Testcontainers cannot start Docker
- Confirm the Docker daemon is reachable by the current user and that
docker infosucceeds before rerunning Maven. - Flyway reports a validation error
- Do not rewrite an applied migration. Restore the original migration and add the next versioned migration for schema changes.
- Generated documentation is missing
- Run
./mvnw clean verify. Do not create or edit files manually undertarget/pages. - The Maven wrapper is not executable
- On Unix-like systems run
chmod +x mvnw, then rerun the wrapper command.