Asynchronous Integration

Events between services, eventual consistency, and outbox.

0/1 done

Theory

Between bounded contexts, prefer asynchronous integration via domain events. Synchronous HTTP between services creates temporal coupling — when one is down, the other limps.

Patterns to know:

  • Transactional Outbox — write events in the same DB transaction as the state change.
  • Idempotent Consumer — handle the same event twice safely.
  • Saga — coordinate a multi-step business process across services without distributed transactions.

Worked example — the outbox table

Worked example — the Transactional Outbox table, in SQL and as the YAML you'll write.

The outbox is just a regular DB table. Every state-changing transaction writes both the new state and one row into the outbox — atomically. A separate publisher polls for un-dispatched rows and pushes them to the broker:

CREATE TABLE orders_outbox (
  id              BIGSERIAL  PRIMARY KEY,
  aggregate_id    UUID       NOT NULL,         -- the Order id
  event_type      TEXT       NOT NULL,         -- e.g. 'OrderPlaced'
  payload         JSONB      NOT NULL,         -- the event body
  occurred_at     TIMESTAMPTZ NOT NULL,        -- when the fact happened
  dispatched_at   TIMESTAMPTZ NULL             -- NULL = not yet published
);
CREATE INDEX ON orders_outbox (dispatched_at) WHERE dispatched_at IS NULL;

Same schema, expressed in our seed YAML shape (this is what the playground asks for):

table: orders_outbox
columns:
  - { name: id,            type: bigserial,   pk: true }
  - { name: aggregate_id,  type: uuid,        nullable: false }
  - { name: event_type,    type: text,        nullable: false }
  - { name: payload,       type: jsonb,       nullable: false }
  - { name: occurred_at,   type: timestamptz, nullable: false }
  - { name: dispatched_at, type: timestamptz, nullable: true }

Key invariants:

  • payload is the serialized event body — exactly the structure you defined in Level 3 lesson 1.
  • dispatched_at IS NULL means "not yet sent". The publisher polls for those, sends them, then writes the timestamp.
  • Because the outbox row is written in the same DB transaction as the order itself, you can never "forget" to publish an event.

Reading in progress · 0 of 1 activity done