process-manager¶
Stateful coordinator that drives a long-running flow. See Concepts: Process Manager.
Schema¶
properties:
scope:
type: object
properties:
domain:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [domain]
additionalProperties: false
deliveryGuarantee:
type: string
enum: [at-least-once, at-most-once]
idempotency:
type: object
properties:
owner:
type: string
enum:
- self
- downstream
- infrastructure
- none
- not-required
strategy:
type: string
required: [owner]
additionalProperties: false
correlatedBy:
oneOf:
- type: object
properties:
source:
const: event-field
field:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [source, field]
additionalProperties: false
state:
type: object
invariants:
type: array
items:
type: object
properties:
name:
type: string
pattern: "^[a-z][a-z0-9-]*$"
rule:
type: string
required: [name, rule]
additionalProperties: false
constraints:
type: array
items:
type: object
properties:
name:
type: string
pattern: "^[a-z][a-z0-9-]*$"
rule:
type: string
required: [name, rule]
additionalProperties: false
startsWhen:
type: array
minItems: 1
items:
oneOf:
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
aggregate:
type: string
pattern: "^[a-z][a-z0-9-]*$"
event:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, aggregate, event]
additionalProperties: false
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
event:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, event]
additionalProperties: false
endsWhen:
type: array
minItems: 1
items:
type: object
properties:
name:
type: string
pattern: "^[a-z][a-z0-9-]*$"
condition:
type: string
required: [name, condition]
additionalProperties: false
timers:
type: array
items:
type: object
properties:
name:
type: string
pattern: "^[a-z][a-z0-9-]*$"
description:
type: string
required: [name]
oneOf:
- properties:
after:
type: object
properties:
value:
type: integer
minimum: 1
unit:
type: string
enum: [seconds, minutes, hours, days, weeks, months, years]
required: [value, unit]
additionalProperties: false
required: [after]
- properties:
at:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [at]
unevaluatedProperties: false
reactions:
type: array
minItems: 1
items:
type: object
properties:
when:
oneOf:
- oneOf:
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
aggregate:
type: string
pattern: "^[a-z][a-z0-9-]*$"
event:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, aggregate, event]
additionalProperties: false
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
event:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, event]
additionalProperties: false
- type: object
properties:
timer:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [timer]
additionalProperties: false
rule:
type: string
emits:
type: array
items:
oneOf:
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
aggregate:
type: string
pattern: "^[a-z][a-z0-9-]*$"
command:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, aggregate, command]
additionalProperties: false
- type: object
properties:
boundedContext:
type: string
pattern: "^[a-z][a-z0-9-]*$"
dynamicConsistencyBoundary:
type: string
pattern: "^[a-z][a-z0-9-]*$"
command:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [boundedContext, dynamicConsistencyBoundary, command]
additionalProperties: false
setTimers:
type: array
items:
type: string
pattern: "^[a-z][a-z0-9-]*$"
cancelTimers:
type: array
items:
type: string
pattern: "^[a-z][a-z0-9-]*$"
required: [when, rule]
additionalProperties: false
required:
- scope
- deliveryGuarantee
- correlatedBy
- state
- startsWhen
- endsWhen
- reactions
allOf:
- if:
properties:
deliveryGuarantee:
const: at-least-once
required: [deliveryGuarantee]
then:
required: [idempotency]
Anatomy¶
A Process Manager scopes to a Domain via scope.domain. Domain-scope is necessary because a Process Manager often coordinates across Bounded Contexts; binding it to a single one would misrepresent its reach.
The required deliveryGuarantee field carries at-least-once or at-most-once, and the schema enforces the same conditional pairing as on Event Handlers and Policies: an at-least-once Process Manager must also carry an idempotency object. The shape of idempotency is the same – idempotency.owner from the closed set self, downstream, infrastructure, none, not-required, plus an optional strategy carrying prose.
The correlatedBy field is a discriminated oneOf on source. Today the only variant is event-field, which pairs source with a required field. The named field must exist on every Event in startsWhen and in any reactions[].when Event – the linter checks this; the schema only enforces the structural shape. The instance key for a running Process Manager is the value found at that field path on each incoming Event.
The state field is a required JSON Schema object describing per-instance state. Unlike on an Aggregate, a Process Manager's state is bookkeeping for the flow itself: which Events have arrived, which decisions have been made, which timers are armed.
The startsWhen field is a single Event reference – Aggregate-bound (boundedContext, aggregate, event) or BC-scoped (boundedContext, event) – that triggers a new instance. The endsWhen array holds the conditions under which an existing instance terminates, with at least one entry. Each entry is { name, rule }, where rule is prose.
The reactions array binds triggers to per-trigger logic, with at least one entry. Each reaction has a required when and rule, and may carry any of emits, setTimers, cancelTimers. The when field is itself a oneOf: it accepts an Event reference (Aggregate-bound or BC-scoped) or a timer trigger ({ timer: <name> }) where <name> is one of the timers declared on the Process Manager. The rule is prose. The emits array carries Command references, each Aggregate-bound (boundedContext, aggregate, command) or DCB-bound (boundedContext, dynamicConsistencyBoundary, command). The setTimers and cancelTimers arrays carry timer names.
The optional timers array declares timer definitions the Process Manager may arm. Each timer requires a name and an optional description, plus exactly one of after or at (a oneOf). The after shape is { value, unit }, with value a positive integer and unit one of seconds, minutes, hours, days, weeks, months, years. The at shape names a state field whose value carries the absolute target timestamp.
The optional invariants array holds named rules every instance's state must satisfy; the optional constraints array holds entity-wide activation conditions. Both follow the { name, rule } shape with prose rules.
description and metadata carry the usual free-form prose and non-semantic attachments. apiVersion is schema.esdm.io/core/v1, kind is process-manager, and name is the Process Manager's kebab-case identifier.