Franz.Common.Messaging.Sagas
2.0.2
dotnet add package Franz.Common.Messaging.Sagas --version 2.0.2
NuGet\Install-Package Franz.Common.Messaging.Sagas -Version 2.0.2
<PackageReference Include="Franz.Common.Messaging.Sagas" Version="2.0.2" />
<PackageVersion Include="Franz.Common.Messaging.Sagas" Version="2.0.2" />
<PackageReference Include="Franz.Common.Messaging.Sagas" />
paket add Franz.Common.Messaging.Sagas --version 2.0.2
#r "nuget: Franz.Common.Messaging.Sagas, 2.0.2"
#:package Franz.Common.Messaging.Sagas@2.0.2
#addin nuget:?package=Franz.Common.Messaging.Sagas&version=2.0.2
#tool nuget:?package=Franz.Common.Messaging.Sagas&version=2.0.2
π¦ Franz.Common.Messaging.Sagas
Version 1.7.5 β Distributed Orchestration Engine for the Franz Framework
Franz.Common.Messaging.Sagas provides long-running workflow orchestration, distributed coordination, and deterministic state machines fully integrated into the Franz architecture.
Sagas in Franz unify:
- Microservice coordination
- Async transactional consistency
- Compensating workflows
- Message-driven state transitions
- Outbox-based reliability
They operate transport-agnostically and integrate seamlessly with:
- Franz.Common.Messaging
- Franz.Common.Mediator
- Kafka
- RabbitMQ
- Azure CosmosDB
- MongoDB
- EntityFramework
- In-memory transient orchestration
π Current Version: 2.0.2
π Whatβs New in v1.7.5
β CosmosDB & Mongo-backed Saga Stores
New persistence providers added:
MongoSagaRepositoryCosmosSagaRepository
Both support:
- Deterministic serialization (
JsonSagaStateSerializer) - Concurrency tokens
- Timestamped audit markers
- Partition-aware storage (CosmosDB)
β Deterministic Saga ID & State Rules
Saga identity now follows one deterministic rule:
SagaId = derived from IMessageCorrelation<T> interface
This eliminates ambiguity across transports and persistence layers.
β Execution Pipeline Improvements
- Fully async-safe execution
- Deterministic handler invocation
- Better error propagation
- Handler return types aligned with
Task<ISagaTransition>
β Improved DI Boot Sequence
All saga infrastructure is now guaranteed to resolve before message listeners start:
- SagaRouter registered early
- SagaOrchestrator registered before Messaging listeners
- Automatic discovery and finalization via
BuildFranzSagas()
β Null-Safety + .NET 10 Compliance
The entire Saga engine is now:
<Nullable>enable<TreatWarningsAsErrors>true>- Aligned with .NET 10 runtime
β Stabilized Mapping & Reflection
- Stronger validation in
SagaRegistration - Improved scanning for Start, Step, Compensation handlers
- Unified contract resolution
β Bug Fixes
- Fixed handler discovery with
ICompensateWith<> - Fixed rare DI timing issues
- Fixed correlation-based saga continuation rules
π§© Core Components
The Saga engine is composed of:
ISaga<TState>
β
SagaRegistration
β
SagaRouter
β
SagaOrchestrator
β
SagaExecutionPipeline
β
ISagaRepository (EF / Mongo / Cosmos / Memory)
β
ISagaAuditSink
π§© Defining a Saga
A complete saga is defined by:
public sealed class OrderSaga :
SagaBase<OrderState>,
IStartWith<OrderCreated>,
IHandle<PaymentAccepted>,
ICompensateWith<PaymentFailed>,
IMessageCorrelation<OrderCreated>,
IMessageCorrelation<PaymentFailed>
{
public override Task OnCreatedAsync(ISagaContext ctx, CancellationToken ct)
{
State.Id = GetCorrelationId((OrderCreated)ctx.Message);
State.CreatedAt = DateTime.UtcNow;
return Task.CompletedTask;
}
public Task<ISagaTransition> HandleAsync(OrderCreated msg, ISagaContext ctx, CancellationToken ct)
=> SagaTransition.Continue(null);
public Task<ISagaTransition> HandleAsync(PaymentAccepted msg, ISagaContext ctx, CancellationToken ct)
=> SagaTransition.Continue(null);
public Task<ISagaTransition> HandleAsync(PaymentFailed msg, ISagaContext ctx, CancellationToken ct)
=> SagaTransition.Continue(null);
public string GetCorrelationId(OrderCreated message) => message.OrderId;
public string GetCorrelationId(PaymentFailed message) => message.OrderId;
}
Handler discovery uses:
IStartWith<TEvent>IHandle<TEvent>ICompensateWith<TEvent>
ποΈ Registering Sagas
var builder = services.AddFranzSagas(opts =>
{
opts.ValidateMappings = true;
});
builder.AddSaga<OrderSaga>();
builder.AddSaga<PaymentSaga>();
services.AddFranzMediator(β¦);
services.AddRabbitMQMessaging(β¦);
var app = host.Build();
app.Services.BuildFranzSagas();
βοΈ Persistence Providers (1.7.5)
| Provider | Package / Class | Status |
|---|---|---|
| InMemory | InMemorySagaRepository |
β Stable |
| EntityFramework | EfSagaRepository |
β Production |
| MongoDB | MongoSagaRepository |
β New in 1.7.5 |
| Cosmos DB | CosmosSagaRepository |
β New in 1.7.5 |
| Redis | RedisSagaRepository |
β Stable |
| Kafka Compaction | (future provider) | β Stable |
π Saga State Model
All saga states must implement:
public interface ISagaState
{
string? ConcurrencyToken { get; set; }
DateTime UpdatedAt { get; set; }
}
And optionally:
public interface ISagaStateWithId
{
string Id { get; set; }
}
π― Execution Pipeline
Wraps each handler:
await _pipeline.ExecuteAsync(async () =>
{
var result = handler.Invoke(...);
if (result is Task t) await t;
});
Allows user-defined middlewares:
- telemetry
- retries
- tracing
- error behavior
π§Ύ Auditing & Logging
SagaLogEvents provides structured logs for all key lifecycle events:
- Saga Start
- Step Execution
- Compensation
- Outgoing message
- Errors
Default auditing sink:
ISagaAuditSink = DefaultSagaAuditSink
Override with:
builder.AddAuditSink<MyElasticSink>();
π§ͺ Testing
For unit tests:
services
.AddFranzSagas(o => o.ValidateMappings = true)
.AddSaga<TestSaga>();
services.AddSingleton<ISagaRepository, InMemorySagaRepository>();
The in-memory store is:
- deterministic
- instant
- ideal for workflow validation
π§± Design Philosophy
The Saga engine adheres to Franzβs core principles:
| Principle | Meaning |
|---|---|
| Deterministic | Saga identity, mapping, and execution order are guaranteed. |
| Modular | Stores, handlers, and audit sinks are fully pluggable. |
| Transport-agnostic | Kafka, RabbitMQ, HTTP, or custom transports. |
| Zero runtime reflection | Only startup scanning. |
| Safe by default | Built-in validation + null-safety. |
v2.0.1 β Internal Modernization
- Messaging and infrastructure refactored for async, thread-safety, and modern .NET 10 patterns.
- All APIs remain fully backward compatible.
- Tests, listeners, and pipeline components modernized.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Franz.Common.AzureCosmosDB (>= 2.0.2)
- Franz.Common.Business (>= 2.0.2)
- Franz.Common.DependencyInjection (>= 2.0.2)
- Franz.Common.EntityFramework (>= 2.0.2)
- Franz.Common.Errors (>= 2.0.2)
- Franz.Common.Mediator (>= 2.0.2)
- Franz.Common.Messaging (>= 2.0.2)
- Franz.Common.Messaging.Kafka (>= 2.0.2)
- Franz.Common.Messaging.RabbitMQ (>= 2.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.