In the Orchestrator-Based Saga, a central orchestrator service systematically manages transactions across multiple microservices. Specifically, it ensures that each step is executed in the correct sequence and handles compensations whenever a step fails. Below is a complete implementation example of an E-commerce Order Management System in Node.js to illustrate this concept further.

Scenario: E-commerce Order Management
Services Involved:
- Orchestrator: Manages the saga, coordinating between all services.
- Order Service: Handles order creation.
- Payment Service: Processes payments.
- Inventory Service: Reserves stock.
- Notification Service: Sends order confirmation notifications.
Step-by-Step Implementation of Orchestrator-Based Saga
Prerequisites
We need to set up docker and run RabbitMQ as a docker container
docker pull rabbitmq
docker run -d --hostname rabbitmq --name rabbitmq -p 5672:5672 rabbitmq
Step 1: Messaging Setup
To facilitate communication between services, RabbitMQ is used as the messaging system. First, install the necessary library:
npm install amqplib
Step 2: Orchestrator Service
The Orchestrator coordinates the flow of the saga, handling each step and compensating when necessary.
// file orchestrator_service.js
const amqp = require('amqplib');
async function orchestratorService() {
const connection = await amqp.connect('amqp://localhost:5672');
const channel = await connection.createChannel();
await channel.assertQueue('orchestrator');
console.log('Orchestrator is running...');
// Listen for responses from services
channel.consume('orchestrator', async (msg) => {
const { step, status, data } = JSON.parse(msg.content.toString());
if (status === 'success') {
if (step === 'orderCreated') {
console.log('Order created, proceeding to payment...');
channel.sendToQueue('payment', Buffer.from(JSON.stringify({ action: 'processPayment', data })));
} else if (step === 'paymentProcessed') {
console.log('Payment processed, proceeding to inventory...');
channel.sendToQueue('inventory', Buffer.from(JSON.stringify({ action: 'reserveStock', data })));
} else if (step === 'stockReserved') {
console.log('Stock reserved, sending notification...');
channel.sendToQueue('notification', Buffer.from(JSON.stringify({ action: 'sendNotification', data })));
}
} else {
console.error(`Error in ${step}, compensating...`);
if (step === 'paymentProcessed') {
console.log('Refunding payment...');
channel.sendToQueue('payment', Buffer.from(JSON.stringify({ action: 'refundPayment', data })));
} else if (step === 'stockReserved') {
console.log('Releasing stock...');
channel.sendToQueue('inventory', Buffer.from(JSON.stringify({ action: 'releaseStock', data })));
}
}
channel.ack(msg);
});
// Start the saga by sending the first message
const orderData = { orderId: 1, customer: 'John Doe', amount: 100 };
channel.sendToQueue('order', Buffer.from(JSON.stringify({ action: 'createOrder', data: orderData })));
}
orchestratorService();
Step 3: Order Service
The Order Service is responsible for handling order creation and subsequently responding to the orchestrator to proceed with the workflow.
// file order_service.js
const amqp = require('amqplib');
async function orderService() {
const connection = await amqp.connect('amqp://localhost:5672');
const channel = await connection.createChannel();
await channel.assertQueue('order');
console.log('Order Service is running...');
channel.consume('order', (msg) => {
const { action, data } = JSON.parse(msg.content.toString());
if (action === 'createOrder') {
console.log(`Creating order ${data.orderId}...`);
// Simulate success
const response = { step: 'orderCreated', status: 'success', data };
channel.sendToQueue('orchestrator', Buffer.from(JSON.stringify(response)));
}
channel.ack(msg);
});
}
orderService();
Step 4: Payment Service
The Payment Service handles payment processing and additionally supports refunds as a necessary compensating action when needed.
// file payment_service.js
const amqp = require('amqplib');
async function paymentService() {
const connection = await amqp.connect('amqp://localhost:5672');
const channel = await connection.createChannel();
await channel.assertQueue('payment');
console.log('Payment Service is running...');
channel.consume('payment', (msg) => {
const { action, data } = JSON.parse(msg.content.toString());
if (action === 'processPayment') {
console.log(`Processing payment for order ${data.orderId}...`);
// Simulate success
const response = { step: 'paymentProcessed', status: 'success', data };
channel.sendToQueue('orchestrator', Buffer.from(JSON.stringify(response)));
} else if (action === 'refundPayment') {
console.log(`Refunding payment for order ${data.orderId}...`);
}
channel.ack(msg);
});
}
paymentService();
Step 5: Inventory Service
The Inventory Service manages stock reservations and, if needed, supports releasing stock as a compensating action.
// file inventory_service.js
const amqp = require('amqplib');
async function inventoryService() {
const connection = await amqp.connect('amqp://localhost:5672');
const channel = await connection.createChannel();
await channel.assertQueue('inventory');
console.log('Inventory Service is running...');
channel.consume('inventory', (msg) => {
const { action, data } = JSON.parse(msg.content.toString());
if (action === 'reserveStock') {
console.log(`Reserving stock for order ${data.orderId}...`);
// Simulate success
const response = { step: 'stockReserved', status: 'success', data };
channel.sendToQueue('orchestrator', Buffer.from(JSON.stringify(response)));
} else if (action === 'releaseStock') {
console.log(`Releasing stock for order ${data.orderId}...`);
}
channel.ack(msg);
});
}
inventoryService();
Step 6: Notification Service
The Notification Service is responsible for sending a confirmation email to notify users upon successful order processing.
// file notification_service.js
const amqp = require('amqplib');
async function notificationService() {
const connection = await amqp.connect('amqp://localhost:5672');
const channel = await connection.createChannel();
await channel.assertQueue('notification');
console.log('Notification Service is running...');
channel.consume('notification', (msg) => {
const { action, data } = JSON.parse(msg.content.toString());
if (action === 'sendNotification') {
console.log(`Sending confirmation email for order ${data.orderId}...`);
console.log('Notification sent successfully.');
}
channel.ack(msg);
});
}
notificationService();
Step 6: Start Services
node inventory_service.js
node payment_service.js
node notification_service.js
node order_service.js
node orchestrator_service.js
Source code: https://github.com/nashtech-garage/nodejs_demo-orchestrator-based-saga
Workflow
- Orchestrator sends a
createOrderrequest to the Order Service. - Order Service creates the order and informs the Orchestrator.
- Orchestrator sends a
processPaymentrequest to the Payment Service. - Payment Service processes the payment and informs the Orchestrator.
- Orchestrator sends a
reserveStockrequest to the Inventory Service. - Inventory Service reserves stock and informs the Orchestrator.
- Orchestrator sends a
sendNotificationrequest to the Notification Service. - Notification Service sends a confirmation email.
If any step fails, the orchestrator triggers compensating actions (e.g., refunding payment or releasing stock).
Conclusion
The Orchestrator-Based Saga pattern provides a centralized approach to managing distributed transactions in microservices, making it ideal for complex workflows that require precise control, error handling, and monitoring. By centralizing task coordination, this pattern ensures consistency and simplifies the implementation of compensating actions in case of failures.
However, the reliance on a central orchestrator introduces potential bottlenecks and tighter coupling, requiring robust scalability and fault-tolerance mechanisms. Despite these considerations, Orchestrator-Based Saga remains a powerful solution for systems where clear workflow definition, centralized monitoring, and predictable transaction management are critical. When implemented effectively, it ensures reliability and resilience in distributed architectures.