Broadcasting Events from Services
Services that produce data — WebSocket adapters, polling timers, background workers — are natural publishers of PostboyGenericMessage events.
They simply call fire(...) with a message instance, completely unaware of which parts of the UI (or other services) are listening.
1. WebSocket adapter publishing stock updates
The StockWebSocketAdapter receives real‑time stock data and publishes a StockUpdatedEvent whenever a product’s availability changes.
Registration happens once in the constructor.
Publishing happens inside the WebSocket message handler.
import {AppPostboyService} from '@shared/services/app-postboy.service';
import {ConnectMessage} from '@artstesh/postboy';
import {Subject} from 'rxjs';
import {StockUpdatedEvent} from './messages/events/stock-updated.event';
export class StockWebSocketAdapter {
constructor(private postboy: AppPostboyService) {
// Register the event type once
this.postboy.exec(new ConnectMessage(StockUpdatedEvent, new Subject<StockUpdatedEvent>()));
}
onStockMessage(raw: { productId: string; status: string }): void {
// Broadcast to every interested subscriber
this.postboy.fire(new StockUpdatedEvent(raw.productId, raw.status));
}
}
2. Customer queue adapter broadcasting queue changes
A different service, CustomerQueueWebSocketAdapter, publishes CustomerQueueEvent when the number of waiting customers changes.
import {AppPostboyService} from '@shared/services/app-postboy.service';
import {ConnectMessage} from '@artstesh/postboy';
import {Subject} from 'rxjs';
import {CustomerQueueEvent} from './messages/events/customer-queue.event';
export class CustomerQueueWebSocketAdapter {
constructor(private postboy: AppPostboyService) {
this.postboy.exec(new ConnectMessage(CustomerQueueEvent, new Subject<CustomerQueueEvent>()));
}
onQueueUpdate(count: number): void {
this.postboy.fire(new CustomerQueueEvent(count));
}
}
3. Subscribing in components (the other side)
Components interested in these events subscribe without importing the adapter service.
They only depend on the message class and the bus.
Header component — listens to both events and displays counts.
import {AppPostboyService} from '@shared/services/app-postboy.service';
import {StockUpdatedEvent} from './messages/events/stock-updated.event';
import {CustomerQueueEvent} from './messages/events/customer-queue.event';
export class OperatorHeaderComponent {
queueCount = 0;
availableItems = 0;
constructor(private postboy: AppPostboyService) {
this.postboy.sub(CustomerQueueEvent).subscribe((msg) => {
this.queueCount = msg.count;
});
this.postboy.sub(StockUpdatedEvent).subscribe((msg) => {
// Some logic to calculate available items from update events
if (msg.newStatus === 'Available') this.availableItems++;
else this.availableItems--;
});
}
}
Toast notification service — shows a popup for each stock change.
export class NotificationService {
constructor(private postboy: AppPostboyService) {
this.postboy.sub(StockUpdatedEvent).subscribe((msg) => {
this.showToast(`Product ${msg.productId} is now ${msg.newStatus}`);
});
}
}
Key points
A service becomes a publisher by calling fire(...) — no knowledge of subscribers is needed.
The same event type can be consumed by multiple components or services in completely different ways.
Adding a new listener (e.g., a logging service) requires no change in the publishing service.
All communication remains typed and explicit via the message class.
03 мая 2026