Postboy Help

Event Patterns

Recurring ways to put events to work: broadcasting from a service to many consumers, reacting to one event in several components, fire-and-forget notifications, and latest-value or state broadcasting with replay and behavior subjects. Every pattern uses only fire, sub, and once on PostboyGenericMessage — see Events for the basics.

Broadcasting from a service

A data-producing service registers the event once and fires on incoming data. Consumers depend only on the message class and the bus — never on the producing service.

import {ConnectMessage, PostboyGenericMessage, PostboyService} from '@artstesh/postboy'; import {Subject} from 'rxjs'; export class CallStartedEvent extends PostboyGenericMessage { static readonly ID = 'call.started'; constructor(public readonly caller: string) { super(); } } export class CallWebSocketAdapter { constructor(private postboy: PostboyService) { this.postboy.exec(new ConnectMessage(CallStartedEvent, new Subject<CallStartedEvent>())); } onIncomingCall(caller: string): void { this.postboy.fire(new CallStartedEvent(caller)); } }

Consumers subscribe without importing the adapter:

postboy.sub(CallStartedEvent).subscribe((msg) => showBanner(msg.caller)); postboy.sub(CallStartedEvent).subscribe((msg) => logCall(msg.caller));

Subscribing in multiple components

One fire updates every part of the UI that cares. Each component subscribes independently; none knows about the others.

// header badge postboy.sub(CallStartedEvent).subscribe(() => (this.hasActiveCall = true)); // customer profile widget postboy.sub(CallStartedEvent).subscribe((msg) => (this.callerName = msg.caller)); // call status panel postboy.sub(CallStartedEvent).subscribe(() => this.startTimer());

Adding a fourth subscriber is a new subscription and nothing else — no @Input chains, no shared state service.

Fire-and-forget

Use one event as the app-wide channel for transient notifications. The publisher does not wait and cannot know who reacted.

export class ToastNotificationEvent extends PostboyGenericMessage { static readonly ID = 'ui.toast'; constructor(public readonly text: string, public readonly severity: 'info' | 'error') { super(); } } // anywhere in the app postboy.fire(new ToastNotificationEvent('Item added to cart', 'info')); // the only place that renders toasts postboy.sub(ToastNotificationEvent).subscribe((msg) => renderToast(msg.text, msg.severity));

A registered event with zero subscribers is discarded without an error, so optional events (telemetry, debug) need no guards.

Latest-value and state broadcasting

When late subscribers must catch up, register with recordReplay:

export class QuoteUpdatedEvent extends PostboyGenericMessage { static readonly ID = 'trading.quote.updated'; constructor(public readonly symbol: string, public readonly price: number) { super(); } } protected _up(): void { this.recordReplay(QuoteUpdatedEvent); // new subscribers get the last quote }

When the event models state, register with recordBehavior; the initial value is a message instance:

export class SessionStateEvent extends PostboyGenericMessage { static readonly ID = 'app.session.state'; constructor(public readonly online: boolean) { super(); } } protected _up(): void { this.recordBehavior(SessionStateEvent, new SessionStateEvent(false)); }

Every subscriber — early or late — sees the current state immediately.

Do and don't

Do

Don't

Register once in a registrator

Re-register the same type ad hoc (silent overwrite)

Publish with fire

Call .next() on the observable returned by sub

Use recordReplay/recordBehavior for latest-value or state

Hand-roll "current value" caches next to the bus

Unsubscribe on component teardown

Leak subscriptions in long-lived apps

Keep events result-free

Smuggle responses into events; use callbacks instead

Next steps

07 September 2026