Skip to content
Kona

Events

An event is a typed message your application publishes when something meaningful happens — an order shipped, a user registered, a payment failed. A listener is code that runs when that message is published. Kona’s event bus (EventDispatcher) is a synchronous, in-process registry: listeners for a given event class run immediately, on the same thread, in registration order.

This section covers the full shipped surface in org.konaframework.events (kona-events): defining events, registering listeners, dispatching, clearing listeners, container wiring via EventServiceProvider, and testing with FakeEvent. Features that are not in Kona today are listed under Not available today.

Guide Use when
Dispatching & listeners Full API, registration patterns, order, forget/flush, pitfalls
Testing FakeEvent, recording dispatches, asserting types

Related (different systems):

  1. You register one or more listeners against an event class (Class<T>).
  2. Something in your app calls dispatch(eventInstance).
  3. The dispatcher looks up listeners for that instance’s exact runtime class.
  4. Each matching listener’s handle runs, in the order they were registered.
  5. When there are no listeners for that class, dispatch is a no-op (no error).

There is no queue hop, no separate worker process, and no automatic discovery of listener classes. If a listener must do heavy work later, dispatch a queue job from inside the listener (or from the same call site).

import org.konaframework.events.EventDispatcher
import org.konaframework.events.EventListener
data class OrderShipped(val id: String)
val bus = EventDispatcher()
bus.listen(OrderShipped::class.java) { event ->
println("shipped ${event.id}")
}
// Equivalent SAM form:
bus.listen(OrderShipped::class.java, EventListener { event ->
println("also ${event.id}")
})
// Reified helper (Kotlin only):
bus.listen<OrderShipped> { event ->
println("reified ${event.id}")
}
bus.dispatch(OrderShipped("42"))

EventServiceProvider binds a single EventDispatcher as a container singleton. Default kona new scaffolds do not register this provider automatically — add it next to your other providers when you want a shared bus.

import org.konaframework.events.EventServiceProvider
val app = Kona.create(args)
.providers(
HttpServiceProvider(),
EventServiceProvider(),
AppServiceProvider(),
// …
)
.boot()
val events = app.resolve(EventDispatcher::class.java)

Register listeners in a provider’s boot method (after every provider has finished register), or in a dedicated events provider you own:

class AppServiceProvider : ServiceProvider {
override fun register(app: Application) { /* bindings only */ }
override fun boot(app: Application) {
val events = app.resolve(EventDispatcher::class.java)
events.listen(OrderShipped::class.java) { event ->
app.resolve(Mailer::class.java).send(/* … */)
}
}
}

You may also construct EventDispatcher() yourself in tests or small tools without registering the provider. Prefer the container singleton in long-running apps so every call site shares the same listener set.

Artifact: org.konaframework:kona-events (included in the Kona BOM). Package: org.konaframework.events.

Type Role
EventDispatcher The bus — listen, dispatch, forget, flush, inspection
EventListener<T> SAM interface (void handle(T event)) for Kotlin lambdas and Java method refs
EventServiceProvider Registers the singleton dispatcher on Application

Use EventDispatcher when:

  • Multiple independent reactions should follow one domain fact (mail, audit log, cache bust) without coupling those reactions into the publisher.
  • You want typed payloads (data classes / plain Java records or beans) instead of string topic names.
  • The work is fast enough to run on the request or command thread.

Prefer other tools when:

Need Prefer
Heavy / slow / unreliable I/O Queue jobs via Bus
Cadence insert/update/delete hooks Model lifecycle events
Push to browsers / websockets Broadcasting
Interactive console exploration Resolve EventDispatcher from a booted app in steam or a grind command

Every dispatch call:

  • Runs listeners inline before dispatch returns.
  • Propagates exceptions from a listener to the caller (later listeners for that dispatch do not run once an earlier one throws).
  • Does not consult queue connections, ShouldQueue, or after-commit hooks — those are not available on this bus today.

There is no separate “sync vs queue” mode on EventDispatcher. Background work belongs on Queues.

Documented so you do not expect APIs that are not in source:

Capability Status
Global Event facade / event() helper Not shipped — resolve EventDispatcher from the container (or construct one)
Attribute / annotation discovery of listeners Not shipped — register with listen explicitly
Event subscriber classes (subscribe maps many events) Not shipped
Wildcard listeners / string event names on this bus Not shipped (use typed classes; broadcasting uses names separately)
Stop propagation / false return to halt the chain Not shipped — all registered listeners run unless one throws
Queued listeners / shouldQueue / listener queues Not shipped — dispatch a job from a listener if needed
After-commit dispatch Not shipped
Inheritance matching (listen on interface/superclass) Not shipped — exact runtime class only
Automatic registration in default scaffolds Not shipped — add EventServiceProvider yourself

See Dispatching & listeners for the complete API tables, patterns, and edge cases, and Testing for FakeEvent.