Skip to main content

MediatorFactory

MediatorFactory.create is the single entry point for wiring everything together — registrars, pipeline behaviors, validators, and the notification strategy all go here.


Basic wiring

The simplest setup: one registrar, no pipeline.

val mediator = MediatorFactory.create(
registrars = listOf(AppRegistrar()),
)

Full wiring

A production setup with all extension points populated:

val mediator = MediatorFactory.create(
registrars = listOf(
UserRegistrar(),
OrderRegistrar(),
OrderNotificationRegistrar(),
),
pipelineBehaviors = listOf(
TraceIdBehavior(), // Stage.Pre — outermost
LoggingPipelineBehavior(), // Stage.Default, order=-100
RetryPipelineBehavior(maxRetries = 2), // Stage.Default, order=0
CircuitBreakerPipelineBehavior(), // Stage.Default, order=0
TimingPipelineBehavior { name, ms -> // Stage.Default, order=0
metrics.record(name, ms)
},
AuditBehavior(auditLog), // Stage.Post — innermost
),
notificationPublisher = ParallelNotificationPublisher(),
missingNotificationHandler = ThrowMissingNotificationHandler(),
)

Parameters

ParameterTypeDefaultDescription
registrarsList<MediatorRegistrar>emptyList()Contribute handlers to the registry at startup
pipelineBehaviorsList<PipelineBehavior>emptyList()Cross-cutting decorators; grouped by Stage then sorted by order within each stage
streamPipelineBehaviorsList<StreamPipelineBehavior>emptyList()Cross-cutting decorators for StreamRequest dispatches; sorted by order
notificationPublisherNotificationPublishStrategyParallelNotificationPublisher()Strategy for delivering notifications to handlers
verifyHandlersBooleantrueWhen true, logs a warning for every request type with no handler after all registrars have run
missingNotificationHandlerNotificationHandler<Notification>ThrowMissingNotificationHandler()Called when a notification is published with no registered handlers. Built-in: ThrowMissingNotificationHandler, SilentMissingNotificationHandler, or provide your own
missingRequestHandlerRequestHandler<Request<Any?>, Any?>ThrowMissingRequestHandler()Called when send() is called for a request type with no registered handler. Use SilentMissingRequestHandler to return a default instead of throwing.

Pipeline execution order

For every mediator.send(request) call, the execution flows like this:

Stage.Pre behaviors (ascending order, outermost)
└─ Stage.Default behaviors (ascending order)
└─ Stage.Post behaviors (ascending order, innermost)
└─ Handler
└─ Stage.Post behaviors (unwinding)
└─ Stage.Default behaviors (unwinding)
Stage.Pre behaviors (unwinding)

Stage always beats order: every Stage.Pre behavior wraps every Stage.Default behavior, regardless of their order values. order only controls sequencing within a stage.

ValidationBehavior runs at order = -50 in Stage.Default by default, so it executes before logging (-100?), caching, and retry behaviors at order = 0.


Notification publishers

PublisherBehaviour
ParallelNotificationPublisherAll handlers run concurrently (default)
SequentialNotificationPublisherHandlers run one-by-one; stops on first error
ContinueOnExceptionNotificationPublisherAll handlers run; errors collected into AggregateException
FireAndForgetNotificationPublisherReturns immediately; handlers run in the background

Verifier limitations

verifyHandlers = true enables startup verification, but it has known limitations:

warning

The verifier can only see what was registered — it cannot detect what was never registered.

A missing handler is invisible at startup. It only surfaces at runtime when send() or publish() is called for a type with no handler.

What the verifier can detectWhat it cannot detect
Duplicate request handler registrations (last one wins silently)A Request type that was never registered at all
Notification handlers registered for a type with an empty handler listA NotificationHandler whose notification is never published
Misconfigured registrars that run but register nothingUnsatisfied constructor dependencies inside a handler

Unsatisfied dependencies (e.g. a null Repository injected into a handler) are outside MediatorK's scope — that is your DI container's responsibility (Koin, Hilt, etc.).

Use MediatorTestUtils.assertAllHandlersRegistered(registrars) from the mediatork-test module to catch missing registrations in tests rather than at runtime.


Next

Kotlin JVM