0
0
MicroservicesConceptIntermediate · 4 min read

Saga Pattern: What It Is and How It Works in Microservices

The 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.

javascript
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();
})();
Output
Step 1: Debit account Step 2: Credit account Error occurred: Credit failed Compensating: Refund debit Saga rolled back
🎯

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.

Key Takeaways

Saga pattern manages distributed transactions by splitting them into smaller steps with rollback actions.
It ensures data consistency without locking resources, ideal for microservices.
Use sagas for long-running business processes like orders, payments, and bookings.
Compensating actions undo previous steps if a later step fails.
Sagas can be coordinated centrally or managed by event choreography.