A reference implementation of the Saga Pattern using Netflix/Orkes Conductor, Spring Boot, and Kafka to orchestrate distributed transactions across multiple microservices. This project demonstrates how to manage distributed transactions via Orchestration, where each microservice performs a local transaction and communicates state changes through events. If any step fails, compensating transactions roll back previously successful steps, ensuring overall data consistency.
-
Orchestration-Based Saga
- A centralized Orchestrator (using Netflix/Orkes Conductor) controls the saga flow across microservices.
-
Microservices
- Separate services for Order, Inventory, Payment, Shipping, Notification, etc.
-
Kafka Messaging
- Asynchronous event-driven communication via Kafka topics.
-
CQRS
- A separate Query Service listens to events and maintains a read-optimized view of data.
-
Local Transactions & Compensations
- Each service uses a local ACID transaction; compensating commands are sent on failure to revert successful steps.
saga-microservices-orchestrator
├── common
│ └── src/main/java/com/distributedtransactions/common/dto
│ # Shared DTOs and event classes
├── orchestrator
│ └── src/main/java/com/distributedtransactions/orchestrator
│ # Orchestration logic using Conductor
├── order-service
│ └── src/main/java/com/distributedtransactions/orderservice
│ # Handles order creation and cancellation
├── inventory-service
│ └── src/main/java/com/distributedtransactions/inventoryservice
│ # Manages inventory reservations/releases
├── payment-service
│ └── src/main/java/com/distributedtransactions/paymentservice
│ # Processes payments and rollbacks
├── shipping-service
│ └── src/main/java/com/distributedtransactions/shippingservice
│ # Ships orders and handles failures
├── notification-service
│ └── src/main/java/com/distributedtransactions/notificationservice
│ # Sends success/failure notifications
├── query-service
│ └── src/main/java/com/distributedtransactions/queryservice
│ # Maintains CQRS read model
└── docker-compose.yml
# Spins up Kafka, Conductor, etc.
-
Clone the Repository:
git clone https://github.com/felixojiambo/saga-microservices-orchestrator.git cd saga-microservices-orchestrator -
Set Up Infrastructure:
- Kafka and Orkes Conductor can be run via Docker Compose:
docker-compose up -d
- Verify Kafka is accessible at
localhost:9092and Conductor athttp://localhost:8080.
- Kafka and Orkes Conductor can be run via Docker Compose:
-
Configure Conductor Credentials (Optional):
- If using Orkes Conductor Cloud, update your
application.propertiesin the Orchestrator module with Access Key and Secret:conductor.server.url=https://<orkes-conductor-cloud-url>/api conductor.security.client.key-id=<YOUR_KEY> conductor.security.client.secret=<YOUR_SECRET>
- If using OSS Conductor locally, ensure the server is up on port 8080.
- If using Orkes Conductor Cloud, update your
-
Build & Run:
mvn clean install # Start each service in its own terminal: mvn spring-boot:run -f orchestrator/pom.xml mvn spring-boot:run -f order-service/pom.xml mvn spring-boot:run -f inventory-service/pom.xml mvn spring-boot:run -f payment-service/pom.xml mvn spring-boot:run -f shipping-service/pom.xml mvn spring-boot:run -f notification-service/pom.xml mvn spring-boot:run -f query-service/pom.xml
Each microservice can be started independently. Open separate terminal windows/tabs for each:
-
Orchestrator Service
cd saga-microservices-orchestrator/orchestrator mvn spring-boot:run -
Order Service
cd saga-microservices-orchestrator/order-service mvn spring-boot:run -
Inventory Service
cd saga-microservices-orchestrator/inventory-service mvn spring-boot:run -
Payment Service
cd saga-microservices-orchestrator/payment-service mvn spring-boot:run -
Shipping Service
cd saga-microservices-orchestrator/shipping-service mvn spring-boot:run -
Notification Service
cd saga-microservices-orchestrator/notification-service mvn spring-boot:run -
Query Service
cd saga-microservices-orchestrator/query-service mvn spring-boot:run
- Using the Orkes Conductor UI at
http://localhost:8080, register your workflows if you plan to use Conductor’s workflow definitions. - However, if you rely solely on OrchestratorService to manage the saga steps, you can skip defining any additional workflow in Conductor.
11.3.1. Send a Create Order Command
Use cURL or Postman to send a command to create an order. For example, if your Order Service exposes a REST endpoint on port 8083:
curl --location 'http://localhost:8083' \
--header 'Content-Type: application/json' \
--data '{
"orderId": "order-1001",
"customerId": "customer-123",
"amount": 45.0,
"type": "CREATE_ORDER"
}'Alternatively, publish a CREATE_ORDER command via Kafka directly:
docker exec -it saga-test_kafka_1 bash
# Inside Kafka container:
kafka-console-producer --broker-list localhost:9092 --topic order-commands \
--property "parse.key=true" --property "key.separator=:"
>order-1001:{"orderId":"order-1001","customerId":"customer-123","amount":45.0,"type":"CREATE_ORDER"}11.3.2. Observe the Saga Progress
- Order Service creates the order and publishes
ORDER_CREATED. - Orchestrator Service listens to
order-events(e.g.,ORDER_CREATED) and sendsRESERVE_INVENTORY. - Inventory Service reserves inventory and publishes
INVENTORY_RESERVED. - Orchestrator Service sends
PROCESS_PAYMENT. - Payment Service processes payment and publishes
PAYMENT_SUCCESS. - Orchestrator Service sends
SHIP_ORDER. - Shipping Service ships the order and publishes
ORDER_SHIPPED. - Orchestrator Service sends
NOTIFY_CUSTOMER. - Notification Service sends a success notification.
- Query Service updates the read model accordingly.
Check the Query Service REST API (example on port 8088):
curl --location 'http://localhost:8088/api/v1/orders/order-1001'Expected Response:
{
"orderId": "order-1001",
"customerId": "customer-123",
"amount": 45.0,
"status": "SHIPPED"
}To test compensating actions, create an order with an amount that causes payment failure (e.g., amount = 60.0 in your Payment Service logic):
# In Kafka container:
kafka-console-producer --broker-list localhost:9092 --topic order-commands \
--property "parse.key=true" --property "key.separator=:"
>order-1002:{"orderId":"order-1002","customerId":"customer-123","amount":60.0,"type":"CREATE_ORDER"}Expected Flow:
- Order Service: Creates the order →
ORDER_CREATED. - Orchestrator: Sends
RESERVE_INVENTORY. - Inventory Service: Reserves inventory →
INVENTORY_RESERVED. - Orchestrator: Sends
PROCESS_PAYMENT. - Payment Service: Payment fails →
PAYMENT_FAILED. - Orchestrator: Issues
RELEASE_INVENTORY&CANCEL_ORDER. - Inventory Service: Releases inventory →
INVENTORY_RELEASED. - Order Service: Cancels order →
ORDER_CANCELED. - Query Service: Updates the read model with canceled status.
- Notification Service: Sends a failure notification.
curl --location 'http://localhost:8088/api/v1/orders/order-1002'Expected Response:
{
"orderId": "order-1002",
"customerId": "customer-123",
"amount": 60.0,
"status": "CANCELED"
}cd saga-microservices-orchestrator
git add .
git commit -m "docs: add instructions for running and testing the saga orchestration flow"- Saga Orchestrator: Coordinates the entire flow, listening to domain events (
ORDER_CREATED,INVENTORY_RESERVED, etc.) and issuing next-step commands (PROCESS_PAYMENT). - Local Transactions: Each microservice manages its own DB and transaction boundaries.
- Compensation: If a microservice step fails, the Orchestrator instructs other microservices to roll back previously successful steps.
This project is licensed under the MIT License, granting permission to use, modify, and distribute under typical open-source terms.
- Fork the repository.
- Create a new feature branch (
git checkout -b feature/my-new-feature). - Commit your changes (
git commit -m 'Add some feature'). - Push to the branch (
git push origin feature/my-new-feature). - Open a Pull Request.
- Author: Ojiambo
- GitHub: felixojiambo
- Repository: saga-microservices-orchestrator
Enjoy building your Saga Orchestrated Microservices with Netflix/Orkes Conductor and Kafka!