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:
- the write guard,
ServiceLifecycle::before_*,WriteHooks::before,- the repository write,
ServiceLifecycle::after_*,WriteHooks::after,- 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::stageride 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§
- NoWrite
Hooks - The hooks of a service that has none: every write runs in its transaction and nothing more.
- Notification
Requested - A request to tell someone something, staged by a hook trigger’s
send_emailornotify. 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. - Outbox
Record - A serialized domain event staged in the outbox and carried to consumers.
- Scheduled
Trigger - A scheduled entry of a model’s hook triggers (
schedule:on a trigger): what a composing service’s scheduler runs atschedule, with the generatedrun_<model>_scheduled. - Write
Ctx - What a write hook knows about the write it runs in.
Enums§
- Change
- One row change, as a write hook sees it.
- Change
Kind - 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§
- Declared
Event - A fact the module declares it publishes (
events:in a schema file), as the generated typed struct of its payload. - Write
Hooks - Code that runs inside a write’s transaction, before and after the repository write.
Functions§
- current_
correlation_ id - The correlation id
with_correlation_idbound 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: theTRIGGER_MAX_DEPTHth level inside another is refused with the violationTRIGGER_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
Noneand a required one is an error — the generator only lists fields the aggregate has. - with_
correlation_ id - Run
fwithidas 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 thex-correlation-idrequest header (seecorrelation_middleware).