Version v1.x

Job Model

Available in

.NET Node.js Python

Full job model explanation including interfaces, attributes, context, and registration behavior.

In DurableStack, a job is registered work executed by the runtime.

In .NET, that usually means a class implementing a job interface.

In Node.js, that means registering a handler function with runtime.registerJob(...) or runtime.registerRecurring(...).

Each execution of that job is a run with its own lifecycle, attempt count, and status transitions.

Core job contracts

In .NET, use IDurableJob when a job has no payload.

In Node.js, register a handler with the same logical job name and no payload type.

In .NET, use IDurableJob<TArgs> when a job needs typed input.

In Node.js, model typed input with a TypeScript type/interface and pass it to runtime.registerJob<TArgs>(...).

What JobContext gives you

JobContext includes these core concepts (property names differ by runtime):

  • RunId: unique identifier for the specific run instance.
  • JobName: logical job name from registration.
  • Attempt: current attempt number for this run.
  • ScheduledForUtc: the intended schedule/enqueue time for this run.
  • Services (.NET): scoped service provider for resolving dependencies during execution.

In .NET, DurableStack discovers public job classes automatically when options.JobRegistration.AutoDiscoverJobsFromAssembly is true (default).

In Node.js, jobs are registered explicitly at runtime startup.

Use attributes in .NET and registration options in Node.js for execution and schedule metadata:

.NET attribute options and defaults

DurableJobAttribute

  • Name (string?): optional stable job name. Default is class name.
  • MaxAttempts (int): total attempts including initial attempt. Default 3.
  • RetryBehavior (RetryBehavior): FixedDelay or Backoff. Default FixedDelay.
  • RetryInitialDelaySeconds (int): per-job initial retry delay. Default 0 meaning unset, which falls back to runtime options.RetryDelay.

RecurringJobAttribute

  • Cron (string, constructor): required cron expression.
  • TimeZone (string): IANA time zone ID. Default UTC.
  • Enabled (bool): whether recurring schedule starts enabled. Default true.
  • AllowConcurrentRuns (bool): whether overlapping recurring runs are allowed. Default false.

Node.js registration options (equivalent concepts)

  • maxAttempts: total attempts including initial execution.
  • retryBehavior: "FixedDelay" or "Backoff".
  • retryInitialDelaySeconds: per-job initial retry delay.
  • enabled (recurring): whether recurring schedule starts enabled.
  • allowConcurrentRuns (recurring): whether overlapping runs are allowed.

One job, two execution paths

A .NET job with only DurableJob is enqueue-only.

A .NET job with both DurableJob and RecurringJob supports:

  • automatic recurring materialization from cron
  • manual enqueue/run-now using IDurableStackClient (or runtime.enqueue(...) in Node.js)

Runtime registration behavior

During startup, DurableStack builds runtime job registrations.

In .NET, this comes from discovered/registered job classes.

In Node.js, this comes from explicit runtime.registerJob(...) / runtime.registerRecurring(...) calls.

Key registration rules:

  • Duplicate job names are rejected.
  • Duplicate job types are rejected (.NET).
  • Invalid MaxAttempts (<= 0) fails startup.
  • Invalid cron or invalid time zone fails startup.

Activation model and dependency resolution (.NET)

Jobs are resolved from DI using one of two activation modes:

  • ScopedPerExecution (default): creates a fresh scope per run.
  • RootProvider: resolves from root provider (scoped dependencies are not supported).

In most cases, keep ScopedPerExecution.

Explicit registration for advanced scenarios

In .NET, you can explicitly register jobs instead of auto-discovery:

Or with options:

Practical guidance

  • Keep job names stable once production data exists.
  • Keep handlers idempotent so retries are safe.
  • Use typed payload contracts when payload shape matters (IDurableJob<TArgs> in .NET, typed registerJob<TArgs>(...) in Node.js).
  • Keep job classes focused on orchestration and call domain services for business logic.