User Guide
Overview
This guide explains how to run, verify, and inspect the modular monolith locally. It is intentionally operational: the README explains what the project demonstrates, while this page covers day-to-day commands and troubleshooting.
Requirements
- Java 21
- Maven 3.9+
- Docker with Docker Compose
On Windows, use Docker Desktop with the WSL 2 based engine and Linux containers. Run all commands from the repository root.
Check the local toolchain:
java -version
mvn -version
docker version
docker compose version
Local Setup
Clone the repository, then run commands from the repository root. The project has one Maven wrapper at the root because all modules are built together. The commands below use an installed Maven binary; ./mvnw on Linux/macOS or .\mvnw.cmd on Windows provides the equivalent repository-pinned entry point.
Useful module map:
ecommerce-app executable Spring Boot application
shared-kernel small shared domain abstractions
catalog products, stock, read model, Redis cache
orders order placement, lookup, event publication
payment payment event handling and persistence
coverage-report aggregate JaCoCo report module
Running Infrastructure
Start PostgreSQL and Redis, then wait for both health checks:
docker compose up -d --wait
Stop infrastructure and remove development volumes:
docker compose down -v
The Docker Compose credentials are for local development only.
Running the Application
Start infrastructure first, then run the application:
docker compose up -d
mvn -pl ecommerce-app -am spring-boot:run
The application starts on:
http://localhost:8080
Runtime API documentation:
- Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs
Running Tests
Run the full verification suite:
mvn clean verify
With Docker available, this runs unit tests, architecture tests, and Testcontainers integration tests against PostgreSQL and Redis.
Run faster unit and architecture tests only:
mvn test
If Docker is not available, Testcontainers integration tests are skipped by the existing disabledWithoutDocker = true configuration. That is useful for quick local checks, but it is not a full integration-test signal.
Generating OpenAPI
OpenAPI generation starts the application through Maven, reads the runtime API document, and writes a generated JSON file under target.
docker compose up -d --wait
mvn -pl ecommerce-app -am -Pgenerate-openapi -DskipTests verify
docker compose down -v
Generated output:
ecommerce-app/target/generated-docs/openapi.json
OpenAPI metadata, paths, Swagger UI path, and API grouping are configured in:
ecommerce-app/src/main/resources/openapi.yaml
Generating JavaDoc
Generate aggregate JavaDoc:
mvn -DskipTests package javadoc:aggregate
Generated output:
target/reports/apidocs/index.html
Generating Coverage Reports
Run verification:
mvn clean verify
Generated aggregate JaCoCo output:
coverage-report/target/site/jacoco-aggregate/index.html
Generating Test Reports
After running tests, generate human-readable Surefire and Failsafe reports:
mvn surefire-report:report-only surefire-report:failsafe-report-only
Generated module report examples:
catalog/target/reports/surefire.html
ecommerce-app/target/reports/failsafe.html
API Examples
List products:
curl http://localhost:8080/api/products
Fetch one product:
curl http://localhost:8080/api/products/1
Place an order:
curl -X POST http://localhost:8080/api/orders \
-H "Idempotency-Key: checkout-001" \
-H "Content-Type: application/json" \
-d '{"productId":1,"quantity":2}'
Fetch an order:
curl http://localhost:8080/api/orders/<order-id>
Fetch payment result:
curl http://localhost:8080/api/payments/<order-id>
Idempotency Behavior
Order placement accepts an Idempotency-Key header.
- Repeating the same request with the same key returns the original order with
200 OK. - A repeated request with the same key does not reserve stock again.
- A repeated request with the same key does not create another payment attempt.
- Reusing the same key for a different product or quantity returns
409 Conflict.
This keeps HTTP retries safe without adding distributed transaction machinery.
Error Handling
Example insufficient stock response:
{
"code": "INSUFFICIENT_STOCK",
"message": "Insufficient stock for product 1"
}
Expected API behavior includes:
- invalid quantity returns a validation error
- unknown product returns a not-found response
- insufficient stock returns a domain error
- conflicting idempotency key reuse returns
409 Conflict
Use the OpenAPI UI for the published endpoint reference.
Troubleshooting
Docker Not Running
Symptoms:
docker versioncannot connect to the daemon- Testcontainers integration tests are skipped
- OpenAPI generation cannot start PostgreSQL or Redis
Fix:
- Start Docker Desktop.
- Confirm it uses Linux containers.
- Run
docker versionanddocker compose version. - Re-run
mvn clean verify.
Testcontainers Issues
Testcontainers needs a reachable Docker daemon. On Windows, Docker Desktop should use the WSL 2 based engine. If integration tests are skipped, verify Docker first before debugging the test code.
Useful checks:
docker ps
docker info
PostgreSQL Issues
If the application cannot connect to PostgreSQL:
- Start infrastructure with
docker compose up -d --wait. - Check container health with
docker compose ps. - Restart local infrastructure with
docker compose down -vanddocker compose up -d --wait.
Flyway owns schema creation. Do not switch Hibernate to ddl-auto=update.
Redis Issues
If product query caching fails locally:
- Verify Redis is running with
docker compose ps. - Restart infrastructure with
docker compose down -vanddocker compose up -d --wait. - Re-run the relevant application or integration test.
Redis is used only for read caching; PostgreSQL remains the source of truth.
OpenAPI Generation Issues
OpenAPI generation requires the application to start successfully. If it fails:
- Start infrastructure with
docker compose up -d --wait. - Verify port
8080is free. - Run
mvn -pl ecommerce-app -am -Pgenerate-openapi -DskipTests verify. - Check
ecommerce-app/target/generated-docs/openapi.json.
Port Conflicts
The local defaults are 8080 for Spring Boot, 5432 for PostgreSQL, and 6379 for Redis. If startup reports that an address is already in use:
- Run
docker compose psto check whether this project’s containers already own the port. - Stop an older project stack before starting this one.
- Check local processes using the reported port before changing repository configuration.
- Keep the documented defaults when possible so application, Compose, tests, and OpenAPI generation remain aligned.