01
What it does
A Spring Boot microservices backend covering checkout, inventory, orders, and payments. The interesting part is what it does with distributed transactions and concurrency, not the CRUD surface.
02
Checkout via SAGA
A checkout request triggers a SAGA orchestrated by the checkout service: reserve inventory, charge payment, create order, publish inventory event. Each step has an explicit compensating action. If payment fails after inventory is reserved, the SAGA emits a release inventory step and the order never gets created.
POST /api/v1/checkout/process
{
"customerId": "customer-uuid",
"items": [{ "productId": "...", "quantity": 1 }],
"paymentMethod": "CREDIT_CARD"
}Steps use the outbox pattern so an event is committed in the same transaction as the database write that produced it. A relayer publishes from the outbox to Kafka, which means no event is ever published for a transaction that rolled back.
03
Optimistic concurrency
Inventory uses @Version on the JPA entity. Concurrent reserve requests on the same SKU detect the race at commit time and retry. Under fault injection in load tests this cut observed deadlocks by about 40 percent compared to the original pessimistic lock approach.
04
Caching and events
Redis caches inventory reads on hot paths. Cache invalidation is event driven: when a Kafka inventory event is consumed, the matching SKU's cache key is evicted, so reads stay consistent with reserves and restocks. Kafka itself is optional. Setting INVENTORY_EVENTS_ENABLED=false switches the service to synchronous database reads only.
05
Resilience
Resilience4j circuit breakers wrap service to service calls. All write endpoints accept an idempotency key and dedupe within a configurable window. Retries are bounded and use exponential backoff with jitter.
06
Targets and observability
Internal SLO is p95 under 300 ms and p99 under 800 ms for checkout under load. Spring Boot Actuator exposes liveness and readiness probes, plus Prometheus metrics. A static HTML dashboard at port 3000 shows live inventory, processed orders, and per request response times so you can run a load test in one tab and watch the system breathe in another.
07
Service layout
| Component | Stack | Notes |
|---|---|---|
| Inventory | Spring Boot, PostgreSQL | Optimistic locking, Kafka producer |
| Checkout | Spring Boot | SAGA orchestrator |
| Orders | Spring Boot, PostgreSQL | Append only event log |
| Payments | Spring Boot | Idempotent charge endpoint |
| Frontend | HTML, vanilla JS | Test harness, not a real storefront |
Everything runs via Docker Compose locally. Service to service URLs use Compose names so the wiring is identical to the deployment topology.
