Skip to content

Events and delivery ​

Commands commit events. Consumers (views, policies, and projections) receive them later, when you call flush().

ts
const [receipt, error] = await bank.account.deposit('a-1', 100)
if (error !== null) throw error

// Deliver pending events to policies and projections.
const [, deliveryError] = await bank.flush()
if (deliveryError !== null) throw deliveryError

The commit ​

A successful command saves the new state, its events, and a receipt in one step. The receipt describes the aggregate commit, not the success of every consumer.

Each event is wrapped in an envelope:

FieldMeaning
idStable event ID, e.g. ["Bank","Account","a-1"]:2. Use it as an idempotency key
streamThe source stream
seqSequence number within the stream
atTimestamp (ms)
source{ type, id } of the aggregate or view
eventThe tagged event payload

Delivery ​

  • flush() is explicit. There is no background worker or automatic backoff. Call flush() yourself (an atLeast read also flushes).
  • Failures retry. A failed consumer stops that flush and keeps its checkpoint. The next flush() retries the unacknowledged delivery.
  • At-least-once. Projection writes and policy actions must tolerate repeated delivery.
  • Ordering is per source stream. There's no global ordering across sources.

WARNING

Don't await flush() from inside a consumer that is already running in that flush.

Who sees history ​

ConsumerReplays history?Rebuild trigger
ViewYesBump version
ProjectionYesBump version
PolicyNo, new events onlyNew policies start at the current position

Read your own writes ​

Views are eventually consistent. Pass the receipt to catch up first:

ts
const [summary] = await bank.summary.get('a-1', { atLeast: receipt })

Without atLeast, you get whatever is materialized now, which may be null.

Accord is currently UNLICENSED. No open-source license has been selected yet.