Skip to content

SergiiKram/workflow-core

 
 

Repository files navigation

Idempotent Workflow Core

Build and Tests CodeQL Publish Nuget

This is a fork of the https://github.com/danielgerlag/workflow-core with idempotent workflow instance creation.

The Reference field is used as an idempotency key.

So now you can safely retry workflow creation and have an exactly-once guarantee. Also, in scenarios where you create a workflow to process document, user, external event, etc., you can derive 'reference' from these entities and later find the matching workflow if you failed to persist the returned workflow id the first time.

Additional changes include:

  • EventsPurger to delete old events from the database and batching for WorkflowPurger
  • improved OpenTelemetry tracebility, i.e. linking steps to the parent workflow
  • sequential GUIDs (NewId library) for entities for better database performance
  • support for .NET 8

Example:

<PackageReference Include="SergiiKram.WorkflowCore.Persistence.PostgreSQL" Version="3.9.0.6" />
/// <summary>
/// Trigger <c>HelloWorldWorkflow</c> manually.
/// </summary>
/// <returns></returns>
[HttpPost("hello-world")]
public async Task<IActionResult> HelloWorld(string reference)
{
    try
    {
        var id = await _workflowController.StartWorkflow(HelloWorldWorkflow.WorkflowId, reference: reference);

        return Ok(new { id = id });
    }
    catch (WorkflowExistsException)
    {
        var workflowInstance = await _workflowStore.GetWorkflowInstanceByReference(reference);

        return Ok(new { id = workflowInstance.Id });
    }
}

Only PostgreSQL is supported for now.

Original README

Workflow Core

Workflow Core is a light weight embeddable workflow engine targeting .NET Standard. Think: long running processes with multiple tasks that need to track state. It supports pluggable persistence and concurrency providers to allow for multi-node clusters.

Announcements

New related project: Conductor

Conductor is a stand-alone workflow server as opposed to a library that uses Workflow Core internally. It exposes an API that allows you to store workflow definitions, track running workflows, manage events and define custom steps and scripts for usage in your workflows.

https://github.com/danielgerlag/conductor

Documentation

See Tutorial here.

Fluent API

Define your workflows with the fluent API.

public class MyWorkflow : IWorkflow
{
    public void Build(IWorkflowBuilder<MyData> builder)
    {    
        builder
           .StartWith<Task1>()
           .Then<Task2>()
           .Then<Task3>();
    }
}

JSON / YAML Workflow Definitions

Define your workflows in JSON or YAML, need to install WorkFlowCore.DSL

{
  "Id": "HelloWorld",
  "Version": 1,
  "Steps": [
    {
      "Id": "Hello",
      "StepType": "MyApp.HelloWorld, MyApp",
      "NextStepId": "Bye"
    },        
    {
      "Id": "Bye",
      "StepType": "MyApp.GoodbyeWorld, MyApp"
    }
  ]
}
Id: HelloWorld
Version: 1
Steps:
- Id: Hello
  StepType: MyApp.HelloWorld, MyApp
  NextStepId: Bye
- Id: Bye
  StepType: MyApp.GoodbyeWorld, MyApp

Sample use cases

  • New user workflow
public class MyData
{
	public string Email { get; set; }
	public string Password { get; set; }
	public string UserId { get; set; }
}

public class MyWorkflow : IWorkflow
{
    public void Build(IWorkflowBuilder<MyData> builder)
    {    
        builder
            .StartWith<CreateUser>()
                .Input(step => step.Email, data => data.Email)
                .Input(step => step.Password, data => data.Password)
                .Output(data => data.UserId, step => step.UserId)
           .Then<SendConfirmationEmail>()
               .WaitFor("confirmation", data => data.UserId)
           .Then<UpdateUser>()
               .Input(step => step.UserId, data => data.UserId);
    }
}
  • Saga Transactions
public class MyWorkflow : IWorkflow
{
    public void Build(IWorkflowBuilder<MyData> builder)
    {    
        builder
            .StartWith<CreateCustomer>()
            .Then<PushToSalesforce>()
                .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10))
            .Then<PushToERP>()
                .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10));
    }
}
builder
    .StartWith<LogStart>()
    .Saga(saga => saga
        .StartWith<Task1>()
            .CompensateWith<UndoTask1>()
        .Then<Task2>()
            .CompensateWith<UndoTask2>()
        .Then<Task3>()
            .CompensateWith<UndoTask3>()
    )
    .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromMinutes(10))
    .Then<LogEnd>();

Persistence

Since workflows are typically long running processes, they will need to be persisted to storage between steps. There are several persistence providers available as separate Nuget packages.

Search

A search index provider can be plugged in to Workflow Core, enabling you to index your workflows and search against the data and state of them. These are also available as separate Nuget packages.

Extensions

Samples

Contributors

  • Daniel Gerlag - Initial work
  • Jackie Ja
  • Aaron Scribner
  • Roberto Paterlini

Related Projects

  • Conductor (Stand-alone workflow server built on Workflow Core)

Ports

License

This project is licensed under the MIT License - see the LICENSE.md file for details

About

Lightweight workflow engine for .NET Standard

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C# 99.9%
  • Other 0.1%