Stone.jsDocs
Paradigm

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

terminalbash
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.

app/Application.tsdeclarativeimperative
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' }]
}))
app/OrderService.tsts
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.

app/Application.tsts
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 {}
app/Orders.tsts
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.

app/Application.tsts
@BusListener({ extractor: (event) => {
  const raw = event.get('metadata', {})
  return { key: raw.type, payload: raw.data }
}})

#Drivers

driverTypeDescription
localbuilt-inThe app EventEmitter. In-process delivery, zero-config.
memorybuilt-inRecords emitted messages for tests and local dev.
eventbridgeAWS SDKPutEvents (Source / DetailType / Detail); rules fan out to consumer functions.

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)