The Challenge

In an educational platform, timely notifications are critical. When a teacher posts a new assignment, students need to know immediately. When a student submits work, the teacher should be notified right away. When an admin changes a schedule, everyone affected needs to see the update in real-time.

Our original notification system was poll-based — the client would check for new notifications every 30 seconds. This approach had three major problems: delayed notifications (up to 30 seconds), wasted server resources from constant polling, and poor battery life on mobile devices.

Architecture Overview

We rebuilt the system from the ground up using a WebSocket-based architecture with the following components:

  • WebSocket Gateway: A Node.js server using Socket.io that maintains persistent connections with all connected clients.
  • Event Bus: A Redis-based pub/sub system that distributes notification events across multiple server instances.
  • Notification Service: A backend service that creates, stores, and routes notifications based on configurable rules.
  • Angular Signal Store: A client-side reactive store using Angular signals that manages notification state.

The WebSocket Layer

Each client establishes a WebSocket connection on login. The connection is authenticated using the same JWT token used for REST API calls. We use Socket.io rooms to group connections by tenant (school) and user role, enabling efficient broadcasting.

For example, when a teacher creates a new assignment for Class A, the notification service publishes an event to the tenant:123:class:A:students room. Only students in that specific class receive the notification — no unnecessary traffic.

Handling Scale

With thousands of concurrent connections, a single WebSocket server isn't enough. We use Redis adapter for Socket.io to synchronize events across multiple server instances. When a notification is published, Redis ensures it reaches the correct server instance that holds the target client's connection.

Client-Side with Angular Signals

On the frontend, we use Angular's signal-based reactivity to manage notification state. When a WebSocket message arrives, it updates a signal store, which automatically triggers UI updates across all components that consume notification data — the bell icon counter, the notification dropdown, and the notification center page.

This approach eliminates the need for manual subscription management and prevents memory leaks from forgotten unsubscriptions.

Offline Support

Not all users are connected at all times. When a notification is generated for an offline user, it's stored in the database and marked as undelivered. When the user reconnects, the system sends all pending notifications in a single batch, sorted by priority and timestamp.

The new system delivers notifications in under 100ms on average, compared to up to 30 seconds with the old polling approach — a 300x improvement.

This architecture has been running in production for 6 months, handling over 2 million notifications per day across our platform with 99.97% delivery reliability.