| ← Back to Design Docs | ← Documentation Home |
Responsibility
ServiceCall lifecycle and invariants.Core Model
(tenantId, serviceCallId)| State: name, submittedAt, dueAt, requestSpec, status (Scheduled | Running | Succeeded | Failed), tags, startedAt?, finishedAt?, outcome meta |
Commands (intent)
{ tenantId, name, dueAt, requestSpec, tags? }{ tenantId, serviceCallId, requestSpec } (issued to Execution){ tenantId, serviceCallId, dueAt } (issued to Timer)Events (facts)
{ name, requestSpec\*, submittedAt, tags? }{ dueAt }{ startedAt }{ finishedAt, responseMeta }{ finishedAt, errorMeta }Policies
dueAt <= now THEN issue StartExecution; ELSE issue ScheduleTimer.status == Scheduled AND dueAt <= now
THEN issue StartExecution.status == Running past timeout, mark Failed:Timeout.Identity & Context
IDs Generated:
IDs Received:
Pattern:
```typescript ignore
// Typical: ServiceCallId provided by API in SubmitServiceCall command
// correlationId is Option
// Extract metadata from incoming command envelope (provided by adapter) const commandMetadata = yield * MessageMetadata
// Fallback: If command lacks ServiceCallId (shouldn’t happen per ADR-0010) const finalServiceCallId = serviceCallId ?? yield * ServiceCallId.makeUUID7()
// Construct domain event (validated via Schema) const event = new ServiceCallSubmitted({ tenantId, serviceCallId: finalServiceCallId, name: commandData.name, // … other fields })
// Publish with MessageMetadata Context
// - correlationId: pass through from command (already Option
**Pattern** (orchestration-event-bus.adapter.ts - future implementation):
```typescript ignore
// Adapter extracts MessageMetadata from Context
const metadata = yield * MessageMetadata
// Generate envelope ID (UUID v7)
const envelopeId = yield * EnvelopeId.makeUUID7()
// Construct envelope via Schema class
const envelope: MessageEnvelope.Type = new MessageEnvelope({
id: envelopeId,
type: event._tag,
payload: event,
tenantId: event.tenantId,
timestampMs: yield * clock.now(),
correlationId: metadata.correlationId,
causationId: metadata.causationId,
aggregateId: Option.some(event.serviceCallId),
})
yield * eventBus.publish([envelope])
Rationale: Orchestration is the aggregate owner (ServiceCall). It receives ServiceCallId from API (preferred per ADR-0010) or generates via UUID7 if missing. Adapter generates EnvelopeId (UUID v7) for broker deduplication. Workflow provides MessageMetadata Context with correlationId from HTTP request and causationId from triggering command envelope. See ADR-0010 for identity generation strategy, ADR-0011 for schema patterns, and ADR-0013 for MessageMetadata Context pattern.
Ports
Out-of-Scope (here)
Acceptance (MVP)
Scheduling Path (Due StartExecution Running)
flowchart TB
subgraph "Orchestration"
A["ServiceCallSubmitted / ServiceCallScheduled"]
B{"dueAt <= now"}
C["StartExecution (command)"]
F["ServiceCallRunning (domain event)"]
end
subgraph "Timer"
D["ScheduleTimer (command)"]
E["DueTimeReached (event)"]
end
A --> B
B -->|yes| C
B -->|no| D
D --> E
E --> C
C --> F
classDef cmd fill:#e3f2fd,stroke:#1e88e5,color:#0d47a1
classDef proc fill:#fff3e0,stroke:#fb8c00,color:#e65100
classDef dom fill:#e8f5e9,stroke:#43a047,color:#1b5e20
class C,D cmd
class E proc
class A,F dom
Event Mapping (Process Domain)
flowchart TB
subgraph "Execution (process events)"
ES[ExecutionStarted]
EOK[ExecutionSucceeded]
EKO[ExecutionFailed]
end
subgraph "Orchestration (domain events)"
DR[ServiceCallRunning]
DS[ServiceCallSucceeded]
DF[ServiceCallFailed]
end
ES -->|set Running| DR
EOK -->|set Succeeded| DS
EKO -->|set Failed| DF
classDef proc fill:#fff3e0,stroke:#fb8c00,color:#e65100
classDef dom fill:#e8f5e9,stroke:#43a047,color:#1b5e20
class ES,EOK,EKO proc
class DR,DS,DF dom
Sequence (Scheduled StartExecution Outcome)
sequenceDiagram
autonumber
participant ORCH as Orchestration
participant DB as DomainDB
participant EXEC as Execution
link ORCH: Doc @ ./orchestration.md
link EXEC: Doc @ ./execution.md
Note over ORCH,EXEC: solid = command/port, dashed = event
ORCH->>DB: create Scheduled (tx) + outbox ServiceCallSubmitted/Scheduled
alt dueAt <= now
ORCH->>EXEC: StartExecution [command]
else
ORCH->>TIMER: ScheduleTimer [command]
end
EXEC-->>ORCH: ExecutionStarted [event]
ORCH->>DB: set Running (tx) + outbox ServiceCallRunning
alt success
EXEC-->>ORCH: ExecutionSucceeded [event]
ORCH->>DB: set Succeeded (tx) + outbox ServiceCallSucceeded
else failure
EXEC-->>ORCH: ExecutionFailed [event]
ORCH->>DB: set Failed (tx) + outbox ServiceCallFailed
end
Inputs/Outputs Recap
Messages
State access