A implementation of the Saga Pattern for coordinating distributed transactions across microservices. This system demonstrates how to reliably execute multi-step business workflows while maintaining data consistency across independently deployed services using Spring Boot, Apache Kafka, and PostgreSQL.
The SAGA Distributed System coordinates an order fulfillment workflow by orchestrating three independent microservices. Each service manages its own database and communicates via event-driven architecture using Kafka. If any step fails, automatic compensating transactions roll back previous changes—ensuring eventual consistency even in failure scenarios.
- ✅ Saga Orchestration Pattern - Centralized workflow engine with clear step dependencies
- ✅ Event-Driven Communication - Asynchronous Kafka-based message passing
- ✅ Distributed Transactions - Multi-step workflow with automatic rollback on failure
- ✅ Compensating Transactions - Undo operations for inventory reserves and payment charges
- ✅ Audit Logging - Complete step-by-step transaction history and status tracking
- ✅ Automated Testing - Integration tests with Docker, Kafka, and PostgreSQL
- ✅ Schema-Per-Service - Independent database schemas for data isolation
| Service | Port | Responsibility |
|---|---|---|
| saga-orchestrator | 8084 | REST API, saga engine, workflow orchestration |
| inventory-service | 8081 | Inventory management, stock reservations |
| payment-service | 8082 | Payment processing, charge & refund operations |
| notification-service | 8083 | User notifications via Kafka |
The system executes a three-step saga:
| Step | Service | Command Topic | Reply Topic | Compensation Topic |
|---|---|---|---|---|
| 1️⃣ | Inventory | inventory.commands |
inventory.replies |
inventory.compensate |
| 2️⃣ | Payment | payment.commands |
payment.replies |
payment.compensate |
| 3️⃣ | Notification | notification.commands |
notification.replies |
(no rollback needed) |
Success Path: Reserve Inventory → Charge Payment → Send Notification
Failure Path: If any step fails, prior steps are automatically compensated (reversed)
- PostgreSQL 17 - Schema-per-service pattern (port 5434)
inventoryschema - Stock reservations and inventory managementpaymentschema - Transaction records and payment historyorchestratorschema - Saga state management & audit logs
- Apache Kafka (KRaft mode) - Event streaming and message broker (port 9092)
- UTC Timezone - All database connections normalized to UTC for cross-timezone consistency
- JDK 25 or later
- Maven 3.9+
- Docker Desktop (or Docker Engine)
- Git
From the repository root:
docker compose up -dThis starts:
- PostgreSQL 17 with
init-db/01-create-schemas.sql(schemas + seed products) - Kafka (KRaft, single broker)
Wait until Postgres is healthy (Compose defines a healthcheck), then start the four Spring Boot applications (four terminals or your IDE).
After starting Docker Compose, you can run all integration tests:
mvn clean testUse each module’s Maven wrapper or mvn spring-boot:run from the nested project directory, in any order (all need Kafka + Postgres up first):
cd "inventory-service\inventory-service"; .\mvnw spring-boot:run
cd "payment-service\payment-service"; .\mvnw spring-boot:run
cd "notification-service\notification-service"; .\mvnw spring-boot:run
cd "saga-orchestrator\saga-orchestrator"; .\mvnw spring-boot:run$body = @{
userId = "user-42"
email = "user@example.com"
items = @(@{ sku = "SKU-MOUSE-001"; quantity = 1; price = 29.99 })
} | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri "http://localhost:8084/api/sagas" -Body $body -ContentType "application/json"Poll status until COMPLETED (or FAILED if a step fails):
Invoke-RestMethod -Uri "http://localhost:8084/api/sagas/<SAGA_ID>"Step audit log:
Invoke-RestMethod -Uri "http://localhost:8084/api/sagas/<SAGA_ID>/logs"By default, notifications succeed so the happy path completes. To simulate a failure at the last step and exercise inventory release + payment refund, run the notification service with the demo profile:
cd "notification-service\notification-service"
.\mvnw spring-boot:run -Dspring-boot.run.profiles=demo(application-demo.yml sets notification.simulate-failure: true.)
Start the same infrastructure Compose provides, then run Maven (tests talk to localhost:5434 and localhost:9092):
docker compose up -d
mvn testOr a single module:
cd "saga-orchestrator\saga-orchestrator"; .\mvnw testWhat is covered:
- saga-orchestrator: full
ORDER_SAGAtoCOMPLETEDagainst Postgres + Kafka from Compose, with in-test stub participants replying on command topics (worker apps do not need to run). - inventory-service / payment-service: publish a command to Kafka; listener updates PostgreSQL; assertions on stock or row count.
- notification-service: Embedded Kafka (no Docker required for this module), success path with
simulate-failure=false.
| Setting | Purpose |
|---|---|
notification.simulate-failure |
true forces the notification step to throw (demo compensation). Default in application.yml is false. |
saga.timeout.* |
Orchestrator timeout scheduler (see application.yml). |
docker-compose.yml # Postgres + Kafka
init-db/ # Postgres init SQL
saga-orchestrator/ # Orchestrator Spring Boot app
inventory-service/
payment-service/
notification-service/