WorkflowForge 2.0.0

dotnet add package WorkflowForge --version 2.0.0
                    
NuGet\Install-Package WorkflowForge -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WorkflowForge" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WorkflowForge" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="WorkflowForge" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WorkflowForge --version 2.0.0
                    
#r "nuget: WorkflowForge, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WorkflowForge@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WorkflowForge&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=WorkflowForge&version=2.0.0
                    
Install as a Cake Tool

WorkflowForge Core

<p align="center"> <img src="https://raw.githubusercontent.com/animatlabs/workflow-forge/main/icon.png" alt="WorkflowForge" width="120" height="120"> </p>

Zero-dependency workflow orchestration framework for .NET

NuGet License

Overview

WorkflowForge Core is the foundational workflow orchestration library providing industrial-strength workflow capabilities with zero external dependencies. Built on the forge/foundry/smith metaphor, it delivers microsecond-level performance, built-in compensation (Saga pattern), and a flexible middleware pipeline.

Key Features

  • Zero Dependencies: Absolutely no external NuGet packages required
  • Microsecond Performance: Sub-20μs operation execution in high-performance scenarios
  • Saga Pattern: Built-in compensation/rollback support via RestoreAsync
  • Middleware Pipeline: Extensible Russian Doll pattern for cross-cutting concerns
  • Dictionary-Based Data Flow: Thread-safe ConcurrentDictionary for shared context
  • Type-Safe Operations: Optional IWorkflowOperation<TInput, TOutput> for explicit data contracts
  • Configurable Output Chaining: Pass operation output to next input via WorkflowForgeOptions.EnableOutputChaining
  • Event System: SRP-compliant lifecycle events (Workflow, Operation, Compensation)
  • Builder Pattern: Fluent API for workflow construction
  • .NET Standard 2.0: Compatible with .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+

Installation

dotnet add package WorkflowForge

Requirements: .NET Standard 2.0 or later

Quick Start

1. Create Your First Workflow

using WorkflowForge;
using WorkflowForge.Extensions;

// Create workflow
var workflow = WorkflowForge.CreateWorkflow("OrderProcessing")
    .AddOperation("ValidateOrder", new ValidateOrderOperation())
    .AddOperation("ChargePayment", new ChargePaymentOperation())
    .AddOperation("ReserveInventory", new ReserveInventoryOperation())
    .AddOperation("CreateShipment", new CreateShipmentOperation())
    .Build();

// Create execution environment
using var foundry = WorkflowForge.CreateFoundry("Order-12345");
foundry.SetProperty("OrderId", "12345");
foundry.SetProperty("CustomerId", "CUST-001");

// Execute workflow
using var smith = WorkflowForge.CreateSmith();
await smith.ForgeAsync(workflow, foundry);

// Read results
var shipmentId = foundry.GetPropertyOrDefault<string>("ShipmentId");

2. Inline Operations (Quick Prototyping)

var workflow = WorkflowForge.CreateWorkflow("QuickDemo")
    .AddOperation("Step1", async (foundry, ct) =>
    {
        foundry.Logger.LogInformation("Executing Step 1");
        foundry.SetProperty("Result", "Success");
        await Task.CompletedTask;
    })
    .AddOperation("Step2", async (foundry, ct) =>
    {
        var result = foundry.GetPropertyOrDefault<string>("Result");
        foundry.Logger.LogInformation("Step 2 received: {Result}", result);
        await Task.CompletedTask;
    })
    .Build();

using var smith = WorkflowForge.CreateSmith();
using var foundry = WorkflowForge.CreateFoundry("QuickDemo");
await smith.ForgeAsync(workflow, foundry);

Architecture

The Industrial Metaphor

WorkflowForge uses an industrial manufacturing metaphor:

  • Forge (WorkflowForge static class): Main factory for creating workflows and components
  • Foundry (IWorkflowFoundry): Execution environment with shared data/context
  • Smith (IWorkflowSmith): Orchestrator that executes workflows through foundries
  • Operation (IWorkflowOperation): Individual executable task

Data Flow: All workflow data lives in foundry.Properties (ConcurrentDictionary) by default. Use type-safe operations (IWorkflowOperation<TInput, TOutput>) only when explicit contracts are needed.

Core Abstractions

