{"id":17449564,"url":"https://github.com/jplane/durablestatemachines","last_synced_at":"2025-06-29T13:36:06.025Z","repository":{"id":95098263,"uuid":"310676150","full_name":"jplane/DurableStateMachines","owner":"jplane","description":"Bringing the power of hierarchical state machines to a .NET Core runtime near you.","archived":false,"fork":false,"pushed_at":"2021-02-08T21:18:55.000Z","size":1276,"stargazers_count":10,"open_issues_count":3,"forks_count":2,"subscribers_count":4,"default_branch":"durable-functions-host","last_synced_at":"2025-02-23T23:41:15.812Z","etag":null,"topics":["automata","dotnet-core","durable-functions","durabletask","finite-state-machine","state-machine","statechart"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jplane.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-06T18:29:37.000Z","updated_at":"2024-07-24T04:36:17.000Z","dependencies_parsed_at":"2023-05-26T15:45:42.955Z","dependency_job_id":null,"html_url":"https://github.com/jplane/DurableStateMachines","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jplane%2FDurableStateMachines","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jplane%2FDurableStateMachines/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jplane%2FDurableStateMachines/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jplane%2FDurableStateMachines/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jplane","download_url":"https://codeload.github.com/jplane/DurableStateMachines/tar.gz/refs/heads/durable-functions-host","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241460053,"owners_count":19966511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automata","dotnet-core","durable-functions","durabletask","finite-state-machine","state-machine","statechart"],"created_at":"2024-10-17T21:42:07.365Z","updated_at":"2025-04-19T14:55:33.857Z","avatar_url":"https://github.com/jplane.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Durable State Machines\n\nDSM implements a declarative state machine programming model as an extension for [Azure Durable Functions](https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp). It's based on the \"hierarchical state machine\" concepts behind [statecharts](https://statecharts.github.io/) and the [SCXML W3C Recommendation](https://www.w3.org/TR/scxml/).\n\n| | |  |\n| ------------- | ----------- | --- |\n| DurableStateMachines.Core | Metadata + interpreter (standalone, in-memory component... no durable bits) | [![NuGet](https://img.shields.io/nuget/v/DurableStateMachines.Core)](https://www.nuget.org/packages/DurableStateMachines.Core/) |\n| DurableStateMachines.Functions | Durable Functions integration | [![NuGet](https://img.shields.io/nuget/v/DurableStateMachines.Functions)](https://www.nuget.org/packages/DurableStateMachines.Functions/) |\n| DurableStateMachines.Client | Durable Functions client API | [![NuGet](https://img.shields.io/nuget/v/DurableStateMachines.Client)](https://www.nuget.org/packages/DurableStateMachines.Client/) |\n| | |  |\n\n\n## What are statecharts?\n\nThe best definition comes from [here](https://statecharts.github.io/what-is-a-statechart.html):\n\n\u003e The primary feature of statecharts is that states can be organized in a hierarchy:  A statechart is a [state machine](https://statecharts.github.io/what-is-a-state-machine.html) where each state in the state machine may define its own subordinate state machines, called substates.  Those states can again define substates.\n\nThe utility of traditional state machines goes down as system complexity increases, due to [state explosion](https://statecharts.github.io/state-machine-state-explosion.html). Also, state machines by themselves are merely a description of behavior, not behavior itself. Statecharts (and DSM) address both of these limitations.\n\n## Goals\n\nDSM aims to provide a full-featured statechart implementation for [Azure Durable Functions](https://docs.microsoft.com/en-us/azure/azure-functions/durable/). This means you can run state machines locally on your laptop, or anywhere Azure Functions will run (Kubernetes, Azure serverless, edge compute, etc.).\n\nSome specific design and implementation choices:\n\n- An [abstraction](./Common/Model) for describing statechart definitions and an [initial](./Metadata) implementation\n\n- Abstractions for both [pull](./Common/Model/Execution/IQueryMetadata.cs)- and [push](./Common/Model/Execution/ISendMessageMetadata.cs)-based communication with external systems; talk to all your favorite native cloud services from within your statechart!\n\n- 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.)\n\n- An extensible [telemetry service](./FunctionClient/Listener.cs) implemented with [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction)... observe state machine execution as it occurs, etc.\n\n## Getting Started\n\nStatecharts support standard state machine concepts like [atomic](https://statecharts.github.io/glossary/atomic-state.html) and [parallel](https://statecharts.github.io/glossary/parallel-state.html) states, state [transitions](https://statecharts.github.io/glossary/transition.html), [actions](https://statecharts.github.io/glossary/action.html) within states, etc.\n\nStatecharts also support advanced concepts like [history states](https://statecharts.github.io/glossary/history-state.html), [event-based transitions](https://statecharts.github.io/glossary/event.html), and [nested or compound state hierarchies](https://statecharts.github.io/glossary/compound-state.html). \n\nIn SCDN you define statecharts using declarative metadata in JSON. See below for examples.\n\nFor advanced scenarios, you can also define your own metadata syntax and map it to primitives the SCDN engines can interpret and execute directly.\n\n## Usage\n\nRun statecharts as a Durable Function [orchestration](https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-orchestrations?tabs=csharp), using the standard [IDurableClient](https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.webjobs.extensions.durabletask.idurableclient?view=azure-dotnet) APIs.\n\n### Option 1: strongly-typed C#\n\n```csharp\n// in your DF app (on the server)\n\npublic class Definitions\n{\n    [StateMachineDefinition(\"my-state-machine\")]\n    public StateMachine\u003c(int x, int y)\u003e MyStateMachine =\u003e\n        new StateMachine\u003c(int x, int y)\u003e\n        {\n            Id = \"test\",\n            States =\n            {\n                new AtomicState\u003c(int x, int y)\u003e\n                {\n                    Id = \"state1\",\n                    OnEntry = new OnEntryExit\u003c(int x, int y)\u003e\n                    {\n                        Actions =\n                        {\n                            new Assign\u003c(int x, int y)\u003e\n                            {\n                                Target = d =\u003e d.x,\n                                ValueFunction = data =\u003e data.x + 1\n                            }\n                        }\n                    },\n                    OnExit = new OnEntryExit\u003c(int x, int y)\u003e\n                    {\n                        Actions =\n                        {\n                            new Assign\u003c(int x, int y)\u003e\n                            {\n                                Target = d =\u003e d.x,\n                                ValueFunction = data =\u003e data.x + 1\n                            }\n                        }\n                    },\n                    Transitions =\n                    {\n                        new Transition\u003c(int x, int y)\u003e\n                        {\n                            Targets = { \"alldone\" }\n                        }\n                    }\n                },\n                new FinalState\u003c(int x, int y)\u003e\n                {\n                    Id = \"alldone\"\n                }\n            }\n        };\n}\n\n```\n\n```csharp\n// on the client\n\nIDurableClient client = GetDurableFunctionClient();     // obtain via dependency injection\n\nvar data = (5, 0);                                      // any serializable type\n\nvar instanceId = await client.StartNewStateMachineAsync(\"my-state-machine\", data);\n\nvar result = await client.WaitForStateMachineCompletionAsync(instanceId);\n\ndata = result.ToOutput\u003c(int x, int y)\u003e();\n\nConsole.WriteLine(data.x);\n\n```\n\n### Option 2: HTTP + JSON\n\n```\n\n// from any client\n\nHTTP POST /runtime/webhooks/durabletask/orchestrators/statemachine-definition\n\n{\n  'input': {\n    'items': [ 1, 2, 3, 4, 5 ],\n    'sum': 0\n  },\n  'statemachine': {\n    'id': 'test',\n    'initialstate': 'loop',\n    'states': [\n      {\n        'id': 'loop',\n        'type': 'atomic',\n        'onentry': {\n          'actions': [\n            {\n              'type': 'foreach',\n              'valueexpression': 'items',\n              'currentitemlocation': 'arrayItem',\n              'actions': [\n                {\n                  'type': 'assign',\n                  'target': 'sum',\n                  'valueexpression': 'sum + arrayItem'\n                },\n                {\n                  'type': 'log',\n                  'messageexpression': '\"\"item = \"\" + arrayItem'\n                }\n              ]\n            }\n          ]\n        },\n        'transitions': [\n          {\n            'conditionexpression': 'sum \u003e= 15',\n            'target': 'done'\n          }\n        ]\n      },\n      {\n        'id': 'done',\n        'type': 'final',\n        'onentry': {\n          'actions': [\n            {\n              'type': 'log',\n              'messageexpression': '\"\"item = \"\" + arrayItem'\n            }\n          ]\n        }\n      }\n    ]\n  }\n}\n\n```\n\n## Samples and Tests\n\n- Unit tests are [here](./Tests) (work-in-progress :-))\n\n- Small standalone durable tests are [here](./DurableTests)\n\n- More full-featured sample applications are [here](./Samples)\n\n## Background and Resources\n\n- Excellent, pragmatic statechart explainer [here](https://statecharts.github.io/)\n\n- The [SCXML](https://www.w3.org/TR/scxml/) statecharts spec\n\n- David Khourshid's excellent Javascript statechart library [XState](https://github.com/davidkpiano/xstate) (with fabulous [docs](https://xstate.js.org/docs/))\n\n- Nerd out on David Harel's original [research](https://www.sciencedirect.com/science/article/pii/0167642387900359/pdf) that formalized statecharts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjplane%2Fdurablestatemachines","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjplane%2Fdurablestatemachines","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjplane%2Fdurablestatemachines/lists"}