A .NET workflow engine built on Railway Oriented Programming. Each workflow is a chain of discrete steps with automatic error propagation—if a step fails, the rest are skipped.
Success Track: Input → [Step 1] → [Step 2] → [Step 3] → Output
↓
Failure Track: Exception → [Skip] → [Skip] → Exception
Error handling buries your business logic:
public async Task<OrderReceipt> ProcessOrder(OrderRequest request)
{
var inventory = await _inventory.CheckAsync(request.Items);
if (!inventory.Available)
return Error("Items out of stock");
var payment = await _payments.ChargeAsync(request.PaymentMethod, request.Total);
if (!payment.Success)
return Error("Payment failed");
var shipment = await _shipping.CreateAsync(request.Address, request.Items);
if (shipment == null)
return Error("Shipping setup failed");
return new OrderReceipt(payment, shipment);
}public class ProcessOrderWorkflow : EffectWorkflow<OrderRequest, OrderReceipt>, IProcessOrderWorkflow
{
protected override async Task<Either<Exception, OrderReceipt>> RunInternal(OrderRequest input)
=> Activate(input)
.Chain<CheckInventoryStep>()
.Chain<ChargePaymentStep>()
.Chain<CreateShipmentStep>()
.Resolve();
}Each step is a single-responsibility class with constructor-injected dependencies. If CheckInventoryStep throws, ChargePaymentStep and CreateShipmentStep never run.
-
Railway Oriented Programming — Two-track execution with
Either<Exception, T>from LanguageExt - Effect System — Atomic workflows where effects (database writes, logs) only persist on success
- Metadata Tracking — Every execution recorded with timing, inputs/outputs, and failure details
- Roslyn Analyzer — Compile-time validation that step input/output types chain correctly
-
IDE Extensions — Inlay hints showing
TIn → TOuttypes for each chain call (VSCode, Rider/ReSharper) -
Workflow Discovery — Register workflows once, dispatch by input type via
IWorkflowBus - Job Scheduling — Manifest-based scheduling with retries and dead-lettering via Hangfire
- Web Dashboard — Blazor UI for inspecting registered workflows and execution history
dotnet add package Theauxm.ChainSharp.EffectAdd packages as you need them — persistence, workflow discovery, scheduling, etc. See Packages below.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddChainSharpEffects(o => o.AddEffectWorkflowBus(typeof(Program).Assembly));
var app = builder.Build();
app.Run();| Package | Description | Version |
|---|---|---|
| Theauxm.ChainSharp | Core workflow engine | |
| Theauxm.ChainSharp.Effect | Effects, metadata tracking, DI | |
| Theauxm.ChainSharp.Effect.Data | Persistence abstractions | |
| Theauxm.ChainSharp.Effect.Data.Postgres | PostgreSQL persistence | |
| Theauxm.ChainSharp.Effect.Data.InMemory | In-memory persistence (testing) | |
| Theauxm.ChainSharp.Effect.Orchestration.Mediator | Workflow discovery and routing | |
| Theauxm.ChainSharp.Effect.Orchestration.Scheduler | Manifest-based job scheduling | |
| Theauxm.ChainSharp.Effect.Orchestration.Scheduler.Hangfire | Hangfire integration | |
| Theauxm.ChainSharp.Effect.Dashboard | Blazor web dashboard | |
| Theauxm.ChainSharp.Effect.Provider.Json | JSON effect logging | |
| Theauxm.ChainSharp.Effect.Provider.Parameter | Parameter serialization | |
| Theauxm.ChainSharp.Effect.StepProvider.Logging | Step-level structured logging |
Full Documentation — Getting started, core concepts, usage patterns, architecture, and more.
Contributions are welcome. This project uses Conventional Commits for versioning — see Semantic Release for details.
MIT
Without the help and guidance of Mark Keaton and Douglas Seely this project would not have been possible.