Getting started
Install
sh
go get github.com/arisros/fateThe 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
CreateMachinevalidates a config and returns an immutable*Machine.NewActormakes a running instance;Startruns the initial entry;Senddispatches an event.Snapshotreads the current state;Subscribeobserves changes;PersistandNewActorFromSnapshotround-trip to JSON.
Where to go next
- Defining machines — hierarchy, parallel regions, guards, actions, history.
- Effects and adapters — delays and invocations.
- Persistence and determinism — why the engine is replay-safe.
- Temporal — durable execution.
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.