SagaFlow 0.1.35
dotnet add package SagaFlow --version 0.1.35
NuGet\Install-Package SagaFlow -Version 0.1.35
<PackageReference Include="SagaFlow" Version="0.1.35" />
paket add SagaFlow --version 0.1.35
#r "nuget: SagaFlow, 0.1.35"
// Install SagaFlow as a Cake Addin #addin nuget:?package=SagaFlow&version=0.1.35 // Install SagaFlow as a Cake Tool #tool nuget:?package=SagaFlow&version=0.1.35
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:
- Design message-based APIs
- What is a message-based web service
- Advantages of message-based web services
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 | Versions 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. |
-
net6.0
- Microsoft.Extensions.FileProviders.Embedded (>= 6.0.30)
- NCrontab.Scheduler (>= 1.2.10)
- Rebus (>= 8.2.2)
- Rebus.Events (>= 6.0.0)
- Rebus.ServiceProvider (>= 10.1.0)
- SagaFlow.Interfaces (>= 0.1.35)
-
net7.0
- Microsoft.Extensions.FileProviders.Embedded (>= 7.0.19)
- NCrontab.Scheduler (>= 1.2.10)
- Rebus (>= 8.2.2)
- Rebus.Events (>= 6.0.0)
- Rebus.ServiceProvider (>= 10.1.0)
- SagaFlow.Interfaces (>= 0.1.35)
-
net8.0
- Microsoft.Extensions.FileProviders.Embedded (>= 8.0.5)
- NCrontab.Scheduler (>= 1.2.10)
- Rebus (>= 8.2.2)
- Rebus.Events (>= 6.0.0)
- Rebus.ServiceProvider (>= 10.1.0)
- SagaFlow.Interfaces (>= 0.1.35)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on SagaFlow:
Package | Downloads |
---|---|
SagaFlow.AspNetCore
Package Description |
|
SagaFlow.SignalR
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.1.35 | 725 | 7/3/2024 |
0.1.34 | 146 | 7/3/2024 |
0.1.33 | 142 | 7/3/2024 |
0.1.32 | 146 | 7/2/2024 |
0.1.31 | 147 | 7/2/2024 |
0.1.30 | 136 | 7/2/2024 |
0.1.29 | 110 | 7/2/2024 |
0.1.28 | 141 | 7/2/2024 |
0.1.27 | 148 | 7/2/2024 |
0.1.26 | 158 | 7/2/2024 |
0.1.25 | 127 | 6/20/2024 |
0.1.24 | 115 | 6/19/2024 |
0.1.23 | 91 | 6/18/2024 |
0.1.22 | 93 | 6/18/2024 |
0.1.21 | 100 | 6/16/2024 |
0.1.20 | 86 | 6/13/2024 |
0.1.19 | 85 | 6/13/2024 |
0.1.18 | 96 | 6/12/2024 |
0.1.14 | 110 | 5/22/2024 |
0.1.13 | 130 | 5/17/2024 |