WorkflowForge 2.0.0
dotnet add package WorkflowForge --version 2.0.0
NuGet\Install-Package WorkflowForge -Version 2.0.0
<PackageReference Include="WorkflowForge" Version="2.0.0" />
<PackageVersion Include="WorkflowForge" Version="2.0.0" />
<PackageReference Include="WorkflowForge" />
paket add WorkflowForge --version 2.0.0
#r "nuget: WorkflowForge, 2.0.0"
#:package WorkflowForge@2.0.0
#addin nuget:?package=WorkflowForge&version=2.0.0
#tool nuget:?package=WorkflowForge&version=2.0.0
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
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
ConcurrentDictionaryfor 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 (
WorkflowForgestatic 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
- Getting Started Guide - Step-by-step tutorial
- Architecture - Design principles and patterns
- Operations Guide - All operation types and patterns
- Events System - Lifecycle events and monitoring
- Configuration - All configuration options
- Extensions - Available extensions
- Samples - 33 hands-on examples
- API Reference - Complete API 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 | Versions 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. |
-
.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.
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