Skip to content

Authority Doc / Public V1 Backend Events

The Event And Outbox Standard keeps backend side effects reliable without pretending delivery is exactly-once.

This page is the server-side source of truth for domain and integration event naming, envelope fields, transactional outbox persistence, publisher behavior, consumer idempotency, projection rebuild rules, dead-letter handling, and backend evidence gates for Public V1.

AuthorityBackend EventsTransactional OutboxPublic V1

Note

This page covers backend/server behavior only. Mobile operation queues and reconciliation belong to offline-sync-protocol.

Server Scope

Keep event authority on the server boundary. It coordinates persistence and delivery, not mobile local sync design.

This Standard Owns

  • Server-side domain, integration, and job event categories.
  • Event names, versioning, and required envelope fields.
  • Transactional outbox persistence, publisher lifecycle, job lookup facts, and required claim/publish/retry/dead-letter transition evidence.
  • Consumer dedupe, projection rebuild rules, and dead-letter handling.
  • Feature-page event documentation and test expectations.

This Standard Does Not Own

  • REST request and response contracts owned by api-contract-standard.
  • Mobile local queue schemas, device receipts, and replay flows owned by offline-sync-protocol.
  • Provider-specific webhook payload formats outside normalized internal events.
  • Analytics visualization rules beyond the event facts they consume.
  • Operational event names, metric names, aggregation, dashboards, and alerts owned by observability-plan.

Note

Server outbox is not the same thing as mobile local outbox.

Event Categories

Use one vocabulary for why a record exists before arguing about brokers, queues, or handlers.

CategoryPurposeRule
Domain eventCaptures a business fact inside the bounded context.Domain events describe past business facts and are emitted from aggregate decisions after state is accepted.
Integration eventCommunicates a stable fact to another context or delivery concern.Integration events are stable cross-context facts and must avoid leaking persistence or request-shape details.
Job commandTriggers operational work such as sending, materializing, or recalculating.Job commands are operational instructions and must remain idempotent because retries are normal.

Naming And Versions

Names should tell operators what happened without opening the payload.

Naming Rule

Use context.aggregate.fact.v1 for published event names.

  • client.invitation.consumed.v1
  • client.relationship.activated.v1
  • daily_execution.workout_log.completed.v1
  • nutrition.meal_swap.approved.v1

Versioning Rules

  • Breaking shape or semantic changes require a new event version and a new event name suffix.
  • Additive fields stay in the current version when consumers can safely ignore them.
  • Consumers must ignore unknown fields and keep required-field validation explicit.

Publication Scope

Only accepted operations publish. A rejected or conflicted sync operation produces a durable receipt and no event.

Envelope

Every published event needs a stable shell before payload-specific fields are considered safe.

FieldRequirementRule
eventIdRequiredStable unique identifier for the published event record.
eventNameRequiredCanonical event name such as client.relationship.activated.v1.
eventVersionRequiredExplicit numeric or string version aligned with the name suffix.
occurredAtRequiredUTC instant when the business fact happened.
recordedAtRequiredUTC instant when the outbox record was persisted.
producerRequiredIdentifies the bounded context or module that emitted the event.
aggregateTypeRequiredNamed aggregate or source entity category.
aggregateIdRequiredStable id for the source aggregate instance.
workspaceIdRequired when applicableWorkspace diagnostic scope when a workspace owns the event.
relationshipIdRequired when applicableRelationship diagnostic scope when a coach-client relationship owns the event.
actorRequiredStructured actor summary for user, service, or system initiator.
correlationIdRequiredAPI-issued identifier for one bounded product workflow, propagated across requests, events, jobs, and provider calls; distinct from W3C traceId.
causationIdRequiredParent command, request, or event id that caused this publication.
idempotencyKeyRequired when applicableOperation dedupe key used by the originating command.
payloadRequiredBusiness payload with only fields needed by approved consumers.
metadataRequiredNon-business delivery metadata, diagnostics, and schema hints.

Producers and consumers propagate the validated API-issued correlationId. They never synthesize a replacement from an untrusted client field.

Note

Payloads must not include bearer tokens, signed upload URLs, raw provider secrets, password/auth material, or unnecessary health/nutrition/media content.

