Workflow Engine

Durable, step-based workflows with persistence and error recovery. Each step is checkpointed — if a workflow crashes, it resumes from the last completed step.

Setup

import "github.com/CodeSyncr/nimbus/workflow"

app.Use(workflow.NewPlugin())
// Dashboard at /workflows

Defining a Workflow

A workflow is a sequence of named steps. Each step is persisted — if the process crashes, it resumes from the last completed step on restart.

wf := workflow.Define("onboarding", func(ctx *workflow.Context) error {
    // Step 1: Create account
    err := ctx.Step("create_account", func() error {
        return createAccount(ctx.Input)
    })
    if err != nil {
        return err
    }

    // Step 2: Send welcome email
    err = ctx.Step("send_email", func() error {
        return sendWelcomeEmail(ctx.Input)
    })
    if err != nil {
        return err
    }

    // Step 3: Setup defaults
    err = ctx.Step("setup_defaults", func() error {
        return setupUserDefaults(ctx.Input)
    })

    return err
})

Starting a Workflow

instance, err := workflow.Start("onboarding", map[string]any{
    "email": "user@example.com",
    "plan":  "pro",
})

Features

FeatureDescription
Step PersistenceEach step is checkpointed; restart resumes from last completed step
Error HandlingSteps can fail and be retried independently
DashboardView workflow status at /workflows
Parallel StepsRun independent steps concurrently
TimeoutsSet step-level and workflow-level timeouts

Real-Life Example: Order Processing

wf := workflow.Define("process_order", func(ctx *workflow.Context) error {
    orderID := ctx.Input["order_id"].(uint)

    // Step 1: Verify payment (if this fails, order stops here)
    err := ctx.Step("verify_payment", func() error {
        return paymentService.Verify(orderID)
    })
    if err != nil {
        return err
    }

    // Step 2: Reserve inventory
    err = ctx.Step("reserve_inventory", func() error {
        return inventoryService.Reserve(orderID)
    })
    if err != nil {
        return err
    }

    // Step 3: Generate shipping label
    err = ctx.Step("shipping_label", func() error {
        return shippingService.CreateLabel(orderID)
    })
    if err != nil {
        return err
    }

    // Step 4: Send confirmation
    return ctx.Step("send_confirmation", func() error {
        return notificationService.OrderConfirmed(orderID)
    })
})

Best Practices

  • Name steps descriptively — they appear in the dashboard and logs
  • Keep each step idempotent — it may be retried on crash recovery
  • Use workflows for multi-step business processes, not simple tasks
  • Pass IDs in input, not full objects — workflow state is serialized
  • Set timeouts on long-running steps to avoid stuck workflows