go-saga-orchestration

module
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2026 License: MIT

README ΒΆ

go-saga-orchestration

A standalone, solution-agnostic saga orchestrator + synchronous CEL rule evaluator you can embed as a Go library or run as a two-binary service.


✨ Features

  • 31 saga step types β€” data transforms, HTTP/webhooks, timers, signals, events, parallel fan-out, foreach, loops, try/catch, human tasks, sub-sagas, and more (see docs/verbs.md).
  • Embed or deploy β€” run in-process with zero infrastructure, or deploy as two Docker-friendly binaries backed by Postgres + RabbitMQ.
  • CEL expressions β€” Google Common Expression Language for conditions, transforms, filters, and routing, all evaluated against live run variables.
  • Named entrypoints β€” Entrypoints map[string]string on a WorkflowDefinition lets a single workflow serve multiple start scenarios; triggers and sub_saga/spawn_saga accept an entrypoint input.
  • gRPC workers β€” microservices connect over bidirectional gRPC streams to handle action steps and return results without polling.
  • Durable audit trail β€” every step transition, rule evaluation, signal, and metric is written as an immutable event row.
  • License-gated verbs β€” feature groups (waits, parallel_control, human_interaction, …) are checked at publish and runtime so environments only use the features they are licensed for.

πŸš€ 30-second embed quickstart

import (
    "context"

    "github.com/Bugs5382/go-saga-orchestration/saga"
    "github.com/Bugs5382/go-saga-orchestration/domain"
    "github.com/Bugs5382/go-saga-orchestration/engine/verbs"
)

sc := saga.InMemory() // in-memory store + in-process advance

// Register your own verb as a closure:
sc.RegisterVerb("charge_card", "common",
    verbs.HandlerFunc(func(ctx context.Context, run domain.SagaRun, step domain.Step) (map[string]any, error) {
        return map[string]any{"ok": true}, nil
    }))

// Define + publish a workflow, then start it:
sc.Register(domain.WorkflowDefinition{
    ID: "checkout", Version: 1, Start: "charge", Published: true,
    Steps: []domain.Step{
        {ID: "charge", Type: "charge_card", Next: "done"},
        {ID: "done", Type: domain.StepTypeEnd},
    },
})
runID, _ := sc.Start(context.Background(), "checkout", map[string]any{"total": 4200})
run, _ := sc.Get(context.Background(), runID)
_ = run.State // succeeded

See examples/basic for a runnable standalone example, and docs/embedding.md for the full embedding guide including production wiring.


🧭 Which mode? Embedded vs service

Embedded library Service mode
Infrastructure None Postgres + RabbitMQ
Workers RegisterVerb closures Separate processes via gRPC
Scale Single process Horizontally scalable
Best for Tests, simple automations, CLIs Production multi-tenant deployments

Embedded: saga.InMemory() for tests; saga.New(saga.Options{Store: pgStore, ...}) for in-process production use with a durable store.

Service: cmd/api (REST, :8080) + cmd/engine (coordinator + gRPC, :9090). Workers connect via the gRPC ExecuteStep stream; clients use the REST API.


πŸ“š Docs

Doc What it covers
docs/verbs.md Complete reference for all 31 step types β€” inputs, outputs, license groups, and example links
docs/embedding.md Quickstart, custom verbs, data flow, entry points, production wiring, lifecycle, service mode
docs/stores.md Store backend selection (STORE_TYPE), env vars, Redis/Valkey durability, REDIS_RUN_TTL, and the stream-requires-postgres limitation
docs/caveats.md Limitations and common gotchas with workarounds
docs/architecture.md Engine internals, coordinator, MQ topology, stores, request flow, CEL rules
docs/api.md + api/openapi.yaml REST API reference (17 endpoints) and OpenAPI 3 spec
docs/grpc.md The WorkerLiveness.ExecuteStep worker protocol
clients/go/worker/README.md Go worker SDK
examples/ Basic embed example and 31 per-verb workflow JSON files

Local development

go run ./cmd/api     # REST API on :8080
go run ./cmd/engine  # coordinator + gRPC on :9090 (needs Postgres + RabbitMQ)
go build ./...       # build everything
go vet ./...         # vet

End-to-end tests under test/e2e require Postgres + RabbitMQ.


Configuration

All configuration is via environment variables (internal/config/config.go):

Variable Default Used by Purpose
WORKFLOW_API_PORT 8080 api REST API listen port
WORKFLOW_ENGINE_GRPC_PORT 9090 engine gRPC worker server port
DATABASE_DSN (empty) both Postgres connection string (durable store)
RABBITMQ_URL (empty) both RabbitMQ connection URL (step dispatch)
STORE_TYPE postgres both Store backend: postgres (default) | redis | valkey | memory β€” see docs/stores.md
REDIS_URL (empty) both Redis/Valkey connection URL (required when STORE_TYPE is redis or valkey)
REDIS_RUN_TTL 0s both Go duration; auto-expire terminal-run keys after this window (default 0s = keep forever)

Layout

Public importable packages (the library surface):

  • saga β€” facade (saga.InMemory(), saga.New(saga.Options{...}), *saga.Saga).
  • domain β€” core types (WorkflowDefinition, SagaRun, Step, RuleDefinition, etc.).
  • engine, engine/verbs β€” coordinator + the 31 saga step implementations + verbs.HandlerFunc.
  • store, store/memory, store/postgres β€” Store interface, in-memory impl, Postgres impl + migrations.
  • api β€” REST handlers, router, and OpenAPI spec (api/openapi.yaml).
  • licensing, secrets, clock β€” resolver interfaces and stubs.

