GithubHelp home page GithubHelp logo

jplane / durablestatemachines Goto Github PK

View Code? Open in Web Editor NEW
8.0 4.0 2.0 1.25 MB

Bringing the power of hierarchical state machines to a .NET Core runtime near you.

License: MIT License

C# 99.87% PowerShell 0.13%
state-machine dotnet-core durabletask finite-state-machine automata statechart durable-functions

durablestatemachines's Introduction

Durable State Machines

DSM implements a declarative state machine programming model as an extension for Azure Durable Functions. It's based on the "hierarchical state machine" concepts behind statecharts and the SCXML W3C Recommendation.

DurableStateMachines.Core Metadata + interpreter (standalone, in-memory component... no durable bits) NuGet
DurableStateMachines.Functions Durable Functions integration NuGet
DurableStateMachines.Client Durable Functions client API NuGet

What are statecharts?

The best definition comes from here:

The primary feature of statecharts is that states can be organized in a hierarchy: A statechart is a state machine where each state in the state machine may define its own subordinate state machines, called substates. Those states can again define substates.

The utility of traditional state machines goes down as system complexity increases, due to state explosion. Also, state machines by themselves are merely a description of behavior, not behavior itself. Statecharts (and DSM) address both of these limitations.

Goals

DSM aims to provide a full-featured statechart implementation for Azure Durable Functions. This means you can run state machines locally on your laptop, or anywhere Azure Functions will run (Kubernetes, Azure serverless, edge compute, etc.).

Some specific design and implementation choices:

  • An abstraction for describing statechart definitions and an initial implementation

  • Abstractions for both pull- and push-based communication with external systems; talk to all your favorite native cloud services from within your statechart!

  • In addition to parent-child state relationships within a single statechart, there is also support for parent-child relationships between statechart instances (execute statechart A within the context of statechart B, etc.)

  • An extensible telemetry service implemented with SignalR... observe state machine execution as it occurs, etc.

Getting Started

Statecharts support standard state machine concepts like atomic and parallel states, state transitions, actions within states, etc.

Statecharts also support advanced concepts like history states, event-based transitions, and nested or compound state hierarchies.

In SCDN you define statecharts using declarative metadata in JSON. See below for examples.

For advanced scenarios, you can also define your own metadata syntax and map it to primitives the SCDN engines can interpret and execute directly.

Usage

Run statecharts as a Durable Function orchestration, using the standard IDurableClient APIs.

Option 1: strongly-typed C#

// in your DF app (on the server)

public class Definitions
{
    [StateMachineDefinition("my-state-machine")]
    public StateMachine<(int x, int y)> MyStateMachine =>
        new StateMachine<(int x, int y)>
        {
            Id = "test",
            States =
            {
                new AtomicState<(int x, int y)>
                {
                    Id = "state1",
                    OnEntry = new OnEntryExit<(int x, int y)>
                    {
                        Actions =
                        {
                            new Assign<(int x, int y)>
                            {
                                Target = d => d.x,
                                ValueFunction = data => data.x + 1
                            }
                        }
                    },
                    OnExit = new OnEntryExit<(int x, int y)>
                    {
                        Actions =
                        {
                            new Assign<(int x, int y)>
                            {
                                Target = d => d.x,
                                ValueFunction = data => data.x + 1
                            }
                        }
                    },
                    Transitions =
                    {
                        new Transition<(int x, int y)>
                        {
                            Targets = { "alldone" }
                        }
                    }
                },
                new FinalState<(int x, int y)>
                {
                    Id = "alldone"
                }
            }
        };
}
// on the client

IDurableClient client = GetDurableFunctionClient();     // obtain via dependency injection

var data = (5, 0);                                      // any serializable type

var instanceId = await client.StartNewStateMachineAsync("my-state-machine", data);

var result = await client.WaitForStateMachineCompletionAsync(instanceId);

data = result.ToOutput<(int x, int y)>();

Console.WriteLine(data.x);

Option 2: HTTP + JSON


// from any client

HTTP POST /runtime/webhooks/durabletask/orchestrators/statemachine-definition

{
  'input': {
    'items': [ 1, 2, 3, 4, 5 ],
    'sum': 0
  },
  'statemachine': {
    'id': 'test',
    'initialstate': 'loop',
    'states': [
      {
        'id': 'loop',
        'type': 'atomic',
        'onentry': {
          'actions': [
            {
              'type': 'foreach',
              'valueexpression': 'items',
              'currentitemlocation': 'arrayItem',
              'actions': [
                {
                  'type': 'assign',
                  'target': 'sum',
                  'valueexpression': 'sum + arrayItem'
                },
                {
                  'type': 'log',
                  'messageexpression': '""item = "" + arrayItem'
                }
              ]
            }
          ]
        },
        'transitions': [
          {
            'conditionexpression': 'sum >= 15',
            'target': 'done'
          }
        ]
      },
      {
        'id': 'done',
        'type': 'final',
        'onentry': {
          'actions': [
            {
              'type': 'log',
              'messageexpression': '""item = "" + arrayItem'
            }
          ]
        }
      }
    ]
  }
}

Samples and Tests

  • Unit tests are here (work-in-progress :-))

  • Small standalone durable tests are here

  • More full-featured sample applications are here

Background and Resources

  • Excellent, pragmatic statechart explainer here

  • The SCXML statecharts spec

  • David Khourshid's excellent Javascript statechart library XState (with fabulous docs)

  • Nerd out on David Harel's original research that formalized statecharts

durablestatemachines's People

Contributors

jplane avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

stellarfoto bubdm

durablestatemachines's Issues

Design and implement standard telemetry/visualization protocol

It would be lovely to define a standard protocol for any SCXML-compliant statechart implementation to implement, for providing telemetry for logging and visualization.

This would allow folks to build various kinds of viz/debuggers/recorders etc. and use whatever backend with them.

Create model metadata abstraction

Model metadata is currently hardcoded to support SCXML. We want to abstract this into a proper C# interface so that we can store metadata in other formats/locations:

  • JSON
  • cloud storage
  • SQL
  • etc.

Implement standard message send operations

The SendMessage operation is stubbed out but not yet implemented.

We'd like a few things here:

  • a standard HTTP send message implementation (should handle HTTP verbs, query strings, body payloads, etc.)
  • a means for users to configure their own send message implementations prior to execution, probably using an ExecutionContext hook
    • probably need to add interface(s) to the abstractions assembly
    • this could include replacing the standard HTTP implementation with something better
  • needs to work for both in-proc and durable task implementations

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.