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):
- Cadence model lifecycle events —
ModelDispatcher/ModelEventaround persistence - Queues — background jobs (
Bus/ShouldQueue); not wired intoEventDispatchertoday - Broadcasting — push events to clients (
Broadcast.event) - Container — resolving the shared dispatcher
- Application & providers — where to register
EventServiceProvider
What the bus does
Section titled “What the bus does”- You register one or more listeners against an event class (
Class<T>). - Something in your app calls
dispatch(eventInstance). - The dispatcher looks up listeners for that instance’s exact runtime class.
- Each matching listener’s
handleruns, in the order they were registered. - When there are no listeners for that class,
dispatchis 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).
Quick start
Section titled “Quick start”import org.konaframework.events.EventDispatcherimport 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"))import org.konaframework.events.EventDispatcher;import org.konaframework.events.EventListener;
public final class OrderShipped { public final String id; public OrderShipped(String id) { this.id = id; }}
EventDispatcher bus = new EventDispatcher();
bus.listen(OrderShipped.class, event -> System.out.println("shipped " + event.id));
bus.listen(OrderShipped.class, (EventListener<OrderShipped>) event -> System.out.println("also " + event.id));
bus.dispatch(new OrderShipped("42"));Wire the dispatcher into your app
Section titled “Wire the dispatcher into your app”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)import org.konaframework.events.EventServiceProvider;
Application app = Kona.create(args) .providers( new HttpServiceProvider(), new EventServiceProvider(), new AppServiceProvider() // … ) .boot();
EventDispatcher events = app.resolve(EventDispatcher.class);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(/* … */) } }}public final class AppServiceProvider implements ServiceProvider { @Override public void register(Application app) { /* bindings only */ }
@Override public void boot(Application app) { EventDispatcher events = app.resolve(EventDispatcher.class); events.listen(OrderShipped.class, event -> { app.resolve(Mailer.class).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.
Maven / Gradle
Section titled “Maven / Gradle”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 |
When to use application events
Section titled “When to use application events”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 |
Sync execution model
Section titled “Sync execution model”Every dispatch call:
- Runs listeners inline before
dispatchreturns. - 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.
Not available today
Section titled “Not available today”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.