Saga Pattern: What It Is and How It Works in Microservices
saga pattern is a way to manage long-running transactions across multiple microservices by breaking them into smaller steps with compensating actions for failures. It ensures data consistency without locking resources, using a sequence of local transactions and rollback steps if needed.How It Works
Imagine you want to book a trip that involves reserving a flight, a hotel, and a car rental. Each step is handled by a different service. The saga pattern breaks this big task into smaller steps, where each service completes its part and then tells the next service to proceed.
If something goes wrong, like the hotel is not available, the saga pattern triggers compensating actions to undo the previous steps, such as canceling the flight reservation. This way, the system stays consistent without locking all resources at once.
It works like a chain of promises: each service does its job and either confirms success or triggers a rollback, ensuring the whole process either completes fully or cleans up safely.
Example
This example shows a simple saga coordinator managing two steps: debit an account and then credit another. If credit fails, it compensates by refunding the debit.
class SagaCoordinator { constructor() { this.state = 'START'; } async runSaga() { try { console.log('Step 1: Debit account'); await this.debitAccount(); console.log('Step 2: Credit account'); await this.creditAccount(); this.state = 'COMPLETED'; console.log('Saga completed successfully'); } catch (error) { console.log('Error occurred:', error.message); await this.compensate(); this.state = 'ROLLED_BACK'; console.log('Saga rolled back'); } } debitAccount() { return new Promise((resolve) => setTimeout(resolve, 500)); } creditAccount() { return new Promise((_, reject) => setTimeout(() => reject(new Error('Credit failed')), 500)); } compensate() { return new Promise((resolve) => { console.log('Compensating: Refund debit'); setTimeout(resolve, 500); }); } } (async () => { const saga = new SagaCoordinator(); await saga.runSaga(); })();
When to Use
Use the saga pattern when you have multiple microservices that need to work together to complete a business process, but you want to avoid locking resources or using distributed transactions.
It is ideal for long-running processes like order processing, payment workflows, or booking systems where each step can succeed or fail independently.
For example, an e-commerce site uses sagas to handle orders: reserve inventory, charge payment, and arrange shipping. If payment fails, the inventory reservation is canceled automatically.
Key Points
- Saga pattern breaks a big transaction into smaller local transactions.
- Each step has a compensating action to undo work if needed.
- It avoids locking resources and supports eventual consistency.
- Works well for distributed microservices and long-running workflows.
- Requires a coordinator or choreography to manage steps and rollbacks.