How to architect a real-time notification system for a SaaS web application?

Asked 2 days ago Updated yesterday 55 views

0

Overview

A real-time notification system delivers immediate updates to users across web, mobile, and email channels. For a multi-tenant SaaS application, the system must handle connections efficiently without crashing backend application servers.

Architectural Components

  • WebSocket Gateway: Manages persistent bi-directional TCP connections with client browsers.
  • Message Broker: Uses Apache Kafka or RabbitMQ to decouple notification triggers from message delivery.
  • Pub/Sub State Store: Uses Redis Pub/Sub to route messages to the specific gateway instance where the target user is connected.

WebSocket Node Server Pattern

The following example demonstrates how a server registers and pushes messages to targeted client sessions:

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
const userSockets = new Map();

wss.on('connection', (ws, req) => {
    const userId = getUserIdFromReq(req);
    userSockets.set(userId, ws);

    ws.on('close', () => {
        userSockets.delete(userId);
    });
});

function sendNotification(userId, message) {
    const socket = userSockets.get(userId);
    if (socket && socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify(message));
    }
}

1 Answer


0

Architecting a Real-Time Notification System for SaaS

Designing a real-time notification system for a Software-as-a-Service (SaaS) application requires handling high throughput, maintaining low latency, and ensuring reliable message delivery across multiple channels such as in-app popups, email, mobile push notifications, and SMS.

1. Core Architectural Components

A production-ready real-time notification system consists of several decoupled layers:

  • Event Producers: Microservices that generate application events (e.g., payment succeeded, comment added).
  • Message Broker & Queue: A distributed log or queue like Apache Kafka or RabbitMQ to handle backpressure and guarantee event delivery.
  • Notification Engine: Background workers that handle user preference filtering, templating, and dispatch logic.
  • Real-Time Connection Layer: A scalable WebSocket gateway cluster backed by Redis Pub/Sub to maintain persistent client connections.
  • Persistence Store: A fast relational or document database (e.g., PostgreSQL or MongoDB) to store notification history and unread counts.

2. Processing and Publishing Event Flow

Below is a Node.js example demonstrating how a worker processes a queued notification event, persists it, and broadcasts it to a user-specific Redis channel for real-time WebSocket delivery:

const Redis = require('ioredis');
const redisPublisher = new Redis();

async function processNotificationEvent(eventData) {
    const { userId, type, payload } = eventData;

    // 1. Store notification in persistent storage
    const notification = await saveToDatabase({
        userId,
        type,
        title: payload.title,
        body: payload.body,
        isRead: false,
        createdAt: new Date()
    });

    // 2. Publish to user-specific Redis channel
    const userChannel = `user_notifications:${userId}`;
    await redisPublisher.publish(userChannel, JSON.stringify(notification));
}

3. Scaling the WebSocket Connection Gateway

Because WebSocket connections are stateful, horizontal scaling requires maintaining state synchronization across nodes. Using Redis Pub/Sub or a managed service (such as AWS API Gateway WebSockets or Pusher) allows any WebSocket node handling a user's client connection to receive and relay published notification events instantaneously.

Write Your Answer