An Industrial-Grade Spring Boot Microservices System (Transactional Outbox, Kafka, Idempotency)
This project is a miniature yet industrial-grade microservices platform built with Spring Boot, PostgreSQL, and Kafka, designed to demonstrate real-world backend engineering concerns rather than toy CRUD examples.
It focuses on data consistency, event-driven architecture, fault tolerance, and operational robustness, following patterns commonly used in production systems.
This project is intentionally opinionated and emphasizes correctness under failure, eventual consistency, and observability.
- order-service — handles order creation and persistence
- payment-service — consumes order events and processes payments
- common-lib — shared event contracts and primitives
- PostgreSQL (transactional storage + outbox)
- Kafka (asynchronous messaging backbone)
- Docker Compose (local orchestration)
Client
|
v
Order Service
| (DB transaction)
|-- Orders table
|-- Outbox table
|
| (async)
v
Kafka (order.events)
|
v
Payment Service
|-- Idempotent consumer
|-- Processed-events table
- Avoids dual-write problems
- Ensures at-least-once delivery
- Maintains business correctness under retries and crashes
Order creation and event emission are decoupled but atomically consistent.
Instead of publishing Kafka messages directly inside business logic:
- The service writes:
- Order record
- Outbox event record
in the same database transaction
- A background publisher later delivers events to Kafka
This guarantees:
- No event is lost if Kafka is temporarily unavailable
- No event is published without its corresponding database state
Outbox lifecycle:
PENDING -> PROCESSING -> SENT
\-> retry with backoff ->/
The outbox publisher is designed to be safe under horizontal scaling.
Key mechanisms:
- Database-level locking using FOR UPDATE SKIP LOCKED
- Explicit PROCESSING state to claim events
- Short-lived transactions to avoid blocking
Failure handling:
- Exponential backoff using available_at
- Retry counters and last error tracking
- Clean, observable state transitions
All cross-service communication is asynchronous and event-based.
- Order Service publishes OrderCreatedEvent
- Payment Service reacts independently
- Services are loosely coupled
- Failures in one service do not cascade
Events are explicitly versioned and self-describing:
- eventId
- eventType
- eventVersion
- occurredAt
- payload
Kafka provides at-least-once delivery, so consumers must be idempotent.
Idempotency is implemented via:
- A processed_events table keyed by event_id
- Atomic “try-start” semantics
- First consumer wins, duplicates are ignored
This ensures:
- No double payment
- No duplicate side effects
- Safe replays and offset resets
The system explicitly handles:
- Kafka downtime
- Consumer restarts
- Duplicate message delivery
- Database transaction rollbacks
Failures are:
- Logged with context
- Retried with backoff
- Observable via database state
- Java 17
- Spring Boot
- Spring Data JPA
- Apache Kafka
- PostgreSQL
- Flyway
- Jackson
- Maven
- Docker Compose
mini-order-platform/
├── common-lib/
│ └── shared event contracts
│
├── order-service/
│ ├── api/ REST controllers
│ ├── domain/ Domain models
│ ├── persistence/ JPA entities and repositories
│ ├── outbox/ Transactional outbox
│ └── messaging/ Kafka producer
│
├── payment-service/
│ ├── messaging/ Kafka consumer
│ ├── idempotency/ Deduplication logic
│ └── persistence/
│
└── docker-compose.yml
Start infrastructure:
docker-compose up -d
Run services:
mvn -pl order-service spring-boot:run
mvn -pl payment-service spring-boot:run
- Health checks via Spring Actuator
- Explicit logging around:
- Event publication
- Consumer processing
- Duplicate detection
- Database tables act as operational truth:
- Outbox state
- Processed events
- Retry attempts
- Distributed transactions (XA)
- Synchronous inter-service REST calls
- Exactly-once guarantees
- In-memory shortcuts for reliability
All correctness is based on explicit, observable state.
- Dead-letter topics
- Schema registry
- OpenTelemetry tracing
- Kubernetes deployment
- OAuth2 / service-to-service authentication
Client -> Order Service -> Database (Orders + Outbox)
|
v
Kafka
|
v
Payment Service -> Processed Events
This project demonstrates:
- How real systems behave under failure
- How Spring Boot is used beyond CRUD
- How to design for eventual consistency
- How to reason about distributed systems trade-offs
The goal is not feature count, but engineering correctness.