Skip to main content

Correlation

Correlation links incoming messages to the correct saga instance.

Basic Correlation

Define how each message type correlates to a saga:

const saga = createSagaMachine<OrderState, OrderMessages>()
.name('OrderSaga')
.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
.correlate('PaymentCaptured', msg => msg.orderId)
.correlate('InventoryReserved', msg => msg.orderId)
// ...

The canStart Option

Messages with canStart: true can create new saga instances:

// This message can start a new saga
.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })

// This message cannot start a saga (must find existing)
.correlate('PaymentCaptured', msg => msg.orderId)

If a message without canStart arrives and no saga exists, it's ignored.

Wildcard Correlation

Use '*' to define a default correlation for all message types:

.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
.correlate('*', msg => msg.orderId) // All other messages use orderId

This is useful when all messages share the same correlation field.

Multiple Correlation IDs

A single message type can correlate by different fields:

// In a payment saga, correlate by different IDs
.correlate('PaymentRequested', msg => msg.paymentId, { canStart: true })
.correlate('PaymentConfirmed', msg => msg.paymentId)
.correlate('RefundRequested', msg => msg.originalPaymentId) // Different field!

Correlation Flow

Best Practices

Use Business IDs

Correlate by meaningful business identifiers:

// ✅ Good - uses business ID
.correlate('OrderSubmitted', msg => msg.orderId)

// ❌ Avoid - uses technical ID
.correlate('OrderSubmitted', msg => msg.messageId)

Consistent Correlation

Ensure all related messages include the correlation field:

// All order-related messages should have orderId
interface PaymentCaptured {
type: 'PaymentCaptured';
orderId: string; // ✅ Correlation field
transactionId: string;
}