Infrastructure (not for direct import):

  • internal/mq β€” RabbitMQ topology, publisher, consumer.
  • internal/cel, internal/rules β€” CEL evaluator + decision-table rule evaluation.
  • internal/grpc β€” gRPC worker liveness server.
  • internal/config, internal/logging β€” environment config + structured logging.

Binaries and supporting dirs:

  • cmd/api, cmd/engine β€” the two service binaries (reference service-mode apps).
  • clients/go/worker β€” Go worker SDK (nested module) for consuming services.
  • proto/ β€” gRPC worker liveness service + generated code.
  • test/e2e β€” end-to-end tests (require Postgres + RabbitMQ).
  • deployments/helm β€” Helm chart skeleton.
  • ui/ β€” reserved for the future reusable UI framework (outside the Go module; planned).

History

Built as a standalone, solution-agnostic saga engine. The orchestrator and the CEL rule evaluator are deliberately decoupled from any single application so the project can be embedded as a library or run as a service across unrelated solutions.

Directories ΒΆ

Path Synopsis
Package api β€” WebSocket stream handler for the run inspector.
Package api β€” WebSocket stream handler for the run inspector.
clients
go/worker
Package worker is the shared library every saga-action worker imports to run as a worker.
Package worker is the shared library every saga-action worker imports to run as a worker.
go/worker/testing
Package workertest provides an in-process harness so per-service worker tests can verify their handlers in isolation without RabbitMQ + gRPC + the real go-saga-orchestration stack.
Package workertest provides an in-process harness so per-service worker tests can verify their handlers in isolation without RabbitMQ + gRPC + the real go-saga-orchestration stack.
Package clock abstracts time so the engine + verbs can be tested without real wall-clock delays.
Package clock abstracts time so the engine + verbs can be tested without real wall-clock delays.
cmd
api command
Command go-saga-orchestration-api is the REST surface for go-saga-orchestration.
Command go-saga-orchestration-api is the REST surface for go-saga-orchestration.
engine command
Command go-saga-orchestration-engine runs the saga coordinator: it consumes saga.advance messages, dispatches steps, runs the timer dispatcher, and serves the gRPC worker API (ExecuteStep streams) on the configured port (default :9090).
Command go-saga-orchestration-engine runs the saga coordinator: it consumes saga.advance messages, dispatches steps, runs the timer dispatcher, and serves the gRPC worker API (ExecuteStep streams) on the configured port (default :9090).
Package domain contains the in-process types go-saga-orchestration exchanges with its HTTP layer, store, and engine.
Package domain contains the in-process types go-saga-orchestration exchanges with its HTTP layer, store, and engine.
Package engine contains the saga coordinator and the built-in verb dispatch table.
Package engine contains the saga coordinator and the built-in verb dispatch table.
verbs
Package verbs holds built-in verb handlers.
Package verbs holds built-in verb handlers.
examples
basic command
Command basic is a minimal, runnable example of embedding the saga orchestration engine as a library β€” no Postgres or RabbitMQ required.
Command basic is a minimal, runnable example of embedding the saga orchestration engine as a library β€” no Postgres or RabbitMQ required.
internal
cel
Package cel embeds google/cel-go to give go-saga-orchestration built-in verbs and rule definitions one shared expression language.
Package cel embeds google/cel-go to give go-saga-orchestration built-in verbs and rule definitions one shared expression language.
config
Package config loads go-saga-orchestration process-level configuration from environment variables.
Package config loads go-saga-orchestration process-level configuration from environment variables.
grpc
Package grpc wires the engine-side gRPC server.
Package grpc wires the engine-side gRPC server.
logging
Package logging configures the process-wide zerolog logger.
Package logging configures the process-wide zerolog logger.
mq
Package mq wraps the platform RabbitMQ for go-saga-orchestration's needs.
Package mq wraps the platform RabbitMQ for go-saga-orchestration's needs.
rules
Package rules evaluates rule definitions.
Package rules evaluates rule definitions.
storefactory
Package storefactory selects and opens a store.Store backend based on the STORE_TYPE environment variable.
Package storefactory selects and opens a store.Store backend based on the STORE_TYPE environment variable.
Package licensing resolves feature flags for verb license-group gating.
Package licensing resolves feature flags for verb license-group gating.
proto
Package saga is the embedding entrypoint: construct an in-process saga engine, register workflows and custom verbs, and drive runs β€” without running the engine binaries.
Package saga is the embedding entrypoint: construct an in-process saga engine, register workflows and custom verbs, and drive runs β€” without running the engine binaries.
Package secrets resolves a secret_ref string (e.g.
Package secrets resolves a secret_ref string (e.g.
Package store defines the persistence interface go-saga-orchestration uses for definitions, runs, events, and registry rows.
Package store defines the persistence interface go-saga-orchestration uses for definitions, runs, events, and registry rows.
memory
Package memory is an in-process store used by unit tests.
Package memory is an in-process store used by unit tests.
postgres
Package postgres is the production Store implementation.
Package postgres is the production Store implementation.
redis
Package redis is a Redis/Valkey-backed store.Store implementation.
Package redis is a Redis/Valkey-backed store.Store implementation.
storetest
Package storetest provides a backend-agnostic conformance ("contract") test suite for the store.Store interface.
Package storetest provides a backend-agnostic conformance ("contract") test suite for the store.Store interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL