Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

04 — Information Architecture (WS / Realtime Module)

The transport contract of the realtime layer: namespaces, rooms, envelope, naming. Mirrors 00-shared/05 at the wire level; module facts here are all source-grounded.


1. Transport

AspectValueSource
ProtocolSocket.io over WebSocket (engine.io fallback transports)ws.gateway.ts:10-11
Namespace/wsws.gateway.ts:21
CORSorigin: '*', credentials: truews.gateway.ts:22
Payload wire formatJSON (socket.io default)
Heartbeatsocket.io defaults (pingInterval 25 s / pingTimeout 20 s) — not tuned in sourcegap → 14_QA_Checklist.md

2. Identity model

A connected socket carries (source ws.gateway.ts:14-18,46-48):

AuthenticatedSocket { userId, tenantId, roles[] }   ← from verified JWT (payload.sub, tenantId, roles)
  • Tenant scope is fixed at connect time by the JWT (ws.gateway.ts:47); it is never supplied by the client on the wire. This is the security boundary of the whole module.
  • roles[] are carried but unused in routing today (gap; see 12).

3. Room model

RoomCreatedWho joinsPurposeSource
tenant:{tenantId}implicit, at connectevery socket of the tenanttenant-wide fan-out of domain eventsws.gateway.ts:50
arbitrary room stringon subscribeany authenticated socketentity-level delivery (e.g. class:10-A, user:{id})ws.gateway.ts:63-68

Rules:

  • subscribe/unsubscribe accept any string (type-checked only: typeof room !== 'string' returns silently — ws.gateway.ts:65,71). No allow-list, no RBAC check → gap (see 12).
  • Rooms are per-connection and die with the socket; clients must re-subscribe after every reconnect (13_State_Management.md).
  • There is no per-user room in source; user-scoped delivery today = tenant room + client-side filter, or an explicit subscribe to a convention like user:{id} (planned convention, not enforced).

4. Event taxonomy

Server→client events are domain event types relayed by the bridge, wrapped in an envelope (ws-bridge.service.ts:17-21):

{
  "eventType": "notification.created",
  "occurredAt": "2026-08-03T09:00:00.000Z",
  "payload": { }
}
  • Names use dot notation — blueprint convention: attendance.updated, payment.completed, notification.created, homework.published (API_STANDARDS.md:642-658).
  • The underlying DomainEvent also carries actorId and correlationId (events/domain-event.interface.ts:1-8) — not forwarded by the bridge today.
  • Client→server events in source: exactly subscribe and unsubscribe (ws.gateway.ts:63-74).

5. Consumer mapping (web UI)

Domain event family (examples from blueprint §28)UI consumerStatus
notification.createdbell badge, toastconvention — modules to emit (planned)
payment.completedfee dashboard tilesplanned
attendance.updatedattendance lists/dashboardplanned
homework.publishedhomework listsplanned
communication.*message threadsplanned (IMPLEMENTATION_PLAN.md:119)
transport.*live trackingplanned (IMPLEMENTATION_PLAN.md:229)
job progressasync-op status (API_STANDARDS.md:579)planned

6. Naming conventions for the web client

  • Channel constants in one file (realtime_channels.dart) — mirror server room strings.
  • Event constants mirror server eventType strings 1:1 (no client-side renaming).
  • Envelope is the only accepted server→client shape; anything else is a protocol violation.