Level 1: System Context
Who and what interacts with Sportz? There is no authentication layer: the “operator” is anyone with API access. That is a deliberate scope decision for a demo platform; see Project Status.Level 2: Containers
What are the deployable units, and how do they talk? The single most important structural fact: REST and WebSocket share one HTTP server. Express handles normal requests; anupgrade listener intercepts /ws connections before they reach Express and hands them to the ws server. One process, one port, one deployable container.
Level 3: Components (Backend)
Inside thesportz container, what are the pieces?
The broadcast functions are created inside attachWebSocketServer() and injected onto app.locals, so the REST routes can trigger a WebSocket broadcast without importing the WS module directly. This is the seam that connects “data was written” to “tell the clients.”
Level 4: Code
The seam, in actual code. When a commentary event is posted:Scalability: what changes as load grows
The architecture above is correct for its current scale. Here is what breaks first, and when:
Note the nuance: Sportz already has a pub/sub pattern (subscribe to match rooms → broadcast to subscribers). What’s missing is making that pub/sub distributed across instances. The change at 100k+ isn’t “add pub/sub”; it’s “move the pub/sub you already have from in-process memory to a shared backbone.”
The key insight for an interview or a design review: the in-process broadcast is not a flaw: it’s the right choice until you horizontally scale the API. Adding a Redis backbone before then is premature complexity.