FlowSmith.Core 0.1.1

dotnet add package FlowSmith.Core --version 0.1.1
                    
NuGet\Install-Package FlowSmith.Core -Version 0.1.1
                    
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="FlowSmith.Core" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FlowSmith.Core" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="FlowSmith.Core" />
                    
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 FlowSmith.Core --version 0.1.1
                    
#r "nuget: FlowSmith.Core, 0.1.1"
                    
#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 FlowSmith.Core@0.1.1
                    
#: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=FlowSmith.Core&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=FlowSmith.Core&version=0.1.1
                    
Install as a Cake Tool

FlowSmith

CI Build NuGet Packages License: MIT

Lightweight workflow engine for .NET with SQL Server persistence.

Overview

FlowSmith v0.1 is a minimal workflow execution engine that supports:

  • Linear multi-step backend workflows
  • Retry with exponential backoff
  • Compensation (reverse execution on failure)
  • SQL Server persistence (ADO.NET, no ORM)
  • Leasing for distributed execution
  • Read-only monitoring UI (Blazor Server)

Architecture

Projects

  1. FlowSmith.Core - Shared contracts and domain models

    • Workflow DSL abstractions (Workflow, IFlowStep, IFlowBuilder)
    • Persistence contracts (IInstanceStore, ILeaseStore, IStepExecutionStore)
    • Application service contracts (IWorkflowCommandService, IWorkflowQueryService)
    • DTOs for queries and monitoring
    • No dependencies (pure contracts)
  2. FlowSmith.Runtime - Execution runtime and persistence

    • SQL Server persistence implementations (ADO.NET)
    • Start/Restart command services
    • Query services for monitoring
    • Extension methods for quick setup:
      • AddFlowSmithRuntime(connectionString) - register all services
      • MapFlowSmithRuntimeApi() - register HTTP API endpoints
    • Does NOT execute steps
    • Depends on: Core
  3. FlowSmith.Worker - Background worker library

    • Polling loop with leasing
    • Workflow execution engine
    • Retry and compensation logic
    • SQL Server persistence implementations (ADO.NET)
    • Uses DI for step instantiation
    • Extension methods for quick setup:
      • AddFlowSmithWorker(connectionString) - register all services
      • RegisterWorkflow<T>(name) - register workflow types
    • Depends on: Core
  4. FlowSmith.UI - Monitoring interface (Blazor Server)

    • Read-only dashboard
    • Instance list and details
    • Step execution timeline
    • Does NOT execute workflows
    • Depends on: Core

Examples Projects

Located in examples/ folder:

  1. FlowSmith.Examples - Sample workflows and worker host

    • OrderProcessingWorkflow (4-step with compensation)
    • Demonstrates retry and compensation patterns
    • Worker host implementation using AddFlowSmithWorker()
    • Depends on: Core, Worker
  2. FlowSmith.Api - REST API for commands and queries

    • Start/Restart workflow endpoints
    • Query endpoints for monitoring
    • Uses FlowSmith.Runtime services via AddFlowSmithRuntime()
    • Hosts FlowSmith.UI Blazor components
    • Depends on: Core, Runtime, UI, ServiceDefaults
  3. FlowSmith.AppHost - .NET Aspire orchestration

    • Orchestrates all services and dependencies
    • Manages SQL Server container
    • Provides monitoring dashboard
    • Depends on: Api (as "flowsmith-api"), Examples (as "flowsmith-worker")
  4. FlowSmith.ServiceDefaults - Aspire service defaults

    • OpenTelemetry configuration
    • Health checks
    • Service discovery
    • Shared by: Api, Examples

Key Principles

  • Deterministic execution: State is fully persisted before each step
  • Crash-safe: Can recover from crashes at any point
  • Explicit SQL: All queries are hand-written, no ORMs
  • Leasing via SQL Server: Uses UPDLOCK + READPAST + ROWLOCK
  • No reflection magic: Explicit workflow registry
  • Worker uses DI: Steps resolved via IServiceProvider

Getting Started

  1. Install Aspire workload:

    dotnet workload install aspire
    
  2. Ensure Docker Desktop is running (required for SQL Server)

  3. Run AppHost:

    dotnet run --project examples/FlowSmith.AppHost/FlowSmith.AppHost.csproj
    

Aspire will automatically:

  • Start SQL Server in a container
  • Create and initialize the database
  • Start FlowSmith.Api (runtime API + UI) and FlowSmith.Examples (worker)
  • Open the Aspire Dashboard for monitoring

Manual Setup (Without Aspire)

Prerequisites

  • .NET 10 SDK
  • SQL Server (LocalDB or full instance)

Database Setup

  1. Create database:

    CREATE DATABASE FlowSmith;
    
  2. Run schema script:

    sqlcmd -S localhost -d FlowSmith -i database/schema.sql
    
  3. Update connection strings in:

    • examples/FlowSmith.Examples/appsettings.json (Worker)
    • examples/FlowSmith.Api/appsettings.json (API + UI)

Build and Run

# Build solution
dotnet build FlowSmith.slnx

# Run tests
dotnet test FlowSmith.slnx

# Run API + UI (in one terminal)
cd examples/FlowSmith.Api
dotnet run

# Run worker (in separate terminal)
cd examples/FlowSmith.Examples
dotnet run

Creating a Workflow

1. Define Steps

public class ReserveInventoryStep : IFlowStep
{
    private readonly ILogger<ReserveInventoryStep> _logger;