Outbox Persistence

The outbox is a database fact table for reliable publication, not a magical delivery guarantee.

outbox_events

Persist integration events in outbox_events inside the same business transaction that accepted the aggregate change.

Minimum ColumnPurpose
idPrimary row identifier for internal storage and admin tooling.
event_idStable unique event identifier.
event_nameCanonical event name.
event_versionExplicit schema version.
producerBounded context or module that emitted the event.
aggregate_typeProducer aggregate category.
aggregate_idProducer aggregate id.
workspace_idWorkspace diagnostic scope.
relationship_idRelationship diagnostic scope.
correlation_idAPI-issued identifier for one bounded product workflow, carried through chained work and distinct from W3C traceId.
causation_idParent command, request, or event id that caused publication.
idempotency_keyOriginating operation dedupe key when one exists.
payload jsonbStructured business payload or full event envelope body.
metadata jsonbDelivery metadata, diagnostics, and schema hints.
statusPublication lifecycle state.
available_atWhen the publisher may try again.
attempt_countRetry counter.
last_attempt_atUTC time of the latest publish attempt.
published_atSuccess time when published.
locked_byCurrent worker lease owner when the row is claimed.
locked_untilLease expiry time so stalled claims can recover.
last_error_codeLatest retry or dead-letter code.
last_error_messageLatest operator-readable failure detail.
created_atInsert time.
updated_atLast lifecycle update time.
StatusMeaning
PENDINGReady for the publisher scan.
CLAIMEDA publisher worker has leased the row for an attempt.
PUBLISHEDBroker handoff succeeded and the row was marked complete.
FAILED_RETRYABLEAttempt failed but another bounded retry is allowed.
DEAD_LETTEREDAutomatic retries stopped and operator review is required.
DISCARDEDOperator intentionally retired the row with an auditable reason.

Constraints

  • Unique event_id constraint is mandatory.
  • Producer dedupe must prevent duplicate outbox inserts for the same accepted operation.
  • For sync-originated commands the originating clientOperationId is the event idempotencyKey, and the durable sync receipt is the producer dedupe record: a replayed operation returns its retained receipt and inserts no second outbox row.
  • Pending scans need an index on status plus availability time.
  • Replay and operator tooling need indexes for status, event name, and published time.
  • Workspace and relationship diagnostics need searchable scope columns.

Persistence Posture

The outbox row is durable evidence that the backend accepted responsibility to publish. It is not proof that every downstream consumer processed the event.

Transaction Rule

Never save business state and publication intent in separate commit boundaries.

Required Rule

The business state change and outbox insert commit in the same database transaction.

await tx.run(async () => {
  const result = aggregate.accept(command);
  await repository.save(result.aggregate);
  await outbox.save(result.integrationEvents);
});

Publisher Behavior

Publisher workers make progress through repeated, observable, bounded attempts.

1. Scan

Find PENDING or retryable rows whose availability time has arrived.

Scan

2. Claim

Lease rows into CLAIMED so parallel workers do not publish the same attempt blindly.

Claim

3. Publish

Send the event envelope to the broker or internal bus.

Publish

4. Resolve

Mark success, schedule a bounded retry, or dead-letter with logs and metrics.

Resolve

Reliability Rules

  • Delivery is at-least-once, never exactly-once by assumption.
  • Retry delays use bounded backoff with clear attempt ceilings.
  • Every transition emits the required lifecycle fact with transition, outcome, event family, attempt/age, and approved lookup identifiers as applicable. Exact log-event and metric names are canonical in the Observability Plan.
  • A crash after publish but before marking success can cause duplicate delivery, so consumers must tolerate repeats.

Runtime Job Lookup

jobId is the queue-adapter-normalized BullMQ runtime lookup id included with attempt number in each job completion/failure log. It is not a business identifier, event-envelope field, outbox column, domain record, or metric label.

Failure Outcome

Publisher failures must preserve the row, increment attempts, keep the latest error code, and move irrecoverable rows to dead-letter instead of silently dropping them.

Ordering

Ordering guarantees must be named precisely because brokers, workers, and retries all reorder work.

