---
id: STATE-MBR-MEM-013
title: Business event delivery
object: DOM-MBR-MEM-006
product_area: membership
module: memberships
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: stated
    meaning: The business write and its captured event have committed.
  - name: queued
    meaning: Transport has queued work for a configured receiver.
  - name: delivered
    meaning: >-
      Receiver work settled, or dispatch discarded unmatched transport work
      without delivery.
  - name: dead_lettered
    meaning: A repeatedly failing queue message is isolated for operator action.
transitions:
  - from: stated
    to: queued
    trigger: Dispatch claims an event for a configured handler.
    guard: Registered stream handlers match the event.
    side_effects:
      - Queue handler work for consumer delivery.
    who: Change-events dispatcher
    provenance: test
    source: >-
      services-membership:modules/events/tests/unit/change-event-registry.spec.ts#registers
      no handler without the ARN
  - from: queued
    to: delivered
    trigger: The consumer receives successful handler results.
    guard: >-
      Each delivered event is acknowledged successfully by its matching
      receiver.
    side_effects:
      - Settle the queue work; termination handlers call Committees.
    who: Change-events consumer
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#gives
      Committees the business event’s own id
  - from: queued
    to: queued
    trigger: 'The receiver refuses, errors or times out.'
    guard: The queue has not exhausted its receive allowance.
    side_effects:
      - Report retryable transport failure.
    who: Change-events consumer
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#reports
      a failure on the queue’s last receive too
  - from: queued
    to: dead_lettered
    trigger: Delivery exhausts the receive allowance.
    guard: The queue redrive threshold is reached.
    side_effects:
      - Isolate the message and raise the configured alarm.
    who: Change-events consumer
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#reports
      a failure on the queue’s last receive too, which moves t
  - from: stated
    to: delivered
    trigger: Dispatch finds no matching receiver.
    guard: No registered handler wants the event.
    side_effects:
      - Discard the transport row while retaining the published event log.
    who: Change-events dispatcher
    provenance: observed
    source: >-
      services-membership:aws/membership.change-events.ts:publishedEventStream,
      businessEventHandlersOf
invalid: []
concurrency: >-
  The dispatcher runs every minute, which is a schedule interval rather than a
  delivery guarantee. Receiver availability, retries and configuration determine
  actual delay, and no ordering guarantee is provided.
invariants:
  - INV-DOM-MBR-MEM-006-07
  - INV-DOM-MBR-MEM-006-08
evidence:
  - >-
    services-membership at 5994c270: modules/events (services, aws);
    aws/membership.change-events.ts; docs/migrations/events/README.md
---

# Business event delivery

## The Lifecycle

```mermaid
stateDiagram-v2
  state stated
  state queued
  state delivered
  state dead_lettered
  stated --> queued
  queued --> delivered
  queued --> queued
  queued --> dead_lettered
  stated --> delivered
```

## Transitions Explained

Publication captures an event with its write. Dispatch queues work only for configured handlers; unmatched transport is discarded without notifying a receiver. Consumer failure asks the queue to retry, eventually isolating persistent failure in the dead-letter queue.

## What Cannot Happen

Rolled-back writes retain no published event. Successful queue settlement removes that message, but duplicates remain possible around transport acknowledgement; receivers must deduplicate.

## Concurrency and Timing

The dispatcher runs every minute, which is a schedule interval rather than a delivery guarantee. Receiver availability, retries and configuration determine actual delay, and no ordering guarantee is provided.