IWorkflowFoundry - Execution Context
public interface IWorkflowFoundry :
    IWorkflowExecutionContext,
    IWorkflowMiddlewarePipeline,
    IOperationLifecycleEvents,
    IDisposable
{
    Task ForgeAsync(CancellationToken cancellationToken = default);
    void ReplaceOperations(IEnumerable<IWorkflowOperation> operations);
    bool IsFrozen { get; }
}
IWorkflowSmith - Orchestration Engine
public interface IWorkflowSmith : IDisposable,
    IWorkflowLifecycleEvents,
    ICompensationLifecycleEvents
{
    Task ForgeAsync(IWorkflow workflow, CancellationToken cancellationToken = default);
    Task ForgeAsync(IWorkflow workflow, ConcurrentDictionary<string, object?> data, CancellationToken cancellationToken = default);
    Task ForgeAsync(IWorkflow workflow, IWorkflowFoundry foundry, CancellationToken cancellationToken = default);

    IWorkflowFoundry CreateFoundry(IWorkflowForgeLogger? logger = null, IServiceProvider? serviceProvider = null);
    IWorkflowFoundry CreateFoundryFor(IWorkflow workflow, IWorkflowForgeLogger? logger = null, IServiceProvider? serviceProvider = null);
    IWorkflowFoundry CreateFoundryWithData(ConcurrentDictionary<string, object?> data, IWorkflowForgeLogger? logger = null, IServiceProvider? serviceProvider = null);

    void AddWorkflowMiddleware(IWorkflowMiddleware middleware);
}
IWorkflowOperation - Executable Task
public interface IWorkflowOperation : IDisposable
{
    Guid Id { get; }
    string Name { get; }
    bool SupportsRestore { get; }
    
    Task<object?> ForgeAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken cancellationToken);
    Task RestoreAsync(object? outputData, IWorkflowFoundry foundry, CancellationToken cancellationToken);
}

Built-in Operations

1. DelegateWorkflowOperation

var workflow = WorkflowForge.CreateWorkflow("DelegateExample")
    .AddOperation("Process", async (foundry, ct) => 
    {
        var input = foundry.GetPropertyOrDefault<string>("Input");
        foundry.Logger.LogInformation("Processing: {Input}", input);
        await Task.Delay(100, ct);
        foundry.SetProperty("Output", $"Processed: {input}");
    })
    .Build();

2. ActionWorkflowOperation

var workflow = WorkflowForge.CreateWorkflow("ActionExample")
    .AddOperation("LogStep", async (foundry, ct) =>
    {
        foundry.Logger.LogInformation("Executing step");
        foundry.SetProperty("Timestamp", DateTime.UtcNow);
        await Task.CompletedTask;
    })
    .Build();

3. ConditionalWorkflowOperation

var conditionalOp = ConditionalWorkflowOperation.Create(
    condition: foundry => foundry.GetPropertyOrDefault<bool>("IsPremium"),
    trueOperation: new PremiumProcessingOperation(),
    falseOperation: new StandardProcessingOperation()
);

workflow.AddOperation("ProcessByTier", conditionalOp);

4. ForEachWorkflowOperation

// Execute multiple operations concurrently (shared input to all)
var forEachOp = ForEachWorkflowOperation.CreateSharedInput(
    new[] { new ProcessItemOperation(), new ValidateOperation() },
    maxConcurrency: 4,
    name: "ProcessItems"
);

// Or split input collection among operations
var splitOp = ForEachWorkflowOperation.CreateSplitInput(
    itemOperations,
    maxConcurrency: 2
);

5. DelayOperation

var delayOp = new DelayOperation(TimeSpan.FromSeconds(5));
workflow.AddOperation("Wait", delayOp);

6. LoggingOperation

var logOp = new LoggingOperation("Order processing completed", logger);
workflow.AddOperation("LogCompletion", logOp);

Custom Operations

Method 1: Inherit WorkflowOperationBase

public class CalculateTotalOperation : WorkflowOperationBase
{
    public override string Name => "CalculateTotal";
    public override bool SupportsRestore => false;

    protected override async Task<object?> ForgeAsyncCore(
        object? inputData,
        IWorkflowFoundry foundry,
        CancellationToken cancellationToken = default)
    {
        var items = foundry.GetPropertyOrDefault<List<OrderItem>>("Items");
        var total = items.Sum(x => x.Price * x.Quantity);
        
        foundry.SetProperty("Total", total);
        foundry.Logger.LogInformation("Calculated total: {Total}", total);
        
        return total;
    }
}

Method 2: Type-Safe Operations (Optional)

public class ValidateOrderOperation : WorkflowOperationBase<Order, ValidationResult>
{
    public override string Name => "ValidateOrder";

