Getting started
Install
Section titled “Install”go get github.com/stablekernel/crucible/stateThe kernel depends only on the Go standard library.
Your first machine, end to end
Section titled “Your first machine, end to end”A machine is generic over three types you define: S (state), E (event), and C (context, your domain entity). Here is a toy turnstile, forged and fired from scratch.
package main
import ( "context" "fmt"
"github.com/stablekernel/crucible/state")
type Gate string // Stype Signal string // Etype Turnstile struct{ Coins int } // C
const ( Locked Gate = "Locked" Unlocked Gate = "Unlocked")
const ( Coin Signal = "Coin" Push Signal = "Push")
func main() { // Forge a builder, declare states + transitions, then Quench to freeze // the definition into an immutable *Machine. Quench panics on misconfig. m := state.Forge[Gate, Signal, Turnstile]("turnstile"). Initial(Locked). Transition(Locked).On(Coin).GoTo(Unlocked). Transition(Unlocked).On(Push).GoTo(Locked). Quench()
// Cast an instance around an entity value. inst := m.Cast(Turnstile{})
// Fire advances the instance and returns a FireResult. It performs NO IO. // NewState is the next state, Effects is data for the caller to dispatch. res := inst.Fire(context.Background(), Coin) fmt.Println(res.NewState) // Unlocked
res = inst.Fire(context.Background(), Push) fmt.Println(res.NewState) // Locked}That toy machine looks like this:
stateDiagram-v2
[*] --> Locked
Locked --> Unlocked: Coin
Unlocked --> Locked: Push
A real machine
Section titled “A real machine”The same primitives scale to hierarchical and parallel statecharts. Here is the
food-delivery order machine that ships as a worked example. Note the nested
Active region with parallel fulfillment and watchdog sub-machines, guarded
transitions, and an after(...) timer:
stateDiagram-v2
[*] --> Placed
state Active {
[*] --> Active_Fulfillment__Cooking
Active_Fulfillment__Cooking
Active_Fulfillment__AwaitingCourier
Active_Fulfillment__EnRoute
Active_Fulfillment__AwaitingCourier --> Active_Fulfillment__EnRoute: PickedUp
Active_Fulfillment__Cooking --> Active_Fulfillment__AwaitingCourier: PlatedUp
--
[*] --> Active_Watchdog__OnTime
Active_Watchdog__OnTime
Active_Watchdog__Overdue
Active_Watchdog__Overdue --> [*]
Active_Watchdog__OnTime --> Active_Watchdog__Overdue: after(30m0s) SLABreached
}
Delivered --> [*]
Canceled --> [*]
Rejected --> [*]
Active --> Refunding: Cancel
Active --> Settling: DroppedOff
Authorizing --> Active: Authorized [or(generousOrder,and(subtotal ge 5000,priority in ["fast","express"]))]
Authorizing --> Rejected: Declined
Placed --> Authorizing: Submit
Refunding --> Canceled: Refunded
Settling --> Delivered
Next: read the foundry vocabulary to learn exactly what each lifecycle verb does.