chain is a small Go workflow library built around composable Action
values. An action receives one value, returns the next value, and can be
connected to other actions through a Workflow.
The package focuses on in-process workflow control. It does not try to be a distributed scheduler or a full parallel DAG engine.
go get github.com/JSYoo5B/chainAn Action is the basic execution unit.
type Action[T any] interface {
Name() string
Run(ctx context.Context, input T) (output T, err error)
}Use NewSimpleAction when a function is enough:
double := chain.NewSimpleAction(
"double",
func(ctx context.Context, input int) (int, error) {
return input * 2, nil
},
)A Workflow connects actions and also implements Action, so workflows can be
nested inside other workflows.
workflow := chain.NewWorkflow("calculation", action1, action2, action3)
output, err := workflow.Run(context.Background(), input)By default, actions are connected through the Success direction in constructor
order. Failure and Abort terminate unless a custom RunPlan is set.
workflow.SetRunPlan(action1, chain.DefaultPlan(action2, errorHandler))A RunPlan maps a direction to the next action.
type RunPlan[T any] map[string]Action[T]Built-in directions are:
SuccessFailureAbort
BranchAction can add custom directions.
NewSimpleActioncreates an action from a function.NewSimpleBranchActioncreates a branch action from functions.AsBranchActionwraps an existing action and adds branching logic.
AsRetryableActionretries a main action and optionally runs rollback before the next attempt.SkipRollbackexplicitly disables rollback for a retryable action.AsBestEffortActionsuppresses a non-critical action error and optionally calls a fallback hook.
AsSequenceSliceActionruns an action over a slice sequentially.AsSequenceMapActionruns an action over a map sequentially.AsParallelSliceActionruns an action over a slice concurrently.AsParallelMapActionruns an action over a map concurrently.
Parallel collection actions preserve output positions or keys, but joined error order follows completion order.
AdaptActionruns an action against a field or sub-value inside a larger value.
This is useful when a workflow carries one aggregate state type, while some actions only operate on one part of that state.
Call Workflow.ValidateGraph explicitly to check a handwritten workflow before
execution; Run does not call it automatically.
It rejects:
- directed cycles
- disconnected workflow graphs
Branching and merge points are allowed. Connectivity is weak: every node must belong to one component when edge directions are ignored. This does not require every node to be reachable from the entry node.
Examples live in the separate examples module,
organized by subject. Each package contains its scenario implementation,
executable examples or tests, and a README:
- Numbers: simple arithmetic workflows and run plans.
- Branching: sign checks, parity, and Collatz.
- Error control: an addition counter with retries, rollback, and optional failure handling.
- Collections: small slices and maps of numbers or words.
- Type adaptation: change one struct field while retaining the others.
The small scenarios use only the standard library and chain. From the
repository root:
cd examples
go generate ./branch
go test ./...The root module's go test ./... does not include the examples module.
chain-gen generates Go workflow constructors from .chain files:
package example
import "github.com/JSYoo5B/chain"
workflow Build(first, second chain.Action[int]) Linear[int] {
nodes: first, second
success: first --> second --> end
}The declaration generates a Go constructor named Build returning
*chain.Workflow[int]. Constructor parameters and prerequisite statements use
Go syntax. A return inside a closure is allowed; returning from the prerequisite
itself is rejected. Nodes require explicit connections, and omitted supported
directions terminate instead of following declaration order.
From a checkout of this repository:
go run ./cmd/chain-gen -o path/workflow_chain.gen.go path/workflow.chainSee the DSL guide for a complete generate-and-test example, branch syntax, diagnostics, and input-file protection. The documentation index also links the action patterns and runnable examples.