A modern, opinionated, multi-module Spring Boot 4 microservice skeleton.
Clone it. Rename it. Ship it.
skeletoni is a production-ready starting point for Java microservices that need to be built right from day one. It combines event-driven architecture, CQRS, multi-protocol communication (REST + gRPC + Messaging), full observability, contract-first API design, and a clean hexagonal multi-module structure — so you spend zero time wiring and all your time building.
┌──────────────────────────────────────────────────────────────────────────┐
│ skeletoni │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ ┌────────────────┐ │
│ │ contract │ │ application │ │ domain │ │ infrastructure │ │
│ │ │──▶│ │──▶│ │◀──│ │ │
│ │ OpenAPI 3 │ │ Use cases │ │ Entities │ │ PostgreSQL │ │
│ │ AsyncAPI 3 │ │ CQRS cmds │ │ Aggreg. │ │ MongoDB │ │
│ │ gRPC proto │ │ CQRS queries│ │ Events │ │ Couchbase │ │
│ │ REST DTOs │ │ Schedulers │ │ Services │ │ Kafka │ │
│ │ Event DTOs │ │ Ports │ │ │ │ RabbitMQ │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │ gRPC stubs │ │
│ │ Resilience4j │ │
│ ┌──────────────────┐ ┌──────────────────────────┐ └────────────────┘ │
│ │ logging │ │ observability │ │
│ │ │ │ │ │
│ │ SLF4J + Logback │ │ Micrometer + Prometheus │ │
│ │ MDC Enrichment │ │ Grafana Dashboards │ │
│ │ Correlation IDs │ │ Actuator Health/Metrics │ │
│ │ Structured JSON │ │ Distributed Tracing │ │
│ └──────────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
| Concern | Technology |
|---|---|
| Language | Java 21 (Virtual Threads / Project Loom) |
| Framework | Spring Boot 4.0 |
| Build | Maven (multi-module) |
| REST API Docs | SpringDoc OpenAPI 3 + Swagger UI |
| Async API Docs | AsyncAPI 3 (Kafka + RabbitMQ event contracts) |
| RPC | gRPC (Protocol Buffers) |
| Messaging | Apache Kafka + RabbitMQ |
| Relational DB | PostgreSQL + Flyway |
| Document DB | MongoDB |
| Key-Value / Doc | Couchbase |
| Resiliency | Resilience4j (Circuit Breaker, Retry, Rate Limiter, Bulkhead) |
| Mapping | MapStruct |
| Boilerplate | Lombok |
| Testing | JUnit 5 + Mockito + Testcontainers |
| Observability | Micrometer + Prometheus + Grafana |
| Logging | SLF4J + Logback (structured JSON) |
| Scheduling | Spring @Scheduled + Virtual Thread executor |
| Code Quality | SonarCloud |
| CI/CD | GitHub Actions |
| Containerization | Docker + Docker Compose |
| Architecture | CQRS + Event-Driven (Hexagonal) |
skeletoni/
├── pom.xml # Parent POM — BOM + plugin management
│
├── contract/ # Public surface area — all API contracts
│ ├── pom.xml
│ ├── src/main/proto/ # Protobuf definitions for gRPC
│ ├── src/main/resources/
│ │ ├── openapi/
│ │ │ └── openapi.yml # OpenAPI 3 REST spec
│ │ └── asyncapi/
│ │ └── asyncapi.yml # AsyncAPI 3 event/messaging spec
│ └── src/main/java/.../contract/
│ ├── rest/ # REST request/response DTOs
│ └── event/ # Published & consumed event schemas
│
├── application/ # Orchestration layer — no framework deps
│ ├── pom.xml
│ └── src/main/java/.../application/
│ ├── command/ # CQRS write side
│ │ ├── CreateExampleCommand.java
│ │ └── CreateExampleCommandHandler.java
│ ├── query/ # CQRS read side
│ │ ├── GetExampleQuery.java
│ │ └── GetExampleQueryHandler.java
│ ├── scheduler/ # @Scheduled jobs (cron externalized)
│ │ └── ExampleScheduler.java
│ └── port/ # Inbound & outbound port interfaces
│ ├── in/
│ │ └── ExampleUseCase.java
│ └── out/
│ ├── ExampleRepository.java
│ └── ExampleEventPublisher.java
│
├── domain/ # Pure business logic — zero deps
│ ├── pom.xml
│ └── src/main/java/.../domain/
│ ├── model/ # Aggregates, entities, value objects
│ │ ├── Example.java
│ │ └── ExampleId.java
│ ├── event/ # Domain events
│ │ └── ExampleCreatedEvent.java
│ └── service/ # Domain services
│ └── ExampleDomainService.java
│
├── infrastructure/ # All framework & I/O adapters
│ ├── pom.xml
│ └── src/main/java/.../infrastructure/
│ ├── postgres/ # Spring Data JPA + Flyway
│ │ ├── ExampleJpaEntity.java
│ │ ├── ExampleJpaRepository.java
│ │ └── ExamplePostgresAdapter.java
│ ├── mongodb/ # Spring Data MongoDB (read models)
│ │ ├── ExampleDocument.java
│ │ └── ExampleMongoAdapter.java
│ ├── couchbase/ # Spring Data Couchbase
│ │ ├── ExampleCouchbaseEntity.java
│ │ └── ExampleCouchbaseAdapter.java
│ ├── kafka/ # Kafka producers & consumers
│ │ ├── ExampleKafkaProducer.java
│ │ └── ExampleKafkaConsumer.java
│ ├── rabbitmq/ # RabbitMQ publishers & listeners
│ │ ├── ExampleRabbitPublisher.java
│ │ └── ExampleRabbitListener.java
│ ├── grpc/ # gRPC server implementation
│ │ └── ExampleGrpcService.java
│ ├── resilience/ # Resilience4j decorators & config
│ │ └── ResilienceConfig.java
│ ├── config/ # Spring beans & integration config
│ │ ├── KafkaConfig.java
│ │ ├── RabbitMQConfig.java
│ │ ├── MongoConfig.java
│ │ └── SchedulerConfig.java
│ └── src/main/resources/
│ ├── application.yml
│ ├── application-local.yml
│ └── db/migration/ # Flyway scripts (V1__init.sql ...)
│
├── logging/ # Cross-cutting structured logging
│ ├── pom.xml
│ └── src/main/java/.../logging/
│ ├── MdcContextFilter.java # Servlet filter — injects correlationId
│ ├── CorrelationIdInterceptor.java # Propagates correlation across HTTP calls
│ ├── KafkaMdcConsumerInterceptor.java # MDC propagation for Kafka consumers
│ └── LoggingAutoConfiguration.java # Spring auto-config entry point
│
└── observability/ # Metrics, tracing, health
├── pom.xml
├── dashboards/
│ └── skeletoni-dashboard.json # Pre-built Grafana dashboard
└── src/main/java/.../observability/
├── MetricsConfig.java # Custom Micrometer meters
├── HealthConfig.java # Custom health indicators
└── TracingConfig.java # Micrometer Tracing config
- Java 21+
- Maven 3.9+
- Docker & Docker Compose
git clone https://github.com/your-org/skeletoni.git my-service
cd my-serviceDo a project-wide find & replace of skeletoni → your service name in:
- All
pom.xmlfiles (artifactId,name,groupId) application.yml(spring.application.name)- All Java package names under
src/ - Proto package declarations in
.protofiles asyncapi.ymlandopenapi.ymlinfo blocks
docker compose up -dThis brings up: PostgreSQL, MongoDB, Couchbase, Kafka + Kafka UI, RabbitMQ Management, Prometheus, and Grafana.
./mvnw spring-boot:run -pl infrastructureThe service starts on http://localhost:8080.
| Interface | URL | Description |
|---|---|---|
| Swagger UI | http://localhost:8080/swagger-ui.html | Interactive REST explorer |
| OpenAPI JSON | http://localhost:8080/v3/api-docs | OpenAPI 3 machine-readable spec |
| AsyncAPI spec | contract/src/main/resources/asyncapi/asyncapi.yml |
Kafka + RabbitMQ event contracts |
| AsyncAPI Studio | https://studio.asyncapi.com | Paste the spec to visualize |
| gRPC | localhost:9090 |
Protobuf-based RPC |
| Kafka UI | http://localhost:8081 | Browse topics, messages, consumer lag |
| RabbitMQ Mgmt | http://localhost:15672 | Exchanges, queues, bindings (guest/guest) |
Commands and queries are strictly separated at the application layer. The domain emits events that drive both internal read-model projections and cross-service integration.
HTTP/gRPC ──▶ Controller ──▶ CommandBus ──▶ CommandHandler ──▶ Aggregate
│
DomainEvent emitted
│
┌────────────────────────┴────────────────────────┐
Kafka topic RabbitMQ exchange
(durable, replay) (routing, fanout)
│
┌────────┴────────┐
Projector Downstream
(MongoDB read service consumer
model update)
- Write model — PostgreSQL, strongly consistent, transactional
- Read model — MongoDB or Couchbase, denormalized, query-optimized
- Events — Kafka for durable cross-service events; RabbitMQ for internal routing and fanout
All Kafka topics and RabbitMQ exchanges/queues consumed or published by this service are documented in contract/src/main/resources/asyncapi/asyncapi.yml following the AsyncAPI 3 specification.
This file is the single source of truth for:
- Which topics/queues this service publishes to (outbound events)
- Which topics/queues this service subscribes to (inbound events)
- The schema (JSON Schema / Avro reference) of every message payload
- Bindings for Kafka partition keys, RabbitMQ routing keys, headers
Teams consuming this service's events should reference the AsyncAPI spec the same way frontend teams reference an OpenAPI spec.
Topics and consumer group IDs are declared in application.yml. Producers use KafkaTemplate. Consumers use @KafkaListener with dead-letter topic (DLT) support via @RetryableTopic.
Exchanges, queues, and bindings are declared as @Bean in infrastructure/config/RabbitMQConfig.java. Consumers use @RabbitListener.
| Store | Role | Module |
|---|---|---|
| PostgreSQL + Flyway | Write model — transactional, consistent | infrastructure/postgres |
| MongoDB | Read model — projections, document queries | infrastructure/mongodb |
| Couchbase | Key-value lookups, distributed cache-like access | infrastructure/couchbase |
Flyway migrations run automatically on startup. Naming convention: V{n}__{description}.sql — e.g. V1__create_example_table.sql.
Resilience4j patterns are configured per outbound integration in infrastructure/resilience/:
| Pattern | Applied to |
|---|---|
| Circuit Breaker | All outbound HTTP calls, DB adapters |
| Retry + backoff | Kafka producers, HTTP clients |
| Rate Limiter | Public API endpoints |
| Bulkhead | Thread pool isolation per external dependency |
http://localhost:8080/actuator/prometheus ← Prometheus scrape endpoint
http://localhost:9090 ← Prometheus UI
http://localhost:3000 ← Grafana (admin / admin)
A pre-built Grafana dashboard (observability/dashboards/skeletoni-dashboard.json) covers JVM memory, GC, HTTP latency, Kafka consumer lag, RabbitMQ queue depth, and custom business metrics.
GET /actuator/health ← Liveness + readiness + all component statuses
GET /actuator/info ← Build info + git commit
GET /actuator/metrics ← All registered Micrometer meters
GET /actuator/prometheus ← Prometheus-format scrape target
Every log line is emitted as JSON and enriched with:
| Field | Source |
|---|---|
correlationId |
X-Correlation-ID header (generated if absent) |
traceId / spanId |
Micrometer Tracing (W3C Trace Context) |
service |
spring.application.name |
env |
spring.profiles.active |
version |
pom.xml project version |
The logging module provides a servlet filter and Kafka interceptor that inject these fields into MDC automatically — no manual MDC calls needed in application code.
Jobs are declared in application/scheduler/ using @Scheduled. All cron expressions are externalized to application.yml under app.scheduler.*. The scheduler executor is configured to use Virtual Threads (Project Loom), so blocking jobs don't consume platform threads.
./mvnw test # Unit tests only (fast, no Docker required)
./mvnw verify # Unit + integration tests (Docker required)| Layer | Scope | Tools |
|---|---|---|
domain |
Pure unit — aggregates, domain services | JUnit 5 + Mockito |
application |
Use case unit tests with port mocks | JUnit 5 + Mockito |
contract |
Controller slice tests | @WebMvcTest + MockMvc |
infrastructure |
Full integration — real DB, real broker | Testcontainers |
Testcontainers manages real PostgreSQL, MongoDB, Kafka, and RabbitMQ containers for integration tests. No in-memory fakes, no manual setup.
SonarCloud analysis runs on every push to main via GitHub Actions. To analyze locally:
./mvnw sonar:sonar \
-Dsonar.projectKey=your-org_skeletoni \
-Dsonar.organization=your-org \
-Dsonar.token=$SONAR_TOKENQuality gate covers: coverage thresholds, duplication, code smells, security hotspots, and bug detection.
Two workflows in .github/workflows/:
Checkout → Java 21 setup → Maven build → Unit tests
→ Integration tests (Testcontainers) → SonarCloud → Docker build
Checkout → Java 21 setup → Maven build → Full test suite
→ Docker build + push to registry → GitHub Release notes
Secrets required: SONAR_TOKEN, DOCKER_USERNAME, DOCKER_PASSWORD (or equivalent registry credentials).
| Variable | Default | Description |
|---|---|---|
SERVER_PORT |
8080 |
HTTP port |
GRPC_PORT |
9090 |
gRPC port |
SPRING_DATASOURCE_URL |
see compose.yml |
PostgreSQL JDBC URL |
SPRING_DATA_MONGODB_URI |
see compose.yml |
MongoDB connection URI |
SPRING_COUCHBASE_CONNECTION_STRING |
see compose.yml |
Couchbase cluster address |
SPRING_KAFKA_BOOTSTRAP_SERVERS |
localhost:9092 |
Kafka broker list |
SPRING_RABBITMQ_HOST |
localhost |
RabbitMQ host |
MANAGEMENT_PROMETHEUS_ENABLED |
true |
Expose Prometheus scrape endpoint |
SONAR_TOKEN |
(CI secret) | SonarCloud authentication token |
To turn this skeleton into a real service:
- Rename
skeletonieverywhere - Define your aggregate in
domain/model/ - Write your first Flyway migration in
db/migration/V1__init.sql - Add a command + handler in
application/command/ - Expose it via a REST controller in
contract/rest/ - Define your Kafka topic and AsyncAPI contract in
asyncapi.yml - Write one Testcontainers integration test
Everything else — resiliency, metrics, distributed tracing, structured logging, CI pipeline — works out of the box.