Skip to content

Getting started ​

Install ​

sh
go get github.com/arisros/fate

The engine has no dependencies beyond the standard library. The optional Temporal integration is a separate module (go get github.com/arisros/fate/temporal).

A first machine ​

This counter has one state and mutates its context on each event:

go
package main

import (
    "context"
    "fmt"

    "github.com/arisros/fate/action"
    "github.com/arisros/fate/engine"
)

type Ctx struct{ Count int }

type Evt interface{ isEvt() }
type Inc struct{}
type Reset struct{}

func (Inc) isEvt()   {}
func (Reset) isEvt() {}

func main() {
    m, err := engine.CreateMachine(engine.MachineConfig[Ctx, Evt]{
        ID:      "counter",
        Initial: "active",
        States: map[string]engine.StateNodeConfig[Ctx, Evt]{
            "active": {On: map[string][]engine.TransitionConfig[Ctx, Evt]{
                "Inc":   {{Actions: []action.Action[Ctx, Evt]{action.Assign(func(c Ctx, _ Evt) Ctx { c.Count++; return c })}}},
                "Reset": {{Target: "active", Actions: []action.Action[Ctx, Evt]{action.Assign(func(c Ctx, _ Evt) Ctx { c.Count = 0; return c })}}},
            }},
        },
    })
    if err != nil {
        panic(err)
    }

    a := engine.NewActor(m)
    _ = a.Start(context.Background())
    _ = a.Send(context.Background(), Inc{})
    _ = a.Send(context.Background(), Inc{})

    fmt.Println(a.Snapshot().Context.Count) // 2
}

The shape of the API ​

  • CreateMachine validates a config and returns an immutable *Machine.
  • NewActor makes a running instance; Start runs the initial entry; Send dispatches an event.
  • Snapshot reads the current state; Subscribe observes changes; Persist and NewActorFromSnapshot round-trip to JSON.

Where to go next ​

The examples/ directory has runnable programs (a traffic light, a real-time timer driver) and the package's testable Example functions double as documentation on pkg.go.dev.

Released under the MIT License · v0.6.0 · pkg.go.dev