BaseSocketEventService
Overview
BaseSocketEventService is the shared base class for WebSocket event services across all BANA backend packages. It extends the IGNIS BaseService and provides a uniform API for broadcasting messages and sending to specific rooms via the IGNIS WebSocketEmitter.
Each downstream package creates its own concrete subclass (e.g., SaleSocketEventService, IdentitySocketEventService) that adds domain-specific notification methods.
| Property | Value |
|---|---|
| Source | packages/core/src/base/service/base-socket-event.service.ts |
| Extends | BaseService (from @venizia/ignis) |
| Dependencies | BaseApplication, WebSocketEmitter |
Class Definition
import { BaseApplication, BaseService, WebSocketDefaults, WebSocketEmitter } from '@venizia/ignis';
export class BaseSocketEventService extends BaseService {
private _emitter: WebSocketEmitter | null = null;
private _emitterBindingKey: string;
constructor(
protected application: BaseApplication,
opts: { scope: string; emitterBindingKey: string },
) {
super({ scope: opts.scope });
this._emitterBindingKey = opts.emitterBindingKey;
}
get emitter(): WebSocketEmitter { /* lazy lookup from DI container */ }
isReady(): boolean;
async broadcast<PayloadType>(opts: { topic: string; data: PayloadType }): Promise<void>;
async sendToRoom<PayloadType>(opts: { room: string; topic: string; data: PayloadType }): Promise<void>;
}Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
application | BaseApplication | The IGNIS application instance (injected via CoreBindings.APPLICATION_INSTANCE) |
opts.scope | string | Logger scope name, typically ClassName.name |
opts.emitterBindingKey | string | DI binding key where the WebSocketEmitter is registered |
Lazy Emitter Pattern
The WebSocketEmitter is not available at construction time because WebSocket components bind it during the binding() phase, which runs after service instantiation. The getter uses a lazy lookup pattern:
This pattern ensures the service works correctly regardless of DI binding order.
API
isReady()
Checks whether the WebSocketEmitter has been bound in the DI container. Use this before calling broadcast() or sendToRoom() to gracefully skip notifications when WebSocket infrastructure is not available.
isReady(): booleanReturns: true if the emitter is bound and retrievable, false otherwise.
broadcast()
Sends a message to all connected WebSocket clients by publishing to the default room (WebSocketDefaults.ROOM).
async broadcast<PayloadType = unknown>(opts: {
topic: string;
data: PayloadType;
}): Promise<void>| Parameter | Type | Description |
|---|---|---|
topic | string | The event name clients will receive |
data | PayloadType | The payload to send |
sendToRoom()
Sends a message to all WebSocket clients in a specific room.
async sendToRoom<PayloadType = unknown>(opts: {
room: string;
topic: string;
data: PayloadType;
}): Promise<void>| Parameter | Type | Description |
|---|---|---|
room | string | The target room name |
topic | string | The event name clients will receive |
data | PayloadType | The payload to send |
Downstream Usage
Six packages extend BaseSocketEventService:
| Package | Concrete Class | Binding Key |
|---|---|---|
@nx/sale | SaleSocketEventService | SaleWebSocketBindingKeys.WEBSOCKET_EMITTER |
@nx/identity | IdentitySocketEventService | Identity-specific key |
@nx/commerce | CommerceSocketEventService | Commerce-specific key |
@nx/inventory | InventorySocketEventService | Inventory-specific key |
@nx/finance | FinanceSocketEventService | Finance-specific key |
@nx/payment | PaymentSocketEventService | Payment-specific key |
Example: SaleSocketEventService
import { BaseSocketEventService, TSaleOrder } from '@nx/core';
import { SaleWebSocketRooms, SaleWebSocketTopics } from '@/common';
import { BaseApplication, CoreBindings, inject } from '@venizia/ignis';
import { SaleWebSocketBindingKeys } from './keys';
export class SaleSocketEventService extends BaseSocketEventService {
constructor(
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
application: BaseApplication,
) {
super(application, {
scope: SaleSocketEventService.name,
emitterBindingKey: SaleWebSocketBindingKeys.WEBSOCKET_EMITTER,
});
}
notifyOrderUpdate(opts: { order: TSaleOrder }): void {
const { order } = opts;
if (!this.isReady()) {
this.logger.warn('Socket event service not ready | SKIP notify order WS');
return;
}
const rooms = SaleWebSocketRooms.getSaleOrderRooms({
saleOrderId: order.id,
merchantId: order.merchantId,
});
Promise.allSettled(
rooms.map(room =>
this.sendToRoom({
room,
topic: SaleWebSocketTopics.ORDER,
data: order,
}),
),
);
}
}Registration Pattern
Each package registers its socket event service inside a WebSocketComponent:
export class ApplicationWebSocketComponent extends BaseComponent {
override async binding(): Promise<void> {
const redis = RedisConnectionFactory.create({ /* ... */ });
const emitter = new WebSocketEmitter({
identifier: 'my-ws-emitter',
redisConnection: redis,
});
this.application.bind({ key: MyBindingKeys.WEBSOCKET_EMITTER }).toValue(emitter);
this.application.service(MySocketEventService);
}
}Architecture Diagram
Related Resources
- IGNIS WebSocket Setup & Configuration -- WebSocket component, emitter, and Redis scaling
- IGNIS WebSocket API Reference -- WebSocketEmitter API and architecture
- IGNIS Services Reference -- BaseService class documentation
- RedisConnectionFactory -- Creating Redis connections for the emitter
- Signal Package -- Centralized WebSocket service (uses its own
SignalEventService)