Skip to content

sc-starman/saganator

Repository files navigation

🌌 Saganator

Saganator is a modular, extensible .NET framework that implements the Saga Pattern for managing distributed workflows, compensating failures, and maintaining consistency across microservices or complex domains.

It provides:

  • Execution and compensation pipelines
  • Activity chaining
  • State bootstrapping and persistence
  • Integration with Redis, EF Core, and In-Memory stores
  • Complete Dependency Injection support

🧠 Why Saganator?

Modern microservices require reliable state coordination across boundaries. Saganator enables you to:

✅ Model distributed transactions
✅ Chain complex workflows with compensation
✅ Bootstrap saga state from databases
✅ Swap persistence mechanisms easily
✅ Integrate with .NET DI and hosted services


🏗️ Architecture Overview

Saganator separates concerns into 4 key layers:

Layer Responsibility
Saganator.Abstractions Interfaces and contracts
Saganator Core implementation (execution, context)
Saganator.DependencyInjection.Core DI setup and builder flow
Saganator.Persistence.* Pluggable state repositories
Saganator.Loader.* State bootstrappers (e.g., EF Core)

🔁 Saga Lifecycle

  1. A message is dispatched via ISagaCoordinator.
  2. It resolves the registered activity via DI.
  3. Saga state is loaded and locked via ISagaStateManager.
  4. The activity is executed, and optionally compensated.
  5. Final state is saved via the repository.

⚙️ Core Interfaces

Interface Purpose
ISagaState Holds saga identity and state
ISagaContext Execution metadata (timestamps, exceptions)
ISagaExecuteActivity Stateless saga step
ISagaActivity Step with compensation logic
ISagaCoordinator Dispatches messages into the pipeline
ISagaEventProcessor Executes saga logic
ISagaStateManager Locks & updates saga state
ISagaStateRepository Abstracts persistence (Redis/In-Mem/etc.)
ISagaLoader Loads states on startup
IStateMapper Transforms between entities and saga states

🔌 Dependency Injection

services.AddSaga<OrderSagaState, Guid>(builder =>
{
    builder.UseInMemoryPersistence();
    builder.UseSagaActivity<StartOrderActivity, StartOrderMessage>();
});

This will:

  • Register all core services
  • Setup saga event handling
  • Wire up persistence
  • Register hosted state loaders (if needed)

🔗 Chaining Activities

Saganator supports building chains of steps:

public class PaymentChain : SagaChainActivity<PaymentState, Guid, PaymentMessage>
{
    public PaymentChain(...) : base(
        new ValidateCardStep(),
        new ReserveFundsStep(),
        new CapturePaymentStep()) { }

    public override async Task HandleChainAsync(...) { ... }
    public override async Task CompensateChainAsync(...) { ... }
}

Each node is reversible and composable.


🧳 Persistence Options

Module Backend Usage
Saganator.Persistence.InMemory ConcurrentDictionary Fast, simple, non-persistent
Saganator.Persistence.Redis Redis Hashes Distributed, scalable
Saganator.Loader.EfCore EF Core + DbContext Bootstraps saga state from database

In-Memory

builder.UseInMemoryPersistence();

Redis

builder.UseRedisPersistence("localhost:6379");

EF Core Loader

builder.UseEfCoreSagaLoader<OrderMapper, OrderState, Guid, OrderEntity, AppDbContext>();

🧪 Example

public class OrderPlacedActivity : ISagaActivity<OrderState, Guid, OrderPlaced>
{
    public Task<OrderState> InitializeAsync(Guid id, OrderPlaced msg) => Task.FromResult(new OrderState { CorrelationId = id });

    public Guid ResolveCorrelationId(OrderPlaced msg) => msg.OrderId;

    public Task HandleAsync(OrderPlaced msg, ISagaContext<OrderState, Guid> ctx)
    {
        ctx.CurrentState.Status = "Processing";
        return Task.CompletedTask;
    }

    public Task CompensateAsync(OrderPlaced msg, ISagaContext<OrderState, Guid> ctx)
    {
        ctx.CurrentState.Status = "Failed";
        return Task.CompletedTask;
    }

    public Task ContextUpdatedAsync(ISagaContext<OrderState, Guid> ctx) => Task.CompletedTask;
}

🔩 Advanced Features

  • ✅ Deep copy of state using expression trees
  • ✅ HostedService integration for loaders
  • ✅ Conditional mapping between states and entities
  • ✅ Compensation flow for partial rollbacks
  • ✅ Reflection-based activity registration via UseActivities(...)

📦 Project Structure

Saganator/
├── Abstractions/
├── Core/
├── Persistence/
│   ├── InMemory/
│   └── Redis/
├── Loader/
│   └── EfCore/
└── DependencyInjection.Core/

🚀 Getting Started

  1. Install via NuGet (structure ready)
  2. Register services with AddSaga<TState, TId>(...)
  3. Define and register your saga activities
  4. Send messages via ISagaCoordinator

📜 License

MIT License


❤️ Contributions

PRs and discussions are welcome. Build powerful distributed workflows together.


About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published