Temporal Integration

Status: [Designed] -- Area 2 of the Grove language. Syntax and semantics are defined; implementation is in progress.

Grove's durable execution model is designed to integrate with Temporal (and similar workflow engines) through a bridge architecture. The Grove runtime compiles workflow and activity declarations into Temporal-compatible constructs, handles type mapping, and manages worker registration. This page explains how Grove maps to Temporal concepts, the bridge architecture, and considerations for SDK interop.

Prerequisites: Workflows Overview, Activities, Durable Execution. What you'll learn: How Grove workflows map to Temporal workflows, the bridge architecture, type naming conventions, worker registry, and interoperability with Temporal SDKs.

Why Temporal

Temporal is a battle-tested durable execution platform. Rather than building a new workflow engine from scratch, Grove leverages Temporal's infrastructure for:

Grove adds value on top by providing a declarative, type-safe language for defining what Temporal executes.

Conceptual Mapping

Grove ConceptTemporal Concept
WorkflowWorkflow Definition
ActivityActivity Definition
NodeActivity invocation (or child workflow)
EdgeDependency ordering (sequencing via await)
start nodeChild Workflow (synchronous)
spawn nodeChild Workflow (fire-and-forget / async)
signalSignal
sleepTimer / workflow.sleep()
retry_policyRetryPolicy on activity options
search blockSearch Attributes (upsert)
on compensatedSaga pattern (manual compensation)
map nodeFan-out pattern with Promise.all()
parallel nodePromise.all() on activity futures
branch nodeConditional logic in workflow code
Workflow version (vN)Task Queue routing / Workflow Type naming

Bridge Architecture

The bridge sits between Grove's declarative definitions and Temporal's SDK. It has three responsibilities:

1. Code Generation

The Grove compiler translates workflow and activity declarations into Temporal worker code. Each workflow becomes a Temporal workflow function, and each activity becomes a Temporal activity function.

Grove Source (.grove files)
        |
    [Compiler]
        |
   Temporal Worker Code
        |
   [Temporal Worker]
        |
   Temporal Server

The generated code handles:

2. Registry

The bridge maintains a registry that maps Grove resource names to Temporal workflow and activity types:

Grove Resource NameTemporal Type Name
acme::orders::fulfill_order::v1acme.orders.fulfill_order.v1
acme::orders::charge_payment::v1acme.orders.charge_payment.v1

The naming convention uses dots instead of double colons, matching Temporal's type naming conventions. This mapping is deterministic -- given a Grove resource name, the Temporal type name is always predictable.

3. Task Queue Management

Each module maps to a Temporal Task Queue. All workflows and activities declared in a module are registered on the same Task Queue:

Module: orders  ->  Task Queue: acme.orders
Module: shipping -> Task Queue: acme.shipping

Workers subscribe to their module's Task Queue and process workflow and activity tasks for that module.

Type Naming

Grove's four-part resource names (project::module::name::version) map to Temporal type names using dot separators:

Grove:    acme::fulfillment::process_order::v2
Temporal: acme.fulfillment.process_order.v2

This convention ensures:

Workflow Versioning and Temporal

Grove's vN versioning maps cleanly to Temporal's model. Each version is a separate Temporal Workflow Type:

fulfill_order v1 -> acme.orders.fulfill_order.v1 (Workflow Type)
fulfill_order v2 -> acme.orders.fulfill_order.v2 (Workflow Type)

In-flight executions of v1 continue running on workers that have v1 registered. New executions default to the latest version. This avoids the complexity of Temporal's workflow.getVersion() / patching mechanism -- version migration is handled at the Grove language level through explicit version declarations.

Retry Policy Mapping

Grove's retry_policy block maps directly to Temporal's RetryPolicy:

Grove FieldTemporal Field
max_attemptsmaximumAttempts
backoffinitialInterval
max_backoffmaximumInterval
retry_policy {
  max_attempts: 5
  backoff: 1s
  max_backoff: 60s
}

Translates to:

RetryPolicy {
  maximumAttempts: 5
  initialInterval: 1s
  maximumInterval: 60s
  backoffCoefficient: 2.0  // default
}

Search Attributes

Grove's search block maps to Temporal's search attributes, which are indexed metadata on workflow executions:

workflow fulfill_order v1 {
  search {
    order_id: input.order_id
    customer_id: input.customer_id
  }
  // ...
}

These become upserted search attributes on the Temporal workflow execution, queryable through Temporal's visibility API:

SELECT * FROM executions WHERE order_id = 'ord-123'

Search attribute types must be compatible with Temporal's supported attribute types: String, Int, Bool, DateTime.

SDK Interoperability

Because Grove workflows compile to standard Temporal workflow and activity types, they can interoperate with code written in Temporal's native SDKs (Go, Java, TypeScript, Python, .NET):

Calling Grove Activities from SDK Code

An SDK workflow can invoke a Grove activity by referencing its Temporal type name:

// TypeScript SDK example
const result = await workflow.executeActivity('acme.orders.charge_payment.v1', input, {
  taskQueue: 'acme.orders',
  startToCloseTimeout: '30s',
});

Calling SDK Activities from Grove

A Grove service declaration can wrap an activity implemented in a Temporal SDK:

service legacy_billing {
  operation calculate_tax {
    input: { amount: Decimal, region: String }
    output: { tax: Decimal, rate: Decimal }
  }
}

The bridge routes the fetch call to the appropriate Task Queue where the SDK-implemented activity is registered.

Sending Signals Between Grove and SDK Workflows

Signals are interoperable. A Grove workflow waiting on a signal can receive it from an SDK workflow, and vice versa, as long as the signal name and payload shape match.

Deployment Model

A typical deployment has:

  1. Temporal Server -- The Temporal cluster (self-hosted or Temporal Cloud).
  2. Grove Workers -- Processes that run the compiled workflow and activity code. Each worker subscribes to one or more Task Queues.
  3. Module-per-Worker -- Each Grove module runs in its own worker process, subscribing to its module's Task Queue.
[Temporal Server]
      |
      +-- Task Queue: acme.orders
      |       |-- Grove Worker (orders module)
      |
      +-- Task Queue: acme.shipping
      |       |-- Grove Worker (shipping module)
      |
      +-- Task Queue: acme.billing
              |-- Grove Worker (billing module)

This model allows independent scaling, deployment, and versioning of each module's workers.

Limitations and Considerations

See Also