    public ReserveInventoryStep(ILogger<ReserveInventoryStep> logger)
    {
        _logger = logger;
    }

    public Task Execute(StepContext context)
    {
        var orderId = context.Data["orderId"];
        var items = context.Data["items"];

        _logger.LogInformation("Reserving inventory for order {OrderId}", orderId);

        // Business logic here
        var reservationId = Guid.NewGuid();
        context.Data["reservationId"] = reservationId;

        return Task.CompletedTask;
    }

    public Task Compensate(StepContext context)
    {
        var reservationId = context.Data.GetValueOrDefault("reservationId");
        _logger.LogWarning("Compensation: Releasing inventory reservation {ReservationId}", reservationId);

        // Reverse the reservation
        return Task.CompletedTask;
    }
}

2. Define Workflow

public class OrderProcessingWorkflow : Workflow
{
    public override void Build(IFlowBuilder builder)
    {
        builder
            .AddStep<ValidateOrderStep>("ValidateOrder")
            .WithRetry(maxAttempts: 3, backoffSeconds: 2)

            .AddStep<ReserveInventoryStep>("ReserveInventory")
            .WithRetry(maxAttempts: 3, backoffSeconds: 5)

            .AddStep<ChargePaymentStep>("ChargePayment")
            .WithRetry(maxAttempts: 2, backoffSeconds: 10)

            .AddStep<SendConfirmationEmailStep>("SendConfirmation")
            .WithRetry(maxAttempts: 2, backoffSeconds: 3);
    }
}

3. Register Workflow

In Worker Program.cs:

using FlowSmith.Worker.Extensions;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

// Register Worker
var connectionString = builder.Configuration.GetConnectionString("FlowSmithDb");
builder.Services.AddFlowSmithWorker(connectionString);

var host = builder.Build();

// Register workflow(s)
host.RegisterWorkflow<OrderProcessingWorkflow>("OrderProcessing");

// Or register multiple workflows:
host.RegisterWorkflows(registry =>
{
    registry.Register<OrderProcessingWorkflow>("OrderProcessing");
    registry.Register<UserRegistrationWorkflow>("UserRegistration");
});

host.Run();

4. Start Workflow

Option A: Via REST API (recommended)

The FlowSmith.Api project demonstrates how to set up the API in just a few lines:

using FlowSmith.Runtime.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Register FlowSmith Runtime
var connectionString = builder.Configuration.GetConnectionString("FlowSmithDb");
builder.Services.AddFlowSmithRuntime(connectionString);

var app = builder.Build();

// Register API endpoints (default prefix: /api/workflows)
app.MapFlowSmithRuntimeApi();

app.Run();

Start workflow via HTTP:

curl -X POST "http://localhost:5000/api/workflows/start?workflowName=OrderProcessing" \
  -H "Content-Type: application/json" \
  -d '{"orderId": 12345, "customerEmail": "user@example.com", "items": ["item1", "item2"]}'

Option B: Via code

using FlowSmith.Runtime.Extensions;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddFlowSmithRuntime(connectionString);
var serviceProvider = services.BuildServiceProvider();

var commandService = serviceProvider.GetRequiredService<IWorkflowCommandService>();

var instanceId = await commandService.StartAsync(
    "OrderProcessing",
    new Dictionary<string, object>
    {
        ["orderId"] = 12345,
        ["customerEmail"] = "user@example.com",
        ["items"] = new[] { "item1", "item2" }
    });

State Machine

Workflow States

  • PendingRunningSucceeded
  • RunningCompensatingCompensated
  • CompensatingCompensationFailed (terminal)

Step States

  • PendingRunningSucceeded
  • RunningFailed (with retry)
  • SucceededCompensated (during compensation)

Testing

Use NSubstitute for mocking (never Moq):

[Fact]
public async Task StartAsync_CreatesInstance()
{
    // Arrange
    var store = Substitute.For<IInstanceStore>();
    var service = new WorkflowCommandService(store, logger);

    // Act
    var instanceId = await service.StartAsync("TestWorkflow");

    // Assert
    await store.Received(1).CreateAsync(
        Arg.Is<WorkflowInstance>(i => i.WorkflowName == "TestWorkflow"),
        Arg.Any<CancellationToken>());
}

Configuration

Connection String

Update in appsettings.json:

{
  "ConnectionStrings": {
    "FlowSmithDb": "Server=localhost;Database=FlowSmith;Integrated Security=true;TrustServerCertificate=true;"
  }
}

Worker Settings

  • Poll interval: 1 second (hardcoded in v0.1)
  • Lease duration: 5 minutes (hardcoded in v0.1)
  • Retry backoff: Exponential (configurable per step)

Roadmap

v0.1 (Current)

  • ✅ Linear workflows
  • ✅ Retry + compensation
  • ✅ SQL Server persistence
  • ✅ Leasing
  • ✅ Basic monitoring

Future

  • Parallel steps
  • Timers and delays
  • Event-driven triggers
  • Distributed cluster support
  • Advanced monitoring and metrics

License

See LICENSE file.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on FlowSmith.Core:

Package Downloads
FlowSmith.UI

FlowSmith UI - Blazor Server monitoring interface. Read-only workflow instance views and timeline visualization.

FlowSmith.Runtime

FlowSmith Runtime - SQL Server persistence implementation, Start/Restart logic, and Query layer. Does not execute steps.

FlowSmith.Worker

FlowSmith Worker - Polling loop, leasing, and step execution engine. Uses DI for workflow instantiation.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 136 3/1/2026
0.1.0 126 3/1/2026