Wraps the handling of an event by a reactor or reducer, for cross-cutting concerns: tracing, metrics, logging, impersonating the identity that caused the event, tenant scoping, transactional wrapping. Mirrors the .NET client's reactor middleware.
A middleware receives the event, its Chronicle.Events.EventContext and next, a function
that continues down the pipeline (finally reaching handle/2 or reduce/3). It returns what
the handler would return - call next and return its result, or return a result of its own to
short-circuit:
defmodule MyApp.Middleware.Timing do
@behaviour Chronicle.Middleware
require Logger
@impl true
def call(event, context, next) do
{micros, result} = :timer.tc(fn -> next.(event, context) end)
Logger.info("handled #{inspect(event.__struct__)} in #{micros}us")
result
end
endMiddleware runs in the order listed, the first being the outermost. Configure it for every reactor and reducer of the application:
config :cratis_chronicle, middleware: [MyApp.Middleware.Timing]or for one module:
use Chronicle.Reactors.Reactor, middleware: [MyApp.Middleware.Impersonate]
use Chronicle.Reducers.Reducer, model: MyApp.Account, middleware: [MyApp.Middleware.Timing]Global middleware is the outer layer, module middleware the inner. An exception raised by a middleware or the handler is handled like one raised by the handler: the partition fails.
For a reducer, next returns the reduced model, so a middleware that short-circuits returns
a model (typically the one it can see through the context's current state - or simply calls
next). Prefer calling next in reducer middleware.
Summary
Functions
The middleware that applies to module: global middleware, then the module's own.
Runs final (a two-arity function of event and context) through middleware, outermost first.
Callbacks
Functions
The middleware that applies to module: global middleware, then the module's own.
Runs final (a two-arity function of event and context) through middleware, outermost first.