Skip to main content

Module write_hooks

Module write_hooks 

Source
Expand description

Write hooks: code that runs inside a write’s own transaction, with the change in hand.

GenericCrudService runs every write of an entity (create, update, patch, delete, restore, hard delete, their bulk forms, and a state-machine transition) inside one database transaction (backbone_orm::in_write_tx), in this order:

  1. the write guard,
  2. ServiceLifecycle::before_*,
  3. WriteHooks::before,
  4. the repository write,
  5. ServiceLifecycle::after_*,
  6. WriteHooks::after,
  7. commit.

A failure at any step rolls the whole write back: the row, whatever the hooks wrote and every event they staged. After the commit the service’s [CrudEventPublisher] keeps its best-effort in-process role, and nothing else.

The schema generator emits a WriteHooks implementation per model from its declared events (on: in a hook file’s events:), which stages them with WriteCtx::stage. Hand code can implement the trait too, and compose it with with_write_hooks.

§What a hook may rely on

  • Statements it runs through the ORM’s scoped helpers, the repository, or WriteCtx::stage ride the write’s transaction and see its uncommitted rows.
  • A connection a hook opens itself (a bare pool.begin()) is another transaction: it cannot see the row being written, and a statement of it that waits for a lock the write holds waits for a commit that is waiting for the hook.
  • A hook that holds the transaction open for a slow call (HTTP, a queue) holds a database connection and the row locks for as long. Stage an event and let the relay do the slow part.

Structs§

NoWriteHooks
The hooks of a service that has none: every write runs in its transaction and nothing more.
NotificationRequested
A request to tell someone something, staged by a hook trigger’s send_email or notify. An effect outside the database is never performed inside the write’s transaction: the request is an outbox row (NotificationRequested) that commits with the write, and a consumer (a mail or notification module’s relay subscriber) performs it, at least once and idempotently.
OutboxRecord
A serialized domain event staged in the outbox and carried to consumers.
ScheduledTrigger
A scheduled entry of a model’s hook triggers (schedule: on a trigger): what a composing service’s scheduler runs at schedule, with the generated run_<model>_scheduled.
WriteCtx
What a write hook knows about the write it runs in.

Enums§

Change
One row change, as a write hook sees it.
ChangeKind
The kind of a Change, without its rows.

Constants§

TRIGGER_DEPTH_EXCEEDED
The violation code of a trigger run refused for nesting too deep.
TRIGGER_MAX_DEPTH
How many trigger runs may be nested: a trigger whose hand method writes another entity runs that entity’s triggers inside its own, and so on. The run past this depth is refused, so a cycle of triggers ends in an error instead of a stack.

Traits§

DeclaredEvent
A fact the module declares it publishes (events: in a schema file), as the generated typed struct of its payload.
WriteHooks
Code that runs inside a write’s transaction, before and after the repository write.

Functions§

current_correlation_id
The correlation id with_correlation_id bound for the current task.
current_trigger_depth
How many trigger runs enclose the current task.
in_trigger
Run f, the triggers of one hook, one level deeper than the run that encloses it. The generated write hooks wrap every trigger they run in this, so writes a trigger makes through other entities’ services (whose hooks wrap theirs the same way) count their nesting: the TRIGGER_MAX_DEPTHth level inside another is refused with the violation TRIGGER_DEPTH_EXCEEDED, which rolls the whole write back.
migrate_outbox
Create the outbox and inbox tables of schema (idempotent), where the events its hooks stage are written. The composing service runs it at startup, with a role that may create tables; the generated module offers it for each schema it stages in (declared::migrate_outbox).
payload_from_row
The payload of a declared event, read out of the row the write stored: the fields listed, by their serialized names, deserialized into the event’s struct. A listed field the row lacks is left out, so an optional one reads as None and a required one is an error — the generator only lists fields the aggregate has.
with_correlation_id
Run f with id as the correlation id of every write it makes: the id the events staged by those writes carry, so one request’s rows, events and logs share it. The generic write routes do this from the x-correlation-id request header (see correlation_middleware).