Skip to content

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 the handler that is live after a morph:

state Pending of record PendingData is {
  initial handler PendingHandler is { ??? }
  handler AuditHandler is { ??? }
}

Without the marker, the first handler declared wins — 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.

An entity-scope handler is defaulted to initial only when the entity has exactly one state. With multiple states, entity-scope handlers are common parts merged into each state's handler set, so none of them is the entity's initial handler.

Declaring more than one initial handler in a state — or more than one at entity scope when the entity has at most one state — is an Error.

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

  • On Clauses - a specification of how to handle an event