Skip to content

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.

PropertyValue
Sourcepackages/core/src/base/service/base-socket-event.service.ts
ExtendsBaseService (from @venizia/ignis)
DependenciesBaseApplication, WebSocketEmitter

Class Definition

typescript
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

ParameterTypeDescription
applicationBaseApplicationThe IGNIS application instance (injected via CoreBindings.APPLICATION_INSTANCE)
opts.scopestringLogger scope name, typically ClassName.name
opts.emitterBindingKeystringDI 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.

typescript
isReady(): boolean

Returns: 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).

typescript
async broadcast<PayloadType = unknown>(opts: {
  topic: string;
  data: PayloadType;
}): Promise<void>
ParameterTypeDescription
topicstringThe event name clients will receive
dataPayloadTypeThe payload to send

sendToRoom()

Sends a message to all WebSocket clients in a specific room.

typescript
async sendToRoom<PayloadType = unknown>(opts: {
  room: string;
  topic: string;
  data: PayloadType;
}): Promise<void>
ParameterTypeDescription
roomstringThe target room name
topicstringThe event name clients will receive
dataPayloadTypeThe payload to send

Downstream Usage

Six packages extend BaseSocketEventService:

PackageConcrete ClassBinding Key
@nx/saleSaleSocketEventServiceSaleWebSocketBindingKeys.WEBSOCKET_EMITTER
@nx/identityIdentitySocketEventServiceIdentity-specific key
@nx/commerceCommerceSocketEventServiceCommerce-specific key
@nx/inventoryInventorySocketEventServiceInventory-specific key
@nx/financeFinanceSocketEventServiceFinance-specific key
@nx/paymentPaymentSocketEventServicePayment-specific key

Example: SaleSocketEventService

typescript
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:

typescript
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

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.