Extensions
Event Bus
emit on one side, @OnBusEvent on the other. The in-process EventEmitter already handles local events; @stone-js/event-bushandles the cloud: publish a domain event to local and/or cloudtargets, and route incoming bus events to their handlers, on any simple cloud adapter. No dedicated adapter, no touching the core.
#Install
npm i @stone-js/event-bus
npm i @aws-sdk/client-eventbridge # only for the EventBridge driver#Emit
The principle
Publishing a domain fact is part of the domain. Whether a listener is in this process or another service reached through a cloud bus is context. The code emits once; the runtime resolves who hears it.
In Stone.js
One emit(name, payload, { targets }). local uses the app's EventEmitter (used, not modified); cloud publishes through the driver. eventBus is injected.
import { StoneApp } from '@stone-js/core'
import { EventBus } from '@stone-js/event-bus'
@EventBus({ driver: 'eventbridge', source: 'my.app' })
@StoneApp({ name: 'orders' })
export class Application {}import { defineConfig } from '@stone-js/core'
import { defineEventBus } from '@stone-js/event-bus'
export const AppConfig = defineConfig(defineEventBus({
default: 'cloud',
targets: ['local', 'cloud'],
connections: [{ name: 'cloud', driver: 'eventbridge', source: 'my.app' }]
}))export class OrderService {
constructor (private readonly eventBus) {}
async ship (order) {
// reaches in-process listeners AND the cloud bus, same code, monolith or distributed
await this.eventBus.emit('order.shipped', { id: order.id }, { targets: ['local', 'cloud'] })
}
}#Listen, on any cloud adapter
The listener side needs no adapter of its own: it is the light key-router from @stone-js/router. @BusListener, @BusHandler and @OnBusEvent are bus-flavoured aliases of @KeyRouting, @KeyHandler and @OnKey, so the router installs itself as the kernel event handler. Stack @BusListener() on a simple cloud adapter (here AWS Lambda); the adapter receives the event, the light router dispatches it.
import { AwsLambda } from '@stone-js/aws-lambda-adapter'
import { BusListener } from '@stone-js/event-bus'
@BusListener({ source: 'detail-type' }) // which incoming property carries the routing key
@AwsLambda()
@StoneApp({ name: 'consumer' })
export class Application {}import { BusHandler, OnBusEvent } from '@stone-js/event-bus'
@BusHandler()
export class Orders {
@OnBusEvent('order.shipped') async onShipped (payload) { /* … */ }
@OnBusEvent('order.cancelled') async onCancelled (payload) { /* … */ }
}#The routing key is configurable
The source names the incoming-event property holding the key. It defaults to detail-type (where the EventBridge driver puts the event name, so the pair round-trips), and is never hard-coded: set another property, or pass a full extractor for any bus shape.
@BusListener({ extractor: (event) => {
const raw = event.get('metadata', {})
return { key: raw.type, payload: raw.data }
}})#Drivers
| driver | Type | Description |
|---|---|---|
| local | built-in | The app EventEmitter. In-process delivery, zero-config. |
| memory | built-in | Records emitted messages for tests and local dev. |
| eventbridge | AWS SDK | PutEvents (Source / DetailType / Detail); rules fan out to consumer functions. |