SagaFlow 0.1.20

dotnet add package SagaFlow --version 0.1.20
NuGet\Install-Package SagaFlow -Version 0.1.20
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="SagaFlow" Version="0.1.20" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SagaFlow --version 0.1.20
#r "nuget: SagaFlow, 0.1.20"
#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.
// Install SagaFlow as a Cake Addin
#addin nuget:?package=SagaFlow&version=0.1.20

// Install SagaFlow as a Cake Tool
#tool nuget:?package=SagaFlow&version=0.1.20

SagaFlow

A framework to quickly scaffold an application based on a declarative message DTO format. Define your messages (commands and/or events) as POCO DTOs, and have a fully functional UI and underlying queue/workflow engine automatically built to handle the messages. You write message classes, plus your business logic within handlers and/or sagas - everything else is generated at runtime.

Message-based Design

The design has been inspired by ServiceStack. Rather than repeat the benefits of a message based design here, please read up on the great docs over at ServiceStack. eg:

Getting Started

SagaFlow heavily relies on Rebus under the covers to facilate the messaging and saga/workflow engine. It is worth familiarizing yourself with Rebus and service buses in general.

Define your messages

For example, a command to add a new tenant to your infrastructure:

public record AddTenantCommand
{
	public string? Name { get; init; }
}
Scheduled re-occuring commands

For commands that are executed on a scheduled time.

Using the CommandAttribute, add SagaFlow.Interfaces package to the class library containing your messages. Decorate the message with the CommandAttribute with a cron expression for when the job should run.

[Command("Take a shower", Cron = "30 17 * * * *")] // Takes a shower 5:30 pm everyday 
public record TakeAShower { }

If you don't want to use the SagaFlow.Interfaces package, then you can specify the cron expression as part of the DisplayNameAttribute. This allows you to keep your messages library decoupled from SagaFlow.

[DisplayName("Take a shower [cron: 30 17 * * * *]")] // Takes a shower 5:30 pm everyday 
public record TakeAShower { }

Implement your logic

Application logic is written inside of message handlers. These are facilitated directly via Rebus - read up more here.

public class AddTenantCommandHandler : IHandleMessages<AddTenantCommand>
{
    public Task Handle(AddTenantCommand command)
    {
        var db = ...;
        return db.Tenants.Add(new Tenant { Name = command.Name });
    }
}

Resource lists

As well as executing commands, an application almost always needs to display back some lists of resources (often with further commands possible per resource). To achieve this within SagaFlow, implement a resource provider:

public class TenantProvider : IResourceListProvider<Tenant>
{
    public Task<IList<Tenant>> GetAll()
    {
        var db = ...;
        return db.Tenants;
    }
}

public class Tenant
{
    public Guid Id { get; init; }
    public string Name { get; init; }
}

Register middleware

SagaFlow relies on installing its own middleware to publish your app schema, serve the UI, serve your app's resources and receive messages. Firstly configure your providers, handlers and the core SagaFlow service (and underlying Rebus service bus):

builder.Services.AddScoped<IResourceListProvider<Tenant>>(s => new TenantProvider());
builder.Services.AddScoped<AddTenantCommandHandler>();
builder.Services.AddSagaFlow(o => o
    .AddResourceProvidersFromAssemblyOf<TenantProvider>()
    .AddCommandsOfType<AddTenantCommand>()
    .WithLogging(l => l.Console())
    .WithTransport(t => t.UsePostgreSql(db, "messages", "ops-panel.workflow"))
    .WithSubscriptionStorage(s => s.StoreInPostgres(db, "subscriptions", isCentralized: true))
    .WithSagaStorage(s => s.StoreInPostgres(db, "saga-data", "saga-index"))
    .WithRouting(r => r.TypeBased().MapAssemblyOf<AddTenantCommand>("ops-panel"))
    );

Run your app

Your app will serve the generated SagaFlow UI under todo... Insert screenshot of above UI here

Web Components

Sagaflow can expose a set of web components which can be used in your application.

To import the Sagaflow webcomponents to your front end, you will need to import the SagaFlow javascript module:

<script src="/sagaflow/web-components.js" type="module"></script>

In-case you have setup SagaFlow to use a different route for its webapi calls then you will need to import the SagaFlow module as such:


<script type="module">
    import "/[custom-sagaflow-route]/web-components.js";
    
    window.SagaFlow.initialize("[custom-sagaflow-route]")
</script>

or if you don't want to use the attached SagaFlow from the window object.


<script type="module">
    import sagaFlow from "/[custom-sagaflow-route]/web-components.js";

    sagaFlow.initialize("[custom-sagaflow-route]")
</script>

#### sf-command-form - Command form

A Sagaflow web-component to display a simple form that is used to submit a command message to SagaFlow to run.

For example, if you have a message that looks like this:

```csharp
public class BackupDatabaseServer : ICommand
{
    [DisplayName("Database Server")]
    public DatabaseServerId? DatabaseServerId { get; init; }
    
    [DisplayName("Destination Filename")]
    public string? DestinationFilename { get; init; }
}

With a set of Database Server resources that looks like this:

public class SampleDatabaseServerProvider : IResourceListProvider<DatabaseServer, DatabaseServerId>
{
    private static List<DatabaseServer> databases = Enumerable.Range(0, 10)
        .Select(t => new DatabaseServer
        {
            Id = new DatabaseServerId(String.Format("server-{0:00}",t+1)),
            Name = "Server " + (t + 1),
        })
        .ToList();

    public Task<IList<DatabaseServer>> GetAll()
    {
        return Task.FromResult((IList<DatabaseServer>)databases);
    }
}

public class DatabaseServer : IResource<DatabaseServerId>
{
    public DatabaseServerId Id { get; init; }
    public string Name { get; init; }
}

Then to display the Sagaflow command form to allow the user select a Database Server and to enter a Destination Filename, in your html markup:


<sf-command-form commandId="backup-database-server"></sf-command-form>

The form will display a Drop Down list for the user to select a database server resource, and a input box to enter a destination filename.

You can listen for the following SagaFlow DOM events for when a SagaFlow command has been successful sent to the server to be processed:

  • sf-command-success - The SagaFlow command was successful submitted to the server to be processed;
  • sf-command-error - An error was encountered submitting the command to the server to be processed;
  • sf-command-complete - The process of submitting the command to the server has completed, regardless if it was successful or not.

The following will present a SagaFlow command form, when the user has completed the form and click on the run button it will submit the command to the SagaFlow server to run. The front end will listen for the SagaFlow DOM events to present a message to the user indicating success.

<script>
    document.addEventListener('sf-command-success', (e) => alert("Successful"));
</script>

<sf-command-form commandId="backup-database-server"></sf-command-form>

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

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.20 0 6/13/2024
0.1.19 27 6/13/2024
0.1.18 38 6/12/2024
0.1.14 80 5/22/2024
0.1.13 94 5/17/2024