Skip to main content

Core Concepts Overview

Understanding the fundamentals of saga orchestration with Saga Bus.

What is a Saga?​

A saga is a long-running business process that spans multiple services or steps. Unlike a traditional database transaction, sagas handle distributed operations by:

  1. Breaking complex workflows into discrete steps
  2. Maintaining state across those steps
  3. Handling failures with compensation (undo operations)
  4. Reacting to events from multiple sources

The Saga Pattern​

Key Components​

Messages​

Messages are the events and commands that drive sagas. Every message has a type property:

interface OrderSubmitted {
type: 'OrderSubmitted';
orderId: string;
customerId: string;
total: number;
}

Learn more about Messages →

Saga State​

Each saga instance maintains state that persists across message handling:

interface OrderState extends SagaState {
orderId: string;
status: 'pending' | 'confirmed' | 'shipped';
total: number;
}

Learn more about Sagas →

Correlation​

Correlation links messages to the correct saga instance. Typically by a business identifier:

.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
.correlate('PaymentCaptured', msg => msg.orderId)

Learn more about Correlation →

Transport​

The transport handles message delivery between services:

const transport = new RabbitMqTransport({
url: 'amqp://localhost',
});

Store​

The store persists saga state to a database:

const store = new PostgresSagaStore({
connectionString: process.env.DATABASE_URL,
});

Bus​

The bus wires everything together:

const bus = createBus({
transport,
store,
sagas: [{ definition: orderSaga }],
});

Saga Lifecycle​

Optimistic Concurrency​

Saga Bus uses version-based optimistic concurrency to prevent lost updates:

// State includes metadata
interface SagaState {
metadata: {
sagaId: string;
version: number; // Incremented on each update
createdAt: Date;
updatedAt: Date;
isCompleted: boolean;
};
}

If two handlers try to update the same saga simultaneously, one will fail with a ConcurrencyError and retry.

Learn more about State Management →

Error Handling​

Saga Bus distinguishes between:

  • Transient errors - Temporary failures (network, timeout) that should retry
  • Permanent errors - Business logic failures that need compensation
  • Validation errors - Invalid messages that should be rejected

Learn more about Error Handling →

Timeouts​

Set timeouts to handle cases where expected events never arrive:

.on('OrderSubmitted')
.handle(async (msg, state, ctx) => {
ctx.setTimeout(30 * 60 * 1000); // 30 minutes
// ...
})

.on('SagaTimeoutExpired')
.handle(async (msg, state, ctx) => {
// Handle timeout - maybe cancel the order
})

Learn more about Timeouts →

Distributed Tracing​

Saga Bus propagates W3C trace context automatically:

// Trace context flows through
ctx.publish({ type: 'NextStep', ... });
// ↑ Automatically includes traceparent header

Learn more about Distributed Tracing →

Next Steps​