{"id":30105701,"url":"https://github.com/ozontech/state-transition","last_synced_at":"2025-08-10T00:18:57.531Z","repository":{"id":224954820,"uuid":"764297507","full_name":"ozontech/state-transition","owner":"ozontech","description":"StateTransition is a simple, but very handy .NET library for configuring of the state machine.","archived":false,"fork":false,"pushed_at":"2024-03-04T07:40:00.000Z","size":236,"stargazers_count":11,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-22T08:02:42.279Z","etag":null,"topics":["csharp","design-patterns","dotnet","dotnet-library","finite-state-machine","fsm","orchestration","orchestration-systems","saga","state-machine","state-management","stateless","workflow","workflow-automation"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ozontech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"AUTHORS.txt","dei":null}},"created_at":"2024-02-27T20:34:51.000Z","updated_at":"2025-05-07T15:43:41.000Z","dependencies_parsed_at":"2024-02-28T16:05:55.464Z","dependency_job_id":null,"html_url":"https://github.com/ozontech/state-transition","commit_stats":null,"previous_names":["ozontech/state-transition"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ozontech/state-transition","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozontech%2Fstate-transition","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozontech%2Fstate-transition/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozontech%2Fstate-transition/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozontech%2Fstate-transition/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ozontech","download_url":"https://codeload.github.com/ozontech/state-transition/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozontech%2Fstate-transition/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269656923,"owners_count":24454743,"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","status":"online","status_checked_at":"2025-08-09T02:00:10.424Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["csharp","design-patterns","dotnet","dotnet-library","finite-state-machine","fsm","orchestration","orchestration-systems","saga","state-machine","state-management","stateless","workflow","workflow-automation"],"created_at":"2025-08-10T00:18:55.051Z","updated_at":"2025-08-10T00:18:57.506Z","avatar_url":"https://github.com/ozontech.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NuGet](https://img.shields.io/nuget/v/StateTransition)](https://www.nuget.org/packages/StateTransition/)\n\n![](logo.png)\n\n# Overview\n\nThe `StateTransition` is a simple, but very handy .NET library for configuring of the state machine.\n\nThe API of the `StateTransition` provides complete set of features for flexible and quick configuring the state machine by state or trigger with a number of different useful options.\nThere is everything you need and no frills.\n\u003e `less code, more opportunities`\n\n## Disclaimer\n⚠ Although we use it in production, it still isn't v1.0.0. Please, test your configured state machines carefully on dev/stage environments before deploying to the prod.\n\n## Contributing\nThe `StateTransition` is an open-source project and we will thank you for your contributions to our project. Read the rules [here](./CONTRIBUTING.md).\n\n## Motivation\nWhen you need to orchestrate some business process, then ensuring reliable and flexible orchestration of the process will often depend on the possibilities of the state machine engine.\n\nWe are defined 4 important aspects for `StateTransition`'s engine, that we'd like to have for best matching and cover our requirements:\n- `loosely coupled \u0026 entity-oriented`\n- `flexibility`\n- `reliability`\n- `simplicity`\n\nWell, before developing own implementation we made a short research of the couple similar popular libs for configuring of state machines: [stateless](https://github.com/dotnet-state-machine/stateless)\nand [automatonymous](http://masstransit-project.com/Automatonymous/). But these solutions didn’t match all above our requirements.\n\n## Main features\n\nSo let's look at the implemented features based on our requirements.\n- `loosely coupled \u0026 entity-oriented`\n  - The configuration of the `StateTransition` engine is built around the working with `TEntity` object type. Under the hood of engine the `low coupling` characteristic and this is confirmed at least by the fact that the `TEntity` type knows nothing about the state-machine itself and that it controls its state. Because of this, the implementation of entity state changes is flexible and adaptive, and you only need to specify the name of the property that will be responsible for the `TEntity` state. So it allows the `StateTransition` to have full control over the process of changing the state of `TEntity`. Moreover, the `StateTransition`'s engine is absolutely state-less.\n  - This is an unique and distinctive difference from many other state-machine solutions.\n- `flexibility`\n  - A flexible way to work with options to control the transition. Out of the box, 1 basic `IsAutofire` option will already be available, which tells the `StateTransition`'s engine that you can automatically fire a transition to the next state without any trigger.\n    - The `StateTransition`'s engine provides the ability to set your custom options under each transition.\n      - For example, in our services we use the custom `UseTransactionScope` option to specify a `compensable` transaction that will roll back changes that were previously applied before an error occurred in the system.\n      - You can override the `IsAutofire` option value if needed for your custom option.\n  - It is possible to \n    - pass additional custom arguments available as part of the transition options\n    - fire the state transitioning by using `TState` or `TTrigger`\n    - to configure custom `entry/exit` actions that are executed before and after a **specific** transition\n    - to configure of the default `entry/exit` action that are always executed before and after **any** transition\n    - loop the transition to the same `TState` state by using specific `TTrigger`\n    - define transition guards to check the condition on a particular transition from the current to the final state\n    - build a transitions state graph using Mermaid notation based on a configured state machine\n- `reliability`\n  - Out of the box, own native `middleware` functionality is available to provide `reliable` control. Let's look at the most basic and useful examples of how to use this functionality by our opinion:\n      - ensuring data integrity and consistency to avoid distributed transactions\n      - tracing and logging during the transition\n      - handling of exceptions that occurred during the transition, etc.\n- `simplicity`\n  - Easily and handy customizable configuration and to be sure it is enough to get acquainted with [Getting started](#getting-started) section\n  \n## Performance\n```config\nBenchmarkDotNet v0.13.12, macOS Sonoma 14.2.1 (23C71) [Darwin 23.2.0]\nApple M1, 1 CPU, 8 logical and 8 physical cores\n.NET SDK 7.0.305\n[Host]     : .NET 6.0.3 (6.0.322.12309), Arm64 RyuJIT AdvSIMD\nJob-VWVFSC : .NET 6.0.3 (6.0.322.12309), Arm64 RyuJIT AdvSIMD\n\nRuntime=.NET 6.0  RunStrategy=Throughput\n\n// * Legends *\nMean   : Arithmetic mean of all measurements\nError  : Half of 99.9% confidence interval\nStdDev : Standard deviation of all measurements\nRatio  : Mean of the ratio distribution ([Current]/[Baseline])\n1 ns   : 1 Nanosecond (0.000000001 sec)\n```\n\nTransitioning by trigger to the specific state with custom transition action and boolean guard can achieve the following benchmark:\n\n| Method | Mean     | Error    | StdDev   | Ratio |\n|------- |---------:|---------:|---------:|------:|\n| Fire   | 30.24 ns | 0.187 ns | 0.146 ns |  1.00 |\n\nIt is worth taking into account that during these `30+ ns` we also had time to set a new value for the `State` property of the related `TEntity` object type.\nYou can explore our project for benchmarks and run it to verify these calculations! [Here it is](benchmark/StateTransition.Benchmark/OpenClosedStateMachine.cs)!\n\n## Getting Started\n\nTo start configure your state machine, all you need to do is declare a new class that will be inherited from the base class\ngeneric class `StateMachine\u003cTState,TTrigger,TEntity\u003e`.\n\nThe base class `StateMachine\u003cTState,TTrigger,TEntity\u003e` has 3 types of parameters where\n\n- `TState` allows to describe your type with possible states that can be used to add transitions.\n  - Initialization of the state machine involves passing a property with `TState` type responsible for the state of the entity object, which will be updated after the transition to a new status.\n- `TTrigger` allows to describe the necessary triggers that will be used to call the transition on the of the state machine.\n- `TEntity` allows you to specify the type of entity for which the state machine itself will be configured, with all necessary transitions from one state to another.\n\nNow let's look how to easily start configure your state machine using the below sample.\n\n## Sample with simple state machine\n\n```csharp\npublic enum AbcState\n{\n\tA,\n\tB,\n\tC\n}\n\npublic enum AbcTrigger\n{\n\tSetB,\n\tSetC,\n\tAuto\n}\n\npublic class Abc\n{\n\tpublic AbcState State { get; set; }\n}\n\npublic class AbcStateMachine : StateMachine\u003cAbcState, AbcTrigger, Abc\u003e\n{\n\tpublic AbcStateMachine(): base(abc =\u003e abc.State)\n\t{\n\t\tConfigure(AbcState.A)\n\t\t\t.AddTransitionTo(AbcState.B, AbcTrigger.SetB);\n\t\t\n\t\tSetFiniteState(AbcState.B); // either set the state as finite, or just configure this state. you have to choose one!\n\t}\n    \n\tpublic async Task Fire(Abc abc, AbcTrigger trigger)\n\t{\n\t\tawait Fire(new FireByTriggerRequest(trigger, abc, CancellationToken.None));\n\t}\n}\n```\nNow we can use our configured `AbcStateMachine`. Let's fire the transition to the `B` state for `Abc`!\n```csharp\nvar abcStateMachine = new AbcStateMachine();\nvar abc = new Abc {State = AbcState.A};\nawait abcStateMachine.Fire(abc, AbcTrigger.SetB);\n```\n\nTo check out the power of the `StateTransition` engine, - we recommend you start from our [API](#api) section.  \nYou can use our sample [Abc state machine](sample/StateTransition.AbcSample/AbcStateMachine.cs) as a playground for easier and quicker familiarization with the `StateTransition` features.  \nIn addition, it will be useful to view our unit tests where we have tried to cover all features logic using our simple sample with `Abc`.\n\n## API\n\nThe API of the `StateTransition` has the followed structure:\n\n![api-structure](api-structure.png)\n\n- [StateMachine](./src/StateTransition/StateMachine.cs) class is responsible for full control over the state machine itself and directly for state transition.\n- [StateTransitionManager](./src/StateTransition/StateTransitionManager.cs) class is responsible for managing transitions for an each configured state.\n\nMore details about exposed API methods in these classes you can get from their xml-comments in code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozontech%2Fstate-transition","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fozontech%2Fstate-transition","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozontech%2Fstate-transition/lists"}