Events
An event is a message that says something happened. It could be a technical task finishing, such as DatabaseBackedUp, or a change in your business, such as CreditCardCharged, UserRegistered or PackageShipped. This page covers declaring, publishing and handling events.
TIP
Name events in the past tense, since each one is a fact that has already happened. The history of your application can then be told as a sequence of events.
Declaring an event
An event is a class that extends Event:
import { Event } from '@node-ts/bus-messages'
export class CreditCardCharged extends Event {
/**
* A unique name that identifies the message, in a namespace style such as
* organisation/domain/event-name. The bus routes messages by this name.
*/
static NAME = 'my-app/accounts/credit-card-charged'
$name = CreditCardCharged.NAME
/**
* The contract version of this message. Increment it when the message's
* fields change in a way that isn't backwards compatible.
*/
$version = 1
/**
* A credit card was successfully charged
* @param creditCardToken Identifies the card that was charged
* @param amount The amount, in USD, that the card was charged for
* @param chargedAt When the card was charged
*/
constructor(
readonly creditCardToken: string,
readonly amount: number,
readonly chargedAt: Date
) {
super()
}
}Publishing an event
An event can have any number of subscribers, including none. Each subscriber gets its own copy, and usually does some next piece of work because of it. Publish an event with publish(), optionally with attributes:
const creditCardCharged = new CreditCardCharged('tok_visa', 1200, new Date())
// Publish an event. Every subscriber receives a copy
await bus.publish(creditCardCharged)
// Publish an event with attributes
await bus.publish(creditCardCharged, {
correlationId: 'cd091b26-f0e6-43fb-9962-c06786948e26'
})Inside a handler, publish through its context with ctx.publish(), so the event is only published if the handler succeeds.
Handling an event
Events are handled by a function declared with handlerFor, or a class that implements Handler. When the handler resolves, the event is deleted from the queue.
export const creditCardChargedHandler = handlerFor(
CreditCardCharged,
async event => receiptService.record(event.creditCardToken, event.amount)
)export class CreditCardChargedHandler implements Handler<CreditCardCharged> {
// A getter, so the bus can read it without constructing the handler
get messageType() {
return CreditCardCharged
}
async handle(event: CreditCardCharged) {
await receiptService.record(event.creditCardToken, event.amount)
}
}Register the handlers with the bus configuration, then start the bus to begin handling messages:
const subscriber = Bus.configure()
.withMessageTypes(messageTypes)
.withHandler(creditCardChargedHandler) // Function handler
.withHandler(CreditCardChargedHandler) // Class handler
.build()
await subscriber.initialize()
// Start the bus to begin handling messages
await subscriber.start()A class handler is constructed for each message. Without a container its constructor can't take arguments.
See also
- Commands
- Handling messages, for testing handlers
handlerForandHandlerin the API reference