    protected override async Task<ValidationResult> ForgeAsyncCore(
        Order input,
        IWorkflowFoundry foundry,
        CancellationToken cancellationToken = default)
    {
        if (input == null || input.Items.Count == 0)
        {
            return new ValidationResult { IsValid = false, Errors = ["No items in order"] };
        }
        
        return new ValidationResult { IsValid = true };
    }
}

Compensation (Saga Pattern)

Implement RestoreAsync for rollback capabilities:

public class ChargePaymentOperation : WorkflowOperationBase
{
    public override string Name => "ChargePayment";
    public override bool SupportsRestore => true;

    protected override async Task<object?> ForgeAsyncCore(
        object? inputData,
        IWorkflowFoundry foundry,
        CancellationToken cancellationToken)
    {
        var orderId = foundry.GetPropertyOrDefault<string>("OrderId");
        var amount = foundry.GetPropertyOrDefault<decimal>("Total");
        
        var paymentId = await _paymentService.ChargeAsync(orderId, amount, cancellationToken);
        
        foundry.SetProperty("PaymentId", paymentId);
        foundry.Logger.LogInformation("Payment charged: {PaymentId}", paymentId);
        
        return paymentId;
    }
    
    public override async Task RestoreAsync(
        object? outputData,
        IWorkflowFoundry foundry,
        CancellationToken cancellationToken)
    {
        var paymentId = foundry.GetPropertyOrDefault<string>("PaymentId");
        
        if (!string.IsNullOrEmpty(paymentId))
        {
            await _paymentService.RefundAsync(paymentId, cancellationToken);
            foundry.Logger.LogInformation("Payment refunded: {PaymentId}", paymentId);
        }
    }
}

Middleware

Add cross-cutting concerns using the middleware pipeline:

public class TimingMiddleware : IWorkflowOperationMiddleware
{
    private readonly IWorkflowForgeLogger _logger;
    
    public TimingMiddleware(IWorkflowForgeLogger logger)
    {
        _logger = logger;
    }
    
    public async Task<object?> ExecuteAsync(
        IWorkflowOperation operation,
        IWorkflowFoundry foundry,
        object? inputData,
        Func<CancellationToken, Task<object?>> next,
        CancellationToken cancellationToken)
    {
        var sw = Stopwatch.StartNew();
        
        try
        {
            return await next(cancellationToken).ConfigureAwait(false);
        }
        finally
        {
            sw.Stop();
            _logger.LogInformation(
                "Operation {Name} completed in {Duration}ms",
                operation.Name,
                sw.Elapsed.TotalMilliseconds);
        }
    }
}

// Add to foundry
foundry.AddMiddleware(new TimingMiddleware(logger));

Event System

Subscribe to lifecycle events:

// Workflow-level events (from Smith)
smith.WorkflowStarted += (s, e) => 
    Console.WriteLine($"Started: {e.WorkflowName}");
smith.WorkflowCompleted += (s, e) => 
    Console.WriteLine($"Completed in {e.Duration.TotalMilliseconds}ms");
smith.WorkflowFailed += (s, e) => 
    Console.WriteLine($"Failed: {e.Exception.Message}");

// Operation-level events (from Foundry)
foundry.OperationStarted += (s, e) => 
    Console.WriteLine($"Op started: {e.OperationName}");
foundry.OperationCompleted += (s, e) => 
    Console.WriteLine($"Op completed: {e.OperationName}");
foundry.OperationFailed += (s, e) => 
    Console.WriteLine($"Op failed: {e.OperationName}");

// Compensation events (from Foundry)
foundry.CompensationTriggered += (s, e) => 
    Console.WriteLine("Rollback triggered");
foundry.OperationRestoreStarted += (s, e) => 
    Console.WriteLine($"Restoring: {e.OperationName}");

Configuration

WorkflowForgeOptions inherits from WorkflowForgeOptionsBase, providing Enabled, SectionName, Validate(), and Clone() for consistent options behavior.

Programmatic Configuration

var options = new WorkflowForgeOptions
{
    Enabled = true,
    MaxConcurrentWorkflows = 10,
    ContinueOnError = false,
    FailFastCompensation = false,
    ThrowOnCompensationError = true,
    EnableOutputChaining = true
};

var foundry = WorkflowForge.CreateFoundry("MyWorkflow", options: options);

Options Pattern (appsettings.json)

{
  "WorkflowForge": {
    "Enabled": true,
    "MaxConcurrentWorkflows": 10,
    "ContinueOnError": false,
    "FailFastCompensation": false,
    "ThrowOnCompensationError": true,
    "EnableOutputChaining": true
  }
}
services.AddWorkflowForge(configuration);
var smith = services.BuildServiceProvider().GetRequiredService<IWorkflowSmith>();

Performance

WorkflowForge Core is optimized for production workloads (12 scenarios benchmarked, 50 iterations):

  • Execution Speed: 11-540x faster than competitors
  • State Machine: Up to 540x faster (highest advantage)
  • Memory: 9-573x less allocation than competitors
  • Concurrency: Near-perfect linear scaling (16x speedup for 16 workflows)
Scenario WorkflowForge Workflow Core Elsa Advantage
Sequential (10 ops) 247μs 6,531μs 17,617μs 26-71x
State Machine (25) 68μs 20,624μs 36,695μs 303-540x
Concurrent (8 workers) 356μs 38,833μs 94,018μs 109-264x

See Performance Documentation for all 12 scenarios.

Testing

All interfaces are mockable for comprehensive testing:

using Moq;
using Xunit;

public class WorkflowTests
{
    [Fact]
    public async Task Should_Execute_Operations_In_Order()
    {
        // Arrange
        var executionOrder = new List<string>();
        
        var workflow = WorkflowForge.CreateWorkflow("Test")
            .WithOperation("Step1", async (foundry) => executionOrder.Add("Step1"))
            .WithOperation("Step2", async (foundry) => executionOrder.Add("Step2"))
            .WithOperation("Step3", async (foundry) => executionOrder.Add("Step3"))
            .Build();
        
        using var foundry = WorkflowForge.CreateFoundry("Test");
        using var smith = WorkflowForge.CreateSmith();
        
        // Act
        await smith.ForgeAsync(workflow, foundry);
        
        // Assert
        Assert.Equal(new[] { "Step1", "Step2", "Step3" }, executionOrder);
    }
}

Documentation

Extensions

While Core has zero dependencies, extend functionality with official packages:

  • WorkflowForge.Testing - Unit testing utilities (FakeWorkflowFoundry)
  • WorkflowForge.Extensions.Logging.Serilog - Structured logging
  • WorkflowForge.Extensions.Resilience - Retry strategies (zero dependencies)
  • WorkflowForge.Extensions.Resilience.Polly - Advanced resilience with Polly
  • WorkflowForge.Extensions.Validation - DataAnnotations-based validation
  • WorkflowForge.Extensions.Audit - Comprehensive audit logging
  • WorkflowForge.Extensions.Persistence - Workflow state persistence
  • WorkflowForge.Extensions.Persistence.Recovery - Recovery coordinator
  • WorkflowForge.Extensions.Observability.Performance - Performance monitoring
  • WorkflowForge.Extensions.Observability.HealthChecks - Health check integration
  • WorkflowForge.Extensions.Observability.OpenTelemetry - Distributed tracing

Dependency isolation: Extensions internalize third-party libraries with ILRepack where appropriate, while keeping Microsoft/System assemblies external.

License

MIT License - see LICENSE for details.


Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (11)

Showing the top 5 NuGet packages that depend on WorkflowForge:

Package Downloads
WorkflowForge.Extensions.Resilience

Resilience and retry extension for WorkflowForge workflow engine. Provides circuit breakers, retry strategies, and timeout management for robust workflow execution.

WorkflowForge.Extensions.Logging.Serilog

Serilog adapter for WorkflowForge providing professional structured logging capabilities with rich context and correlation.

WorkflowForge.Extensions.Observability.OpenTelemetry

OpenTelemetry integration for WorkflowForge providing distributed tracing, metrics collection, and observability instrumentation for comprehensive workflow monitoring and debugging.

WorkflowForge.Extensions.Observability.HealthChecks

Health monitoring and diagnostics extension for WorkflowForge providing comprehensive health checks, dependency monitoring, and system status reporting for production workflows.

WorkflowForge.Extensions.Observability.Performance

Performance monitoring and profiling extension for WorkflowForge providing detailed metrics, execution timing, memory usage tracking, and performance optimization insights for production workflows.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 405 1/26/2026
1.1.0 480 8/9/2025
1.0.1 447 6/3/2025

Major release v2.0.0:
- NEW: Validation extension using DataAnnotations
- NEW: Audit extension for compliance and operational monitoring
- BREAKING: Events system refactored for SRP (3 focused interfaces)
- ISystemTimeProvider injected via DI for testability
- 446 comprehensive tests, 100% passing
- Enterprise-ready production workflows