What Is Not Guaranteed

  • Global ordering is not guaranteed.
  • Cross-aggregate publication order is not guaranteed.
  • Retry timing can place an older event behind a newer one.

What Must Be Explicit

  • Per-aggregate ordering requires an explicit sequence or partition strategy.
  • Consumers must no-op safely when stale or already-applied events arrive late.
  • Feature pages must call out any ordering key needed for correctness.

Consumers

Consumers prove correctness by making duplicates boring.

Dedupe Rule

Consumers require processed-event receipts by eventId or a deterministic consumer dedupe key.

Success Rule

Duplicate delivery returns success or no-op rather than a noisy failure.

Atomic Consumer State

Projection or job state changes and the processed-event receipt save together in one transaction.

Projections

Read models are disposable if their rebuild source is explicit and durable.

StoreRoleRule
Source aggregate tablesCanonical business state.Own validation and lifecycle truth.
Outbox delivery recordsReliable publication evidence.Track event delivery lifecycle.
Analytics or event fact tablesLong-lived event-derived history.Preserve durable analytical facts when feature analytics require them.
UI read modelsQuery-optimized views.Feature pages must name the rebuild source so the model can be regenerated.

Dead Letters

Dead letters are an operational queue with audit needs, not a hidden graveyard.

Authorized Operator Boundary

Inspect, retry, replay, or discard only through an approved operator mechanism. Record actor, reason, time, and target in audit evidence. Direct database editing is prohibited. Observability detects and links to this mechanism but does not authorize or perform recovery.

Recorded Facts

  • Failure category.
  • Last error code.
  • Attempt count.
  • Operator reason for replay or discard.

Replay Rules

  • Replay of the same logical event uses the original eventId.
  • A replacement event uses a new eventId.
  • Discard must remain auditable with actor, reason, and time.

Security And Privacy

Events should carry just enough detail to do the job and no more.

Payload Minimization

  • Prefer identifiers and normalized references over rich copies of private records.
  • Use secure asset references instead of long-lived direct media URLs.
  • Do not publish secrets, auth material, or provider internals into events.

Authority Hand-offs

Retention and purge rules belong to privacy-and-retention. Support and admin access rules belong to roles-and-permissions.

Feature-Page Rules

Every feature authority page that emits or consumes events must document the same checklist.

Required Checklist

  • Emitted domain events.
  • Promoted integration events.
  • Consumed integration events.
  • Ordering key or partition rule.
  • Idempotency keys and dedupe strategy.
  • Projection rebuild source.
  • Dead-letter behavior.
  • Retry and idempotency tests.

Testing Gate

Evidence must prove the event path from aggregate decision to duplicate-safe consumption.

Required EvidenceWhat Must Pass
Domain event unit testsAggregate decisions emit the expected domain events for accepted state changes.
Transaction and outbox integration testsBusiness state and outbox rows commit together.
Rollback testsFailed transactions leave neither partial state changes nor stray outbox rows.
Publisher retryRetryable publish failures reschedule with bounded backoff and preserved diagnostics.
Duplicate deliveryCrash-and-retry or duplicate publish attempts do not create unsafe double side effects.
Consumer idempotencyReceipts or dedupe keys make duplicates return success/no-op.
Dead-letter transitionAttempt ceilings move failed events into dead-letter with operator-readable facts.
Projection rebuildNamed rebuild sources can regenerate the read model.
Event schema or contract testsPublished envelopes match the approved name, version, and required fields.

This standard depends on adjacent authority pages for workflow, permissions, REST contracts, sync boundaries, operations, and release proof.

Workflow State Map

Lifecycle transitions that often cause event publication.

Roles And Permissions

Backend authorization and support access boundaries for emitted facts.

API Contract Standard

REST request and response rules that hand work into backend events.

Offline Sync Protocol

Separate authority for mobile queues, receipts, replay, and conflict handling.

Integration Register

External systems and providers that may consume or produce normalized events.

Observability Plan

Canonical signal names, transition aggregation, dashboards, alerts, and operational detection; Event And Outbox retains lifecycle and recovery.

Test And Release Gate

CI, merge, and release evidence requirements for event-driven changes.

CoachMe internal planning documentation.