WorkflowForge API Reference
Types, interfaces, builders, and extension points in the core library.
Table of Contents
- Core Factory
- Workflow Interfaces
- Operation Interfaces
- Foundry Interfaces
- Smith Interfaces
- Event Interfaces
- Testing Support
- Extension Interfaces
Core Factory
WorkflowForge
Factories for workflows, foundries, and smiths.
public static class WorkflowForge
Methods
CreateWorkflow
public static WorkflowBuilder CreateWorkflow(string? workflowName = null, IServiceProvider? serviceProvider = null)
Creates a WorkflowBuilder. Name and IServiceProvider are optional.
Parameters:
workflowName: Optional workflow name (null lets you callWithNamelater)serviceProvider: Optional DI container for operation resolution
Returns: WorkflowBuilder for fluent configuration
Example:
var workflow = WorkflowForge.CreateWorkflow("OrderProcessing")
.AddOperation(new ValidateOperation())
.Build();
CreateFoundry
public static IWorkflowFoundry CreateFoundry(
string workflowName,
IWorkflowForgeLogger? logger = null,
IDictionary<string, object?>? initialProperties = null,
WorkflowForgeOptions? options = null)
Returns a foundry: logger, initial properties, and options are optional.
Parameters:
workflowName: Foundry name for identificationlogger: Optional logger for the foundryinitialProperties: Optional initial properties for the foundryoptions: Optional execution options for the foundry
Returns: IWorkflowFoundry execution context
Example:
using var foundry = WorkflowForge.CreateFoundry("MyFoundry", logger, initialProperties, options);
CreateSmith
public static IWorkflowSmith CreateSmith(
IWorkflowForgeLogger? logger = null,
IServiceProvider? serviceProvider = null,
WorkflowForgeOptions? options = null)
Returns a smith: logger, IServiceProvider, and options are optional.
Returns: IWorkflowSmith workflow executor
Example:
using var smith = WorkflowForge.CreateSmith();
await smith.ForgeAsync(workflow, foundry);
Workflow Interfaces
IWorkflow
Immutable workflow definition. Dispose when you are done holding native resources (rare).
public interface IWorkflow : IDisposable
{
Guid Id { get; }
string Name { get; }
string? Description { get; }
string Version { get; }
IReadOnlyList<IWorkflowOperation> Operations { get; }
}
Properties:
Id: Unique identifier for the workflowName: Human-readable workflow nameDescription: Optional description for documentationVersion: Workflow version stringOperations: Ordered collection of operations to execute
WorkflowBuilder
Fluent constructor for IWorkflow.
public sealed class WorkflowBuilder
{
WorkflowBuilder WithName(string name);
WorkflowBuilder WithDescription(string? description);
WorkflowBuilder WithVersion(string version);
WorkflowBuilder AddOperation(IWorkflowOperation operation);
WorkflowBuilder AddOperation<T>() where T : class, IWorkflowOperation;
WorkflowBuilder AddOperation(string name, Func<IWorkflowFoundry, CancellationToken, Task> action, Func<IWorkflowFoundry, CancellationToken, Task>? restoreAction = null);
WorkflowBuilder AddOperation(string name, Action<IWorkflowFoundry> action, Action<IWorkflowFoundry>? restoreAction = null);
WorkflowBuilder AddOperations(params IWorkflowOperation[] operations);
WorkflowBuilder AddOperations(IEnumerable<IWorkflowOperation> operations);
WorkflowBuilder AddParallelOperations(params IWorkflowOperation[] operations);
WorkflowBuilder AddParallelOperations(IEnumerable<IWorkflowOperation> operations, int? maxConcurrency = null, TimeSpan? timeout = null, string? name = null);
IWorkflow Build();
}
Notes:
AddOperation<T>()resolves the operation from the providedIServiceProvider.- Inline operations are wrapped into
ActionWorkflowOperationinstances. AddOperations()adds multiple operations to the sequential execution chain.AddParallelOperations()wraps operations in aForEachWorkflowOperationfor parallel execution with shared input.
Operation Interfaces
IWorkflowOperation
Base interface for all workflow operations.
Prefer subclassing
WorkflowOperationBase. You get IDs, default restore, hooks, andForgeAsyncCorewithout reimplementing the interface.
public interface IWorkflowOperation : IDisposable
{
Guid Id { get; }
string Name { get; }
Task<object?> ForgeAsync(
object? inputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
Task RestoreAsync(
object? outputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
}
Properties:
Id: Unique identifier for the operation instanceName: Human-readable operation name
Methods:
ForgeAsync
Task<object?> ForgeAsync(
object? inputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default)
Executes the operation’s main logic.
Parameters:
inputData: Optional input data (prefer foundry properties)foundry: Execution context providing services, logging, and propertiescancellationToken: Cancellation token
Returns: Operation result (optional)
Tip: Read from and write to foundry.Properties using SetProperty/GetPropertyOrDefault helpers instead of relying on inputData/return values.
RestoreAsync
Task RestoreAsync(
object? outputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default)
Executes compensation logic to undo operation effects.
Parameters:
outputData: Output from originalForgeAsynccallfoundry: Execution contextcancellationToken: Cancellation token
Usage: Called automatically by WorkflowSmith if workflow fails. Operations that override RestoreAsync run their compensation logic; operations that use the base class default (no-op) are safely skipped.
WorkflowOperationBase
Abstract base class implementing common operation patterns with lifecycle hooks.
public abstract class WorkflowOperationBase : IWorkflowOperation
{
public virtual Guid Id { get; } = Guid.NewGuid();
public abstract string Name { get; }
// Lifecycle hooks (virtual, override to customize)
protected virtual Task OnBeforeExecuteAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
=> Task.CompletedTask;
protected virtual Task OnAfterExecuteAsync(object? inputData, object? outputData, IWorkflowFoundry foundry, CancellationToken ct)
=> Task.CompletedTask;
// Core logic (abstract, implement in derived classes)
protected abstract Task<object?> ForgeAsyncCore(
object? inputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken);
// Public entry point (orchestrates hooks + core logic)
public Task<object?> ForgeAsync(
object? inputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
public virtual Task RestoreAsync(
object? outputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
public virtual void Dispose() { }
}
Features:
- Per-instance
Id - Default
RestoreAsyncis a no-op unless you override - Override
Disposeonly when you hold unmanaged resources
Lifecycle hooks (v2.0+):
OnBeforeExecuteAsync,OnAfterExecuteAsyncfor cross-cutting work- Implement
ForgeAsyncCorefor the real step body
Example with Hooks:
public class AuditedOperation : WorkflowOperationBase
{
public override string Name => "AuditedOperation";
protected override Task OnBeforeExecuteAsync(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
{
foundry.Logger.LogInformation("Starting operation with input: {Input}", inputData);
return Task.CompletedTask;
}
protected override async Task<object?> ForgeAsyncCore(object? inputData, IWorkflowFoundry foundry, CancellationToken ct)
{
// Your operation logic here
return await ProcessAsync(inputData, ct);
}
protected override Task OnAfterExecuteAsync(object? inputData, object? outputData, IWorkflowFoundry foundry, CancellationToken ct)
{
foundry.Logger.LogInformation("Completed operation with result: {Output}", outputData);
return Task.CompletedTask;
}
}
Recommended base: WorkflowOperationBase fits most custom steps.
IWorkflowOperation<TInput, TOutput>
Typed input and output. Prefer the foundry for shared context anyway.
public interface IWorkflowOperation<TInput, TOutput> : IWorkflowOperation
{
Task<TOutput> ForgeAsync(
TInput input,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
Task RestoreAsync(
TOutput output,
IWorkflowFoundry foundry,
CancellationToken cancellationToken = default);
}
Use when you want typed inputs and outputs. Most code paths should still pass context through the foundry.
Foundry Interfaces
IWorkflowFoundry
Execution context providing services, state, and logging to operations.
public interface IWorkflowFoundry :
IWorkflowExecutionContext,
IWorkflowMiddlewarePipeline,
IOperationLifecycleEvents,
IDisposable
{
Task ForgeAsync(CancellationToken cancellationToken = default);
void ReplaceOperations(IEnumerable<IWorkflowOperation> operations);
bool IsFrozen { get; }
}
Properties:
ExecutionId: Unique identifier for this foundry executionCurrentWorkflow: Workflow being executed (or null)ServiceProvider: DI container for resolving operation dependenciesLogger: Logging abstractionOptions: Execution options for the foundryProperties: Thread-safe dictionary for workflow dataIsFrozen: True whileForgeAsyncis executing (pipeline is immutable)
Notes:
- Property helpers like
SetProperty,GetPropertyOrDefault, andTryGetPropertyare extension methods. - Operations and middleware cannot be added/removed while
ForgeAsyncis running.
Foundry WithOperation Extensions (from FoundryPropertyExtensions):
IWorkflowFoundry WithOperation(this IWorkflowFoundry foundry, string name, Func<IWorkflowFoundry, Task> action, Func<IWorkflowFoundry, Task>? restoreAction = null);
IWorkflowFoundry WithOperation(this IWorkflowFoundry foundry, string name, Action<IWorkflowFoundry> action, Action<IWorkflowFoundry>? restoreAction = null);
Adds inline operations to the foundry. The optional restoreAction provides compensation logic when the workflow fails.
IWorkflowMiddlewarePipeline
Pipeline builder for operations and middleware.
public interface IWorkflowMiddlewarePipeline
{
void AddOperation(IWorkflowOperation operation);
void AddMiddleware(IWorkflowOperationMiddleware middleware);
void AddMiddlewares(IEnumerable<IWorkflowOperationMiddleware> middlewares);
}
Smith Interfaces
IWorkflowSmith
Workflow executor responsible for orchestrating operation execution.
public interface IWorkflowSmith :
IWorkflowLifecycleEvents,
ICompensationLifecycleEvents,
IDisposable
{
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);
}
Methods:
ForgeAsync(...): Executes a workflow using one of the three patterns (smith-managed, data dictionary, or reusable foundry).CreateFoundry(...): Creates a reusable foundry with optional logger/service provider.CreateFoundryFor(...): Creates a foundry pre-associated with a workflow.CreateFoundryWithData(...): Creates a foundry with an initial data dictionary.AddWorkflowMiddleware(...): Adds workflow-level middleware around the entire execution.
Example:
using var smith = WorkflowForge.CreateSmith();
using var foundry = smith.CreateFoundryFor(workflow);
await smith.ForgeAsync(workflow, foundry);
Event Interfaces
IWorkflowLifecycleEvents
Workflow-level lifecycle events.
public interface IWorkflowLifecycleEvents
{
event EventHandler<WorkflowStartedEventArgs>? WorkflowStarted;
event EventHandler<WorkflowCompletedEventArgs>? WorkflowCompleted;
event EventHandler<WorkflowFailedEventArgs>? WorkflowFailed;
}
Implemented By: IWorkflowSmith
Events:
WorkflowStarted: raised immediately before the first operation runsWorkflowCompleted: raised after the last operation succeedsWorkflowFailed: raised when execution aborts with an error
Example:
smith.WorkflowStarted += (s, e) =>
Console.WriteLine($"Started: {e.Foundry.CurrentWorkflow?.Name}");
smith.WorkflowCompleted += (s, e) =>
Console.WriteLine($"Completed in {e.Duration.TotalMilliseconds}ms");
IOperationLifecycleEvents
Operation-level lifecycle events.
public interface IOperationLifecycleEvents
{
event EventHandler<OperationStartedEventArgs>? OperationStarted;
event EventHandler<OperationCompletedEventArgs>? OperationCompleted;
event EventHandler<OperationFailedEventArgs>? OperationFailed;
}
Implemented By: IWorkflowFoundry
Events:
OperationStarted: raised as each operation beginsOperationCompleted: raised after a successful operation finishesOperationFailed: raised when an operation throws or faults
Example:
foundry.OperationStarted += (s, e) =>
Console.WriteLine($"Operation {e.Operation.Name} started");
foundry.OperationCompleted += (s, e) =>
Console.WriteLine($"Operation {e.Operation.Name} completed");
ICompensationLifecycleEvents
Compensation (saga pattern) lifecycle events.
public interface ICompensationLifecycleEvents
{
event EventHandler<CompensationTriggeredEventArgs>? CompensationTriggered;
event EventHandler<CompensationCompletedEventArgs>? CompensationCompleted;
event EventHandler<OperationRestoreStartedEventArgs>? OperationRestoreStarted;
event EventHandler<OperationRestoreCompletedEventArgs>? OperationRestoreCompleted;
event EventHandler<OperationRestoreFailedEventArgs>? OperationRestoreFailed;
}
Implemented By: IWorkflowSmith
Events:
CompensationTriggered: raised when a failure kicks off the compensation passCompensationCompleted: raised after everyRestoreAsyncattempt finishesOperationRestoreStarted: raised immediately before a givenRestoreAsyncOperationRestoreCompleted: raised afterRestoreAsyncsucceedsOperationRestoreFailed: raised whenRestoreAsyncthrows
Example:
smith.CompensationTriggered += (s, e) =>
Console.WriteLine($"Compensating: {e.Reason} (failed: {e.FailedOperationName})");
smith.OperationRestoreStarted += (s, e) =>
Console.WriteLine($"Rolling back {e.Operation.Name}");
Core Options
public sealed class WorkflowForgeOptions : WorkflowForgeOptionsBase
{
public bool Enabled { get; set; } = true;
public string SectionName { get; }
public int MaxConcurrentWorkflows { get; set; } = 0;
public bool ContinueOnError { get; set; } = false;
public bool FailFastCompensation { get; set; } = false;
public bool ThrowOnCompensationError { get; set; } = false;
public bool EnableOutputChaining { get; set; } = true;
public override IList<string> Validate();
public override object Clone();
}
Notes:
WorkflowForgeOptionsinherits fromWorkflowForgeOptionsBasefor consistentEnabled,SectionName,Validate(), andClone()behavior.
Testing Support
FakeWorkflowFoundry
Test double implementing IWorkflowFoundry.
Package: WorkflowForge.Testing
public class FakeWorkflowFoundry : IWorkflowFoundry
{
// Configurable properties
public Guid ExecutionId { get; set; }
public ConcurrentDictionary<string, object?> Properties { get; }
public IWorkflowForgeLogger Logger { get; set; }
public WorkflowForgeOptions Options { get; set; }
public IServiceProvider? ServiceProvider { get; set; }
// Test assertions
public IReadOnlyList<IWorkflowOperation> Operations { get; }
public IReadOnlyList<IWorkflowOperationMiddleware> Middlewares { get; }
public IReadOnlyList<IWorkflowOperation> ExecutedOperations { get; }
// Test helpers
public void Reset();
public void TrackExecution(IWorkflowOperation operation);
}
Behavior:
- Records operations for assertions
- Swap logger, options, and
IServiceProvider Reset()clears state between tests
Example Usage:
[Fact]
public async Task MyOperation_Should_SetProperty()
{
// Arrange
var foundry = new FakeWorkflowFoundry();
var operation = new MyCustomOperation();
// Act
await operation.ForgeAsync("input", foundry, CancellationToken.None);
// Assert
Assert.True(foundry.Properties.ContainsKey("myKey"));
Assert.Equal("expectedValue", foundry.Properties["myKey"]);
}
[Fact]
public async Task Workflow_Should_ExecuteAllOperations()
{
// Arrange
var foundry = new FakeWorkflowFoundry();
foundry.AddOperation(new StepOneOperation());
foundry.AddOperation(new StepTwoOperation());
// Act
await foundry.ForgeAsync();
// Assert
Assert.Equal(2, foundry.ExecutedOperations.Count);
}
Extension Interfaces
IWorkflowOperationMiddleware
Middleware for operation execution pipeline.
public interface IWorkflowOperationMiddleware
{
Task<object?> ExecuteAsync(
IWorkflowOperation operation,
IWorkflowFoundry foundry,
object? inputData,
Func<CancellationToken, Task<object?>> next,
CancellationToken cancellationToken);
}
Methods:
ExecuteAsync
Task<object?> ExecuteAsync(
IWorkflowOperation operation,
IWorkflowFoundry foundry,
object? inputData,
Func<CancellationToken, Task<object?>> next,
CancellationToken cancellationToken)
Executes middleware logic in the operation pipeline.
Parameters:
operation: Operation being executedfoundry: Execution contextnext: Delegate to call next middleware/operationcancellationToken: Cancellation token
Returns: Operation result
Example:
public class TimingMiddleware : IWorkflowOperationMiddleware
{
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);
}
finally
{
sw.Stop();
_logger.LogInformation("Operation {Op} took {Ms}ms",
operation.Name, sw.ElapsedMilliseconds);
}
}
}
IWorkflowForgeLogger
Logging abstraction for framework and operations.
public interface IWorkflowForgeLogger
{
void LogTrace(string message, params object[] args);
void LogDebug(string message, params object[] args);
void LogInformation(string message, params object[] args);
void LogWarning(string message, params object[] args);
void LogError(Exception? exception, string message, params object[] args);
void LogCritical(Exception? exception, string message, params object[] args);
}
Standard levels: Trace through Critical
Shipped implementations: ConsoleLogger, NullLogger, plus Serilog via the logging extension
ISystemTimeProvider
Time abstraction for testability.
public interface ISystemTimeProvider
{
DateTimeOffset UtcNow { get; }
}
Lets tests fix the clock. Production code normally uses SystemTimeProvider.
Example:
public class TimeSensitiveOperation : WorkflowOperationBase
{
private readonly ISystemTimeProvider _timeProvider;
public TimeSensitiveOperation(ISystemTimeProvider timeProvider)
{
_timeProvider = timeProvider;
}
protected override async Task<object?> ForgeAsyncCore(
object? inputData,
IWorkflowFoundry foundry,
CancellationToken cancellationToken)
{
var now = _timeProvider.UtcNow;
// Time-dependent logic
return inputData;
}
}
Built-In Operations
DelayOperation
Introduces a delay in workflow execution.
public class DelayOperation : WorkflowOperationBase
{
public DelayOperation(TimeSpan delay);
}
Example:
var workflow = WorkflowForge.CreateWorkflow("DelayedWorkflow")
.AddOperation(new DelayOperation(TimeSpan.FromSeconds(2)))
.Build();
LoggingOperation
Logs a message during workflow execution.
public class LoggingOperation : WorkflowOperationBase
{
public LoggingOperation(string message, WorkflowForgeLogLevel logLevel = WorkflowForgeLogLevel.Information, string? name = null);
}
Example:
var workflow = WorkflowForge.CreateWorkflow("LoggingWorkflow")
.AddOperation(new LoggingOperation("Starting workflow"))
.Build();
ConditionalWorkflowOperation
Executes different operations based on a condition.
public class ConditionalWorkflowOperation : WorkflowOperationBase
{
// Async condition with input data
public ConditionalWorkflowOperation(
Func<object?, IWorkflowFoundry, CancellationToken, Task<bool>> condition,
IWorkflowOperation trueOperation,
IWorkflowOperation? falseOperation = null,
string? name = null,
Guid? id = null);
// Sync condition with input data
public ConditionalWorkflowOperation(
Func<object?, IWorkflowFoundry, bool> condition,
IWorkflowOperation trueOperation,
IWorkflowOperation? falseOperation = null,
string? name = null,
Guid? id = null);
// Simple condition (foundry-only) via factory
public static ConditionalWorkflowOperation Create(
Func<IWorkflowFoundry, bool> condition,
IWorkflowOperation trueOperation,
IWorkflowOperation? falseOperation = null,
string? name = null);
}
Example:
var conditionalOp = new ConditionalWorkflowOperation(
condition: (input, f) => f.GetPropertyOrDefault<decimal>("Amount") > 100,
trueOperation: new HighValueOperation(),
falseOperation: new StandardOperation());
ForEachWorkflowOperation
Executes multiple operations concurrently with configurable data distribution.
public sealed class ForEachWorkflowOperation : WorkflowOperationBase
{
// Factory methods
public static ForEachWorkflowOperation CreateSharedInput(
IEnumerable<IWorkflowOperation> operations,
TimeSpan? timeout = null,
int? maxConcurrency = null,
string? name = null);
public static ForEachWorkflowOperation CreateSplitInput(
IEnumerable<IWorkflowOperation> operations,
TimeSpan? timeout = null,
int? maxConcurrency = null,
string? name = null);
public static ForEachWorkflowOperation CreateNoInput(
IEnumerable<IWorkflowOperation> operations,
TimeSpan? timeout = null,
int? maxConcurrency = null,
string? name = null);
}
Data Strategies:
CreateSharedInput: All operations receive the same input dataCreateSplitInput: Input collection is split and distributed among operationsCreateNoInput: Operations receive null input
Example:
// Execute multiple operations in parallel with shared input
var forEachOp = ForEachWorkflowOperation.CreateSharedInput(
new IWorkflowOperation[] { new ProcessA(), new ProcessB(), new ProcessC() },
maxConcurrency: 4,
name: "ProcessAll");
// Throttled execution with 2 concurrent operations max
var throttledOp = ForEachWorkflowOperation.CreateSplitInput(
operations,
maxConcurrency: 2);