Workflows
Status: [Designed] -- Area 2 of the Grove language. Syntax and semantics are defined; implementation is in progress.
Workflows are directed acyclic graphs (DAGs) that orchestrate multi-step, multi-service operations. Each node in the graph is a unit of work -- an activity, a branching decision, a parallel fan-out, or a child workflow invocation. Edges define execution order. The runtime schedules nodes, retries failures, and persists progress so that workflows survive process restarts.
Prerequisites: Concepts Overview, familiarity with Activities.
What you'll learn: How to declare workflows, wire nodes with edges, use branching and parallelism, fan out with map, attach search attributes, and version workflows safely.
The DAG Model
A workflow is a named, versioned DAG:
workflow fulfill_order v1 {
node validate = validate_inventory v1
node charge = charge_payment v1
node ship = create_shipment v1
edge validate -> charge
edge charge -> ship
}
workflow-- Top-level declaration. The name and version form part of the resource identifier (project::module::fulfill_order::v1).node-- A named reference to an activity (or other construct). The right-hand side is an activity reference with a version tag.edge-- A dependency.edge A -> Bmeans B cannot start until A completes successfully.
Nodes with no inbound edges are root nodes and start immediately when the workflow is invoked. Nodes with no outbound edges are leaf nodes whose collective completion marks the workflow as finished.
Nodes
Every node has a name and an assignment. The assignment determines what kind of work the node performs.
Activity Nodes
The most common node type. References a declared activity:
node charge = charge_payment v1
You can pass input mappings inline when the activity's input needs to be derived from the workflow input or from predecessor node outputs:
node charge = charge_payment v1 {
amount: input.total
currency: input.currency
customer_id: input.customer_id
}
The input root object refers to the workflow's own input. Named predecessor nodes are also available as root objects in edge mappings.
Start Nodes
A start node invokes a child workflow and waits for it to complete before the parent workflow continues:
node sub = start child_workflow v1 {
order_id: input.order_id
}
Spawn Nodes
A spawn node starts a child workflow but does not wait for it to finish. The parent workflow continues immediately:
node async_notify = spawn send_notifications v1 {
recipient: input.customer_email
}
Edges
Edges define execution order. They form the DAG structure:
edge validate -> charge
edge charge -> ship
edge charge -> notify
In this example, ship and notify both depend on charge but are independent of each other -- they execute in parallel once charge completes.
Implicit Data Flow
When a node depends on a predecessor via an edge, the predecessor's output is available as a root object in the downstream node's field mappings:
workflow order_pipeline v1 {
node validate = validate_order v1 {
items: input.items
}
node charge = charge_payment v1 {
amount: validate.total // output of the validate node
currency: input.currency
}
edge validate -> charge
}
Branching
Use branch to conditionally choose one path:
node route = branch {
input.tier == "premium" -> premium_fulfillment v1 {
order_id: input.order_id
}
input.tier == "standard" -> standard_fulfillment v1 {
order_id: input.order_id
}
_ -> default_fulfillment v1 {
order_id: input.order_id
}
}
Branch arms are evaluated top to bottom. The first matching condition wins. A wildcard _ arm is the fallback. The runtime executes only the selected activity.
Parallel Execution
Use parallel to execute multiple activities concurrently:
node checks = parallel {
credit = check_credit v1 { customer_id: input.customer_id }
fraud = check_fraud v1 { customer_id: input.customer_id }
inventory = check_inventory v1 { items: input.items }
}
All three activities run at the same time. The checks node completes only when every parallel activity has finished. Downstream nodes can access individual results: checks.credit, checks.fraud, checks.inventory.
Map (Fan-Out)
Use map to execute an activity once per item in a collection:
node process_items = map input.items {
activity = process_line_item v1 {
item_id: item.id
quantity: item.quantity
}
}
The runtime fans out, creating one activity execution per element. The item variable refers to the current element. The result of a map node is a list of activity outputs.
Search Attributes
Search attributes are key-value pairs attached to a workflow execution for external visibility and querying. They are declared with the search block:
workflow fulfill_order v1 {
search {
order_id: input.order_id
customer_id: input.customer_id
status: "started"
}
node validate = validate_inventory v1
// ...
}
Search attributes are available in the workflow execution's metadata and can be queried through the runtime's visibility APIs. The input root object is available in search attribute mappings.
Versioning
Workflows are versioned with vN tags. When you need to change a workflow's structure, create a new version:
workflow fulfill_order v2 {
node validate = validate_inventory v1
node charge = charge_payment v2 // updated activity
node ship = create_shipment v1
node notify = send_confirmation v1 // new node
edge validate -> charge
edge charge -> ship
edge charge -> notify
}
In-flight executions continue running against their original version. New executions use the latest version unless a specific version is requested. The resource naming convention (project::module::fulfill_order::v2) ensures versions are distinct.
Workflow Lifecycle
- A workflow is started by a trigger, schedule, route, another workflow (
start/spawn), or an explicit API call. - Root nodes begin executing immediately.
- As each node completes, the runtime evaluates which downstream nodes are now unblocked.
- When all leaf nodes complete, the workflow is completed.
- If a node fails and exhausts its retry policy, the workflow enters a failed state.
- If compensation is configured, the runtime rolls back completed nodes in LIFO order (see Compensation & Sagas).
See Also
- Activities -- The units of work that nodes reference
- Services -- External API contracts called by activities
- Triggers -- Start workflows from domain events
- Schedules -- Start workflows on a cron schedule
- Compensation & Sagas -- Rollback and saga patterns
- Durable Execution -- Sleep, signal, and suspend/resume
- Temporal Integration -- Bridge architecture for Temporal