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:
- Durable state persistence -- Temporal's durable model for workflow history.
- Automatic retries -- Configurable retry policies with exponential backoff.
- Timer management -- Server-side timers that survive process restarts.
- Signal handling -- Asynchronous inter-workflow communication.
- Visibility -- Querying workflow executions by search attributes.
- Multi-language workers -- Temporal's polyglot architecture allows Grove workers to coexist with workers in other languages.
Grove adds value on top by providing a declarative, type-safe language for defining what Temporal executes.
Conceptual Mapping
| Grove Concept | Temporal Concept |
|---|---|
| Workflow | Workflow Definition |
| Activity | Activity Definition |
| Node | Activity invocation (or child workflow) |
| Edge | Dependency ordering (sequencing via await) |
start node | Child Workflow (synchronous) |
spawn node | Child Workflow (fire-and-forget / async) |
signal | Signal |
sleep | Timer / workflow.sleep() |
retry_policy | RetryPolicy on activity options |
search block | Search Attributes (upsert) |
on compensated | Saga pattern (manual compensation) |
map node | Fan-out pattern with Promise.all() |
parallel node | Promise.all() on activity futures |
branch node | Conditional 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:
- Registering workflows and activities with the Temporal worker.
- Mapping Grove's DAG structure to sequential/parallel activity invocations.
- Translating Grove types to serializable payloads.
- Wiring retry policies to Temporal's
RetryPolicyconfiguration.
2. Registry
The bridge maintains a registry that maps Grove resource names to Temporal workflow and activity types:
| Grove Resource Name | Temporal Type Name |
|---|---|
acme::orders::fulfill_order::v1 | acme.orders.fulfill_order.v1 |
acme::orders::charge_payment::v1 | acme.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:
- Uniqueness -- No collisions across projects, modules, or versions.
- Readability -- Temporal's UI displays meaningful names.
- Versioning -- Different versions coexist as distinct workflow types, allowing safe rollout.
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 Field | Temporal Field |
|---|---|
max_attempts | maximumAttempts |
backoff | initialInterval |
max_backoff | maximumInterval |
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:
- Temporal Server -- The Temporal cluster (self-hosted or Temporal Cloud).
- Grove Workers -- Processes that run the compiled workflow and activity code. Each worker subscribes to one or more Task Queues.
- 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
- Determinism -- Temporal requires workflow code to be deterministic. Grove's declarative model naturally enforces this -- workflows are DAGs with no non-deterministic operations in the workflow definition itself. All side effects are isolated in activities.
- Payload size -- Temporal has payload size limits (typically 2MB). Large data should be stored externally, with references passed through the workflow.
- History length -- Long-running workflows with many activities accumulate event history. Grove's
max_historyconfiguration (where supported) allows setting thresholds for continue-as-new. - Local development -- For development and testing, the Grove runtime can execute workflows without Temporal using an in-memory executor. The behavior matches Temporal semantics but does not require a running Temporal server.
See Also
- Workflows Overview -- DAG model and workflow structure
- Activities -- Activity declarations and retry policies
- Durable Execution -- Sleep, signals, and timers
- Compensation & Sagas -- Saga pattern implementation
- Full Language Reference -- Complete grammar specification