{"id":22885085,"url":"https://github.com/rendis/statepro","last_synced_at":"2026-03-01T01:37:27.138Z","repository":{"id":163744951,"uuid":"502228449","full_name":"rendis/statepro","owner":"rendis","description":"Golang state machine","archived":false,"fork":false,"pushed_at":"2026-02-02T02:00:00.000Z","size":433,"stargazers_count":15,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-02T11:36:08.989Z","etag":null,"topics":["event-handlers","finite-state-machine","fsm","go","golang","orchestration","state-diagram","state-machine","state-management","statechart","statecharts","stately","workflow","xstate"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rendis.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":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-06-11T02:13:33.000Z","updated_at":"2026-02-02T01:57:47.000Z","dependencies_parsed_at":"2023-11-16T01:24:36.785Z","dependency_job_id":"1123d3e4-7e20-4bb7-ab25-7ba161a57135","html_url":"https://github.com/rendis/statepro","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/rendis/statepro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstatepro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstatepro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstatepro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstatepro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rendis","download_url":"https://codeload.github.com/rendis/statepro/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rendis%2Fstatepro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29957452,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T22:53:01.873Z","status":"ssl_error","status_checked_at":"2026-02-28T22:52:50.699Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["event-handlers","finite-state-machine","fsm","go","golang","orchestration","state-diagram","state-machine","state-management","statechart","statecharts","stately","workflow","xstate"],"created_at":"2024-12-13T19:31:44.671Z","updated_at":"2026-03-01T01:37:27.091Z","avatar_url":"https://github.com/rendis.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# StatePro: Advanced State Machine Handling in Golang\n[![Go Report Card](https://goreportcard.com/badge/github.com/rendis/statepro)](https://goreportcard.com/report/github.com/rendis/statepro)\n[![Go Reference](https://pkg.go.dev/badge/github.com/rendis/statepro.svg)](https://pkg.go.dev/github.com/rendis/statepro)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Release](https://img.shields.io/github/release/rendis/statepro.svg?style=flat-square)](https://github.com/rendis/statepro/releases)\n\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"documentation%2Fassets%2Fdog-machine-diagram.png\" alt=\"golang state machine inspired by xstate\" width=\"800\"\u003e\n\u003c/p\u003e\n\n## Table of Contents\n\n1. [Introduction](#introduction)\n2. [Installation](#installation)\n2. [Components of a State Machine](#components-of-a-state-machine)\n3. [Initializing the State Machine](#initializing-the-state-machine)\n    - [JSON Definition of the State Machine](#json-definition-of-the-state-machine)\n    - [Context](#context)\n    - [Definition Registry](#definition-registry)\n4. [Initialization](#initialization)\n5. [Getting an instance of the state machine](#getting-an-instance-of-the-state-machine)\n6. [ProMachine](#promachine)\n7. [Creating Action, Invocation, Guard and Context Handlers](#creating-action-invocation-guard-and-context-handlers)\n    - [Event](#event)\n    - [ActionTool](#actiontool)\n    - [Method Definitions](#method-definitions)\n8. [Examples](#examples)\n\n## Introduction\n\nStatePro is a Golang library for handling Finite State Machines, designed to optimize state management in microservices.\nInspired by [XState](https://xstate.js.org/) but focused on backend development,\nthe JSON representation of the State Machine is compatible with XState's [visual creator](https://stately.ai/),\nfacilitating its design and visualization.\n\nDespite similarities with XState, StatePro has its own set of features, primarily to ensure efficiency and adaptability in\nstate management in microservices. This is because some XState features, although useful for frontend development, do not always\ntranslate effectively to the microservices environment, which can lead to unnecessary complexity.\n\n## Installation\n\nTo install StatePro, use the following command:\n\n```bash\ngo get github.com/rendis/statepro\n```\n\n## Components of a State Machine\n\n1. **State Machine**: Defines the overall behavior of the system and is composed of states, transitions, events,\n   actions, guards, and invocations.\n\n2. **State**: A state represents a particular stage in the lifecycle of a state machine.\n\n3. **Action**: Actions are behaviors that are performed when an event or transition occurs. Actions are synchronous and are executed in the order in which they are defined.\n\n4. **Guard**: Guards are conditions that must be met for a transition to occur.\n\n5. **Invocation**: Invocations are asynchronous tasks that are performed upon entering a state. Unlike other\n   implementations, in StatePro, the result of an invocation (success or failure) does not affect the behavior of the\n   state machine.\n\n6. **Transition**: A transition is a change from one state to another in response to an event.\n\n7. **Event**: Events are inputs that trigger transitions between states.\n\n8. **Context**: The context is an object associated with the state machine that can be modified within actions and\n   queried by the other components.\n\n## Initializing the State Machine\n\nTo initialize a state machine in StatePro, three components are needed: the JSON definition of the state machine, the context, and the definition registry.\n\n### JSON Definition of the State Machine\n\nThe JSON will contain the definition of the state machine. This can be created in [Stately](https://stately.ai/) and imported into your project.\nIt is important that each JSON definition you load into your system has a unique ID. This ID will be used later to associate the JSON with the definition registry.\n\nThe location of the state machine definition JSONs is configurable. By default, StatePro will look in the `statepro.yml` file at the root of the project.\nYou can change the location and filename of the configuration file using the `SetDefinitionPath` method of the `statepro` package:\n\n```go\nimport \"github.com/rendis/statepro\"\n\nstatepro.SetDefinitionPath(\"path/to/your/prop.yml\")\n```\n\nWithin the configuration file, you must define the location of the state machine definition JSON files. For example:\n\n```yml\nstatepro:\n  file-prefix: '\u003cprefix\u003e'\n  paths:\n    - '\u003crute1\u003e'\n    - '\u003crute2\u003e'\n    ...\n```\n\n- `file-prefix`: specifies the prefix that file names must have to be considered as definition files. Only files whose name begins with this prefix will be processed.\n- `paths`: is a list of paths that specify the directories and/or files that should be searched to find the state machine definitions.\n\n### Context\n\nThe context is a struct that will be linked to the behavior of the state machine.\n\n### Definition Registry\n\nThe definition registry is a struct that will contain the action, invocation, guard methods, and context handling methods that will be used in the state machine.\nThis struct should have the type of the `context` as a generic.\n\nThis struct should implement the `MachineRegistryDefinitions` interface, which is used to get the ID of the state machine.\nThis ID should be the same as the ID of the definition JSON that you want to associate it with.\n\n\nFor example:\n\n```go\ntype Context struct {\n    name string\n    state ContextState\n    ...\n}\n\ntype ContextMachineDefinitions[ContextType Context] struct {}\n\n// Implementation of the GetMachineTemplateId method of the MachineRegistryDefinitions interface\nfunc (cmd *ContextMachineDefinitions[ContextType]) GetMachineTemplateId() string {\n    return \"MACHINE_ID\"\n}\n\n// Implementation of action, invocation, guard methods, and context handling methods\n```\n\nOnce you have these three components (JSON definition, context, and definition registry), you can initialize your state machine\nand start using it to manage the flow of your application.\n\n## Initialization\n\nBefore initializing `statepro`, you must have registered **all** the state machine definitions using the `AddMachine` method of the `statepro` package.\nTo initialize a state machine, you should use the `InitMachines` method of the `statepro` package.\n\n```go\nvar definition1 = ContextMachineDefinitions[Context]{}\nvar definition2 = ContextMachineDefinitions2[Context2]{}\nvar definition3 = ContextMachineDefinitions3[Context3]{}\n...\n\n// register definitions\nvar machineId1 = statepro.AddMachine[Context](definition1)\nvar machineId2 = statepro.AddMachine[Context2](definition2)\nvar machineId3 = statepro.AddMachine[Context3](definition3)\n...\n\n// InitMachines should be called after registering all definitions\nstatepro.InitMachines()\n```\n\nThe return value of `AddMachine` is the ID associated with the state machine created from the definition registry `ContextMachineDefinitions[Context]`.\nThis ID will be unique and will be used to get an instance of the state machine.\n\u003e Note: The ID returned by `AddMachine` is not the same as the ID of the JSON definition. It is a unique ID generated for each state machine.\n\n## Getting an instance of the state machine\n\nTo get a state machine, you should use the `GetMachine` method of the `statepro` package, which returns an object of type `ProMachine`.\nThis is an interface that defines the methods that allow you to interact with the state machine.\n\n```go\nvar context = \u0026Context{}\n\nvar contextMachine, err = statepro.GetMachine[Context](machineId1, context)\n```\n\nTo obtain a state machine, in addition to its ID (`machineId1`), an instance of the context must be provided.\nIf it is `nil`, an instance of the context will be attempted to be obtained through the `ContextFromSource` method of the associated definition registry.\nIf an instance of the context does not exist in the `ContextFromSource` method, an error will be returned.\n\n\n## ProMachine\n\nStatePro defines a `ProMachine` interface with several methods that allow you to interact with the state machine:\n\n```go\ntype ProMachine[ContextType any] interface {\n    PlaceOn(stateName string) error\n    StartOn(stateName string) TransitionResponse\n    StartOnWithEvent(stateName string, event Event) TransitionResponse\n    SendEvent(event Event) TransitionResponse\n    GetNextEvents() []string\n    GetState() string\n    IsFinalState() bool\n    GetContext() ContextType\n    CallContextToSource() error\n}\n``` \n\n- `PlaceOn(stateName string) error`: places the state machine in a specific state, without executing entry actions.\n\n- `StartOn(stateName string) TransitionResponse`: places the state machine in a specific state and executes the entry actions.\n\n- `StartOnWithEvent(stateName string, event Event) TransitionResponse`: is similar to `StartOn`, but also sends an event to the state machine.\n\n- `SendEvent(event Event) TransitionResponse`: allows sending an event to the state machine, which can trigger transitions and actions.\n\n- `GetNextEvents() []string`: returns a list of the names of events that can be sent from the current state.\n\n- `GetState() string`: returns the name of the current state.\n\n- `IsFinalState() bool`: checks if the current state is a final state.\n\n- `GetContext() ContextType`: allows getting the value of the context associated with the state machine.\n\n- `CallContextToSource() error`: allows calling the 'ContextToSource' method, if it exists, in the definition registry of the state machine.\n\nThe response structure `TransitionResponse` contains information about the transition(s) that occurred.\n```go\ntype TransitionResponse interface {\n    GetLastEvent() Event\n    Error() error\n}\n```\n\n- `GetLastEvent() Event`: returns the last event that was sent to the state machine.\n- `Error() error`: returns an error if one occurred during the transition.\n\nRegarding the features related to **states** and **transitions**, StatePro allows defining specific behaviors that are defined in the design of the state machine:\n\n#### About a State\n\n- **Execute Entry Actions**: When entering a state, specific actions can be executed.\n\n- **Execute Exit Actions**: When leaving a state, specific actions can be executed.\n\n- **Execute Invocations**: When entering a state, asynchronous tasks can be executed. Although invocations are\n  asynchronous, in StatePro, their result (success or failure) does not affect the decision-making of the state machine.\n  For example, when entering a state, you might want to asynchronously send an event to a message queue. If the\n  invocation fails, the state machine will not be affected and will continue with its normal execution.\n\n#### About Transitions\n\n- **Execute Transition Actions**: During a transition, specific actions can be executed.\n\n- **Execute Guards**: Before performing a transition, conditions can be evaluated that determine whether the transition\n  should be performed or not.\n\n- **Execute Guard Actions**: While evaluating a guard, specific actions can be executed.\n\n## Creating Action, Invocation, Guard and Context Handlers\n\nTo work with StatePro, it is important to understand how to create and use actions, invocations, and guards. These\nmethods are essential to defining the behavior of the state machine. Before delving into how to define\nthese methods, we'll explain two essential elements: `Event` and `ActionTool`.\n\n### Event\n\nThe `Event` object is the basic unit of communication between the states of the machine. An `Event` can contain a\n`Data` value, which can be used to pass information from one state to another. Here's its definition:\n\n```go\ntype Event interface {\n    GetName() string\n    GetFrom() string\n    HasData() bool\n    GetData() any\n    GetDataAsMap() (map[string] any, error)\n    GetErr() error                         \n    GetEvtType() EventType\n    ToBuilder() EventBuilder\n}\n```\n\nTo build an event, you use `EventBuilder`:\n\n```go\ntype EventBuilder interface {\n    WithData(data any) EventBuilder\n    WithErr(err error) EventBuilder\n    WithType(eventType EventType) EventBuilder\n    Build() Event\n}\n```\n\nFor example, building an event would look like this:\n\n```go\nimport \"github.com/rendis/statepro/piece\"\n\nvar evt := piece.BuildEvent(\"EVENT_NAME\").Build()\n```\n\n`ActionTool` is an object used to interact with the state machine from within an action. It has the following definition:\n\n```go\ntype ActionTool[ContextType any] interface {\n    Send(event Event)          \n    Propagate(event Event)\n}\n```\n- `Send(event Event)`: This method allows sending an event to the state machine. The event will be processed by the current state of the machine.\n\n- `Propagate(event Event)`: This method allows propagating an event with new data and errors throughout the operations following the current action. It is useful for transmitting additional information or errors that occur during the execution of an action.\n\n### Method Definitions\n\nThe **action**, **guard**, and **invoke** methods must have the same name as the component they are intended to associate with in the state machine's JSON. \nIt's important to note that the names in the JSON are case-insensitive. This means that if we have `doSomething`, `DoSomething`, and `dosomething` in the JSON, \nfor the state machine engine, **_these three variants will be considered the same_**. Consequently, their counterpart in the code should be named exactly as `DoSomething`. \nThis naming standard is crucial for maintaining consistency between the state machine's JSON and its implementation in the code.\n\nTheses methods must be defined in the `MachineRegistryDefinitions` as follows:\n\n```go\n// Action\nfunc(cmd *ContextMachineDefinitions[ContextType]) ActionName(contextValue *Context, evt Event, actTool ActionTool) error {...}\n\n// Guard\nfunc(cmd *ContextMachineDefinitions[ContextType]) GuardName(contextValue  *Context, evt Event) (bool, error) {...}\n\n// Invocation\nfunc (cmd *ContextMachineDefinitions[ContextType]) InvocationName(contextValue  Context, evt Event) {...}\n```\n\nIn addition, within the definition of state machine records, two methods can be defined for getting and saving the context. These methods are useful for centralizing the logic in one place.\n\nTo define the context retrieval method, the `ContextFromSource` function must be implemented in the **StateMachineRegistry** as follows:\n\n```go\n// Context Retrieval\nfunc (cmd *ContextMachineDefinitions[ContextType]) ContextFromSource(params ... any) (Context, error) {\n    // obtain parameters from params (params[0], params[1], etc)\n    // context retrieval logic\n    return context, nil\n}\n```\nAnd to define the context saving method, the `ContextToSource` function must be implemented in the **StateMachineRegistry** as follows:\n\n```go\n// Context Saving\nfunc (cmd *ContextMachineDefinitions[ContextType]) ContextToSource(context Context) error {\n    // context saving logic\n    return nil\n}\n```\n\n## Examples\n\nIn order to help you get a better understanding of how to use StatePro, we've created several examples that demonstrate its various features. These examples are organized into different branches, each focusing on a specific aspect of StatePro.\n\nHere's a brief description of what each branch covers:\n\n1. **01-example-basic**: This branch contains a basic example of how to use StatePro. It's the perfect starting point if you're new to the library.\n\n2. **02-read-basic-example**: In this branch, you'll find examples of how to read the state of a state machine and how to react to changes.\n\n3. **03-write-basic-example**: This branch focuses on writing to the state machine. You'll learn how to trigger events and cause state transitions.\n\n4. **04-events-example**: This branch goes deeper into event handling with StatePro. It demonstrates how to define and use custom events.\n\n5. **05-invocations-services**: In this branch, we cover the topic of invocations. You'll learn how to define and use asynchronous tasks that get executed when entering a state.\n\n6. **06-context-handlers**: This branch focuses on context handlers. You'll learn how to define and use methods for getting and saving the context of a state machine.\n\nTo view the examples, simply switch to the respective branch. Remember, these examples are intended to be a learning resource. Feel free to modify and experiment with them as you become more comfortable with StatePro.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Fstatepro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frendis%2Fstatepro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frendis%2Fstatepro/lists"}