Skip to content

Saga Configuration

Configuring a saga state machine has three concerns:

  1. Register the state machine and its instance type
  2. Choose a saga repository so instances can be persisted between events
  3. Configure the receive endpoint behavior for concurrency, retry, outbox, and naming

This page covers those pieces from the top down. If you need the conceptual background first, see Saga State Machines.

When you add a saga state machine, MassTransit registers:

  • The state machine class
  • The saga instance type
  • The endpoint configuration needed to consume correlated events
  • The saga repository, when one is specified

In most applications, the common shape is:

services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderStateMachine, OrderState>()
.InMemoryRepository();
x.UsingRabbitMq((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});

With that baseline in mind, the sections below show the available registration and configuration options.

To add a saga state machine, use the AddSagaStateMachine method. There are several overloads for this method.

using MassTransit;
services.AddMassTransit(x =>
{
// Adds the saga state machine
x.AddSagaStateMachine<OrderStateMachine, OrderState>();
// Adds the saga state machine by type using the saga definition (optional)
x.AddSagaStateMachine(typeof(OrderStateMachine), typeof(OrderStateDefinition))
// Adds all saga state machines from the specified assembly, including any saga definitions
x.AddSagaStateMachines(Assembly.GetExecutingAssembly());
// Adds all saga state machines from the namespace containing the type, including any saga definitions
x.AddSagaStateMachinesFromNamespaceContaining<OrderStateMachine>();
// Adds all saga state machines from the namespace containing the type, including any saga definitions
x.AddSagaStateMachinesFromNamespaceContaining(typeof(OrderStateMachine));
});

Consumer sagas are added using the AddSaga method.

using MassTransit;
services.AddMassTransit(x =>
{
// Adds a consumer saga
x.AddSaga<OrderSaga>();
// Adds a saga by type
x.AddSaga(typeof(OrderSaga));
// Adds a saga by type using the saga definition
x.AddSaga(typeof(OrderSaga), typeof(OrderSagaDefinition));
// Adds all sagas in the specified assembly, including any saga definitions
x.AddSagas(Assembly.GetExecutingAssembly());
// Adds all sagas from the namespace containing the type, including any saga definitions
x.AddSagasFromNamespaceContaining<OrderSaga>();
x.AddSagasFromNamespaceContaining(typeof(OrderSaga));
});

Saga definitions are used to specify the endpoint configuration of a saga so that the saga can be automatically configured. Definitions may be explicitly added when calling AddSagaStateMachine / AddSaga or can be discovered automatically using any of the AddSagaStateMachines / AddSagas methods.

An example saga definition is shown below.

public class OrderStateDefinition :
SagaDefinition<OrderState>
{
public OrderStateDefinition()
{
ConcurrentMessageLimit = 16;
// alternatively, configure the endpoint directly using the Endpoint method
Endpoint(e => e.ConcurrentMessageLimit = 16);
}
protected override void ConfigureSaga(IReceiveEndpointConfigurator endpointConfigurator,
ISagaConfigurator<OrderState> sagaConfigurator,
IBusRegistrationContext context)
{
endpointConfigurator.UseMessageRetry(r => r.Interval(2, 100));
endpointConfigurator.UseInMemoryOutbox();
var partition = endpointConfigurator.CreatePartitioner(16);
sagaConfigurator.Message<SubmitOrder>(x => x.UsePartitioner(partition, m => m.Message.CorrelationId));
sagaConfigurator.Message<OrderAccepted>(x => x.UsePartitioner(partition, m => m.Message.CorrelationId));
sagaConfigurator.Message<OrderCanceled>(x => x.UsePartitioner(partition, m => m.Message.CorrelationId));
}
}

The ConfigureSaga method is used to configure the endpoint for the saga state machine when ConfigureEndpoints or ConfigureSaga is called.

Use a saga definition when you want the endpoint behavior to travel with the saga registration rather than being configured inline. This is the usual place to apply retry, outbox, partitioning, and endpoint naming decisions.

Saga state machines are durable by design, so the repository choice matters. The repository stores the saga instance between messages and determines which correlation strategies are supported.

  • Identity-only repositories require correlation by CorrelationId
  • Query-capable repositories also support property-based correlation expressions

Repository-specific setup is covered in the Saga Repositories section.

When adding a saga state machine, the saga repository can be configured using the appropriate extension method. In the example below, the saga state machine is configured to use an in-memory saga repository.

using MassTransit;
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderStateMachine, OrderState>()
.InMemoryRepository();
});

A saga repository can be added separately using the AddSagaRepository<TSaga> method. Using this method, the saga repository type must be specified using the appropriate extension method. In the example below, the saga state machine is configured to use an in-memory saga repository.

using MassTransit;
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderStateMachine, OrderState>();
x.AddSagaRepository<OrderState>()
.InMemoryRepository();
});

The saga repository provider can be used to configure multiple saga repositories using the same repository type. Any saga state machines without an explicitly configured repository will use the repository provider to determine the repository type.

using MassTransit;
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<OrderStateMachine, OrderState>();
x.SetInMemorySagaRepositoryProvider();
});

When adding a saga state machine (or consumer saga), the endpoint can be configured using the Endpoint method.

In this example, the saga state machine is configured to use an in-memory saga repository, and the endpoint is configured using the name order-state with a concurrent message limit of 8.

services.AddMassTransit(r =>
{
r.AddSagaStateMachine<OrderStateMachine, OrderState>()
.InMemoryRepository()
.Endpoint(e =>
{
e.Name = "order-state";
e.ConcurrentMessageLimit = 8;
});
});

For a new saga state machine, the usual sequence is:

  1. Model the workflow using the saga state machine guide
  2. Register it with AddSagaStateMachine
  3. Choose a repository appropriate for your storage and correlation needs
  4. Configure endpoint behavior using a saga definition when concurrency or throughput matter