Handler
A handler is a very important definition in RIDDL because it is RIDDL's way of expressing both business logic and relationships between the various components in a model. Handlers do that by specifying what should be done whenever a message of a particular type is received by its parent definition. Handlers are composed as a set of on clauses that connect a message with a set of statements. The statements provide the business logic that should be executed upon receipt of the message. Because that business logic can send and publish further messages to other components, a relationship can be inferred between the component receiving the message and the component that contains the handler.
There are several kinds of handlers depending on the handler's containing definition, as shown in this table:
| Occurs In | Handler Focus |
|---|---|
| Adaptor | Translate messages between contexts |
| Application | Handling the messages received from the user |
| Context | Implements API of the stateless context |
| Entity | Handler to use on new entity before any morph action |
| Processor | Provide ETL logic for moving inputs to outputs |
| Projector | Handle events, building the projection |
| Repository | Handle updates and queries on the repository |
| State | Handle messages while entity is in that state |
The types of definition in which Handlers occur are known as the "active" definitions. More details are provided in the sections below.
Adaptor Handlers¶
Adaptor handlers provide the translation of messages from one context to another.
Application Handlers¶
Application handlers process the events generated by the Application's user to invoke business logic that typically results in sending further messages to the "back end" (typically a gateway context). This allows the application to be connected to the rest of the model.
Context Handlers¶
Context handlers imply a stateless API for the context, perhaps encapsulating the other things defined within the context.
Entity Handlers¶
Entity handlers specify the default or "catch all" handler for an entity.
When the entity is new, or is not in a specific state, this default handler
is used to process each message. If the message is not processed by the handler,
then the entity's processing for that message is null (message ignored).
When an entity is in a specified state, it processes messages defined by
the handler within that state (see below).
Projector Handler¶
Projector handlers process events to build and maintain read-side views of data. They receive events from the event stream and update the projection's state accordingly.
Projectors are event-only
As of RIDDL 2.0 a projector handler may contain on event and on result
clauses only. on command, on query and on record are rejected at
parse time. Queries against the projected data are served by the
repository the projector updates, not by the projector
itself.
The initial Marker¶
An optional initial keyword marks which handler is live when nothing has
selected one yet. It means different — though related — things at the two
placements a handler can occupy.
On a State Handler¶
initial marks the handler that becomes active when the entity transitions
into that state, whether by morph
or at creation:
state Pending of record PendingData is {
initial handler PendingHandler is { ??? }
handler AuditHandler is { ??? }
}
Without it, a state with several handlers has no defined starting behavior and
the entity would have to become explicitly before it could react — so the
first handler declared wins by default, which means reordering handlers silently
changes behavior. Marking it explicitly makes the model safe to reorder, and is
fully backward compatible with models that do not.
On an Entity-Scope Handler¶
initial marks the handler used as the initial handler for every state that
does not define one of its own — a default across the whole state machine
rather than a per-state choice:
entity Order is {
initial handler Common is { ??? } // initial for Shipped, and any state
// that declares no initial of its own
initial state Pending of record PendingData is {
initial handler PendingHandler is { ??? }
}
state Shipped of record ShippedData
}
Where no marker appears, an entity-scope handler is defaulted to initial only
when the entity has exactly one state.
Errors¶
Declaring more than one initial handler in a state, or more than one at entity
scope, is an Error — at entity scope regardless of how many states the
entity has. Verified against 2.0.0-rc.9-54.
It is not a printer bug
initial handler is real syntax, so riddlc prettify is correct to emit
it. It went undocumented long enough that a consumer reported the output as
a printer defect on the grounds that handlers have no such modifier. They
do.
Options¶
timeout (1 argument), retry (1–2 arguments), idempotent, cacheable and
rate-limit (2 arguments) may be set on a handler's metadata.
State Handler¶
State handlers process messages while an entity is in that
specific state. They are defined inside a state's body,
enabling each state to respond to messages differently—the
foundation for modeling state machines in RIDDL. When an
entity transitions to a new state via a morph statement,
the new state's handlers become active.
state ActiveOrder of record ActiveOrderData is {
handler ActiveOrderHandler is {
on ship: command ShipOrder {
morph entity Order to state Order.ShippedOrder
with record ShippedOrderData(ship.trackingNumber)
}
}
}
Note that a state is typed by a record, and that a morph carries a
record too — not the command that triggered it. A command is a request; the
record is the new state's data.
Occurs In¶
Contains¶
flowchart TD
Handler(["Handler"]) --> OnClause["On Clause"]
Handler --> Comment
OnClause --> Statement