Outbox (Outbox v0.1.0-beta.1)

Copy Markdown View Source

Public facade for the Outbox transactional event bus.

Producers call publish/2 from inside their domain transactions to emit events that subscribers (registered in application config) react to via Outbox.SubscriberJob after the dispatcher fans them out.

See Outbox.Subscriber for the subscriber contract and the README for the full architecture overview.

Summary

Types

Event name. Convention: <entity>.<past-tense-verb> (lowercase, dot-separated).

JSON-serializable payload. Atom keys are converted to strings on insert.

Functions

Returns a Supervisor child_spec for Outbox.Application.

Publish a domain event.

Types

name()

@type name() :: String.t()

Event name. Convention: <entity>.<past-tense-verb> (lowercase, dot-separated).

payload()

@type payload() :: map()

JSON-serializable payload. Atom keys are converted to strings on insert.

Functions

child_spec(opts \\ [])

@spec child_spec(keyword()) :: Supervisor.child_spec()

Returns a Supervisor child_spec for Outbox.Application.

publish(name, payload)

@spec publish(name(), payload()) ::
  {:ok, Outbox.OutboxEvent.t()} | {:error, Ecto.Changeset.t()}

Publish a domain event.

This function performs a single Repo.insert/1 and does not open its own transaction. The caller is responsible for wrapping the domain write and the publish/2 call in Repo.transaction/1 if atomicity-with-the-domain-write is required (it almost always is).

Atom keys in the payload are converted to strings so subscribers always see string keys (consistent with what JSONB round-trips produce).

Examples

Repo.transaction(fn ->
  {:ok, product} = Repo.insert(changeset)
  {:ok, _event} = Outbox.publish("product.created", %{"id" => product.id})
  product
end)