Skip to content

Architecture

1. System Context (C4 L1)

2. Container View (C4 L2)

3. Component View (C4 L3) — Internal Layering

LayerResponsibility
RoutesHTTP surface in RestPaths (/socket/websocket/clients, /notifications) + WS /stream
ControllersAuth + permission gate + recipient scoping (userId from JWT)
ServicesWS messaging façade, notification CRUD, Kafka message handling
Repositories@nx/core re-exports; PolicyDefinitionRepository resolves recipients
ComponentsCross-cutting: WebSocket server, Redis, ECDH, Kafka consumer

4. State Machines Index

Signal has no long-lived stateful domain entity. The closest are the ephemeral WebSocket connection lifecycle and the (binary) notification read state.

EntityStatesDiagram
WebSocket connectionCONNECTING, AUTHENTICATED, ENCRYPTED, CLOSED→ jump
ActivityNotification.isReadUNREAD, READ→ jump

WebSocket Connection

FromEventToGuards
CONNECTINGauthenticateAUTHENTICATEDtype=Bearer + valid JWT via JWKSVerifierTokenService
AUTHENTICATEDhandshakeENCRYPTEDclientPublicKey present → derive + store AES key
ENCRYPTEDdisconnectCLOSEDAES key deleted from _clientAesKeys map

Notification Read State

FromEventToGuards
UNREADmark-readREADrecipientId must match JWT subject; sets isRead=true, readAt=now()

5. Runtime Scenarios

5.1 Encrypted WebSocket Connect

StepDetail
2Payload field is clientPublicKey; requireEncryption: true rejects connections without it
5Per-client ephemeral key pair → forward secrecy; key lives only in memory

5.2 Activity-Notification Pipeline

StepDetail
3orgfindUserIdsInOrganizer; merchantfindUserIdsInMerchant; users → explicit recipientIds; fallback to [actorId]
8Commit happens per-message after the handler returns; on-message error is logged, not retried

5.3 Cross-Service Push

StepDetail
1Producers host no WS server — they only publish; signal is the single edge
3sendToClient delivers locally if present, else re-publishes to the owning instance

6. Crosscutting Concerns

ConcernHow this service handles it
AuthN (REST)JWT + Basic strategies, verified via remote JWKS (VerifierApplication)
AuthN (WS)type=Bearer token verified by JWKSVerifierTokenService in the authenticate handler
AuthZWebSocketClient.* Casbin permissions on client-management routes; /notifications is recipient-scoped (no explicit permission, scoped by JWT subject)
EncryptionECDH P-256 + HKDF-SHA-256 + AES-256-GCM, per-client key in _clientAesKeys map
i18nNotification content built server-side (currently Vietnamese template for PAYMENT_SUCCESS)
LoggingStructured key-value (key: %s); Kafka consumer logs topic/partition/offset
IdempotencyNone at consumer — at-least-once; re-delivery would create duplicate rows (see API Events)
Room ACLvalidateRoom is currently a passthrough (accepts all rooms); merchant-scoped ACL is a documented TODO
IDsSnowflake via IdGenerator, worker 9

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