Saga / Process Manager
The Saga pattern coordinates a long-running business process by reacting to messages and advancing explicit state. PatternKit provides a small in-process saga runtime and a source generator for static, typed process-manager factories.
Use these APIs when the saga state is already loaded by your application and message handling happens inside the current process. Persist state, concurrency tokens, retries, and transport delivery outside PatternKit when the workflow must survive process restarts.
Runtime API
using PatternKit.Messaging;
using PatternKit.Messaging.Sagas;
var saga = Saga<OrderState>.Create()
.On<OrderSubmitted>().Then((state, message, context) =>
state with { OrderId = message.Payload.OrderId, Submitted = true })
.On<OrderPaid>().Then((state, message, context) =>
state with { Paid = true })
.CompleteWhen(state => state.Submitted && state.Paid)
.Build();
var result = saga.Handle(state, Message<OrderSubmitted>.Create(submitted));
Saga<TState> only runs steps whose message type matches the handled message. Use When<TMessage> for guarded transitions.
AsyncSaga<TState> supports asynchronous guards and handlers:
var saga = AsyncSaga<OrderState>.Create()
.On<OrderSubmitted>().Then((state, message, context, cancellationToken) =>
new ValueTask<OrderState>(state with { Submitted = true }))
.Build();
Source Generator
Use [GenerateSaga] on a partial class or struct, then mark static step methods with [SagaStep]. Add [SagaCompleteWhen] to one static completion predicate when needed.
using PatternKit.Generators.Messaging;
using PatternKit.Messaging;
[GenerateSaga(typeof(OrderState))]
public static partial class OrderSaga
{
[SagaStep(typeof(OrderSubmitted), 10)]
private static OrderState Submit(
OrderState state,
Message<OrderSubmitted> message,
MessageContext context) => state with { Submitted = true };
[SagaCompleteWhen]
private static bool IsComplete(OrderState state) => state.Submitted;
}
var result = OrderSaga.Create().Handle(state, Message<OrderSubmitted>.Create(submitted));
Generated sync steps must be static and have this shape:
TState Step(TState state, Message<TMessage> message, MessageContext context)
Generated async steps must be static and have this shape:
ValueTask<TState> StepAsync(
TState state,
Message<TMessage> message,
MessageContext context,
CancellationToken cancellationToken)
The generator emits diagnostics for non-partial saga types, missing steps, invalid step signatures, and invalid completion predicates.
API
- Saga<TState>
- AsyncSaga<TState>
- SagaResult<TState>
- GenerateSagaAttribute
- SagaStepAttribute
- SagaCompleteWhenAttribute
Example Source
src/PatternKit.Examples/Messaging/SagaExample.cstest/PatternKit.Examples.Tests/Messaging/SagaExampleTests.cs