---
paths:
  - "**/workflows/**"
  - "**/activities/**"
  - "**/*.workflow.ts"
  - "**/*.activity.ts"
  - "**/worker.ts"
  - "**/temporal/**"
---
# Temporal

Workflow and activity conventions for Temporal-backed services — the determinism boundary, file layout, registration, retry defaults, and starting workflows.

## Determinism

Temporal replays a workflow from its event history, so anything non-deterministic in workflow code diverges on replay and corrupts the run — the reason behind every row below. Activities are stateless, idempotent functions — retries re-run them.

| In workflow code | Do | Never |
| --- | --- | --- |
| I/O | call activities | direct DB/API calls |
| Imports | static | dynamic `import()` |
| Data crossing the workflow↔activity boundary | JSON-serializable | classes, functions |
| Time | `sleep()` with durations | absolute timestamps |
| Long runs | `continueAsNew` at ~10k history events | unbounded history |
| Child workflows | `await child.result()`, handle `ChildWorkflowFailure` | fire-and-forget |

## Layout

| Path | Holds |
| --- | --- |
| `app/workflows/<feature>/workflow.js` | the workflow definition |
| `app/workflows/<feature>/activities.js` | that feature's activities — plain exported `async` functions, one destructured-object arg |
| `temporal/config/workflowModules.js` · `activityModules.js` | registration (below) |
| `temporal/providers/` · `temporal/services/` · `temporal/worker.js` | shared infrastructure — `WorkflowStarter`, proxy utilities, worker init — edit sparingly |

## The worked example

Task queues are `<name>_${process.env.ENVIRONMENT}` everywhere — registration, child workflows, API starts. The `retry` block is the project default; keep it unless the feature demands otherwise.

```javascript
import { proxyActivities, startChild } from "@temporalio/workflow";

const { fetchData, pushData } = proxyActivities({
  startToCloseTimeout: "30 minutes",
  retry: {
    initialInterval: "10s",
    maximumInterval: "2m",
    backoffCoefficient: 3.0,
    maximumAttempts: 3,
  },
});

export async function myWorkflow({ tenantId, appId }) {
  const data = await fetchData({ tenantId, appId });
  await pushData({ data });

  const child = await startChild("childWorkflow", {
    workflowId: `${tenantId}-child`,
    taskQueue: `my_queue_${process.env.ENVIRONMENT}`,
    args: [{ tenantId }],
  });
  await child.result();

  return { status: 1, message: "Success", data };
}
```

## Registration

Every workflow registers in `temporal/config/workflowModules.js`; `activityDependencies` names every activity the workflow proxies:

```javascript
export const workflows = {
  myWorkflow: {
    path: new URL("../../app/workflows/feature/workflow.js", import.meta.url)
      .pathname,
    queue: `my_queue_${process.env.ENVIRONMENT}`,
    activityDependencies: ["fetchData", "pushData"],
  },
};
```

The feature's activities spread into `temporal/config/activityModules.js`: `import * as featureActivities from "../../app/workflows/feature/activities.js"`, then `export const activities = { ...featureActivities }`.

## Starting from the API

`startWorkflow("myWorkflow", { workflowId, taskQueue, args })` from `temporal/services/WorkflowStarter.js` — `workflowId` built deterministically from the ids in hand (`` `${tenantId}-${appId}-workflow` ``), `args` always an array holding one destructured-object payload.
