✅ System Design 8: Design Chat Application like WhatsApp | Messenger | HLD | LLD
Designing a massive, real-time distributed chat application (such as WhatsApp or Facebook Messenger) serving 1 billion active users requires balancing ultra-low latency, high availability, massive write throughput, and reliable message delivery. This document presents a comprehensive high-level (HLD) and low-level design (LLD) blueprint based on real-world system architecture principles.
1. Why Do We Need This? (Problem Statement & Status Quo)
Modern communication platforms are expected to deliver messages in near real-time across the globe. Unlike traditional CRUD applications that rely on stateless request-response HTTP cycles, chat applications require persistent, bidirectional communication channels capable of handling erratic network conditions, millions of concurrent connections, offline message queuing, media file transfers, and group broadcasts.
Core Functional Requirements
- User Authentication: Secure sign-up, login, and profile management using phone numbers (WhatsApp model) or email addresses (Messenger model).
- 1-to-1 Messaging: Real-time text and media exchange between two individual users.
- Group Messaging: Multi-party chat rooms supporting message broadcasting and membership management.
- Chat History & Pagination: Ability to retrieve past conversation logs using lazy-loading scrolling mechanics.
- Message Delivery Status: Real-time tracking of message states represented by single ticks (sent to server), double ticks (delivered to recipient), and blue ticks (read by recipient).
Non-Functional Requirements & Scale Estimation
- Massive Scale: Supporting 1 billion total users with an average of 100 messages per user per day, culminating in 100 billion messages per day.
- Data Volume: Assuming an average message size (text and media references) of 1 KB, backend storage requirements scale to:
- CAP Theorem Trade-Off: Chat systems prioritize Availability () and Partition Tolerance () over strict linearizability/consistency (). Temporary delays in message ordering or eventual consistency are acceptable, but system downtime is intolerable.
- Ultra-Low Latency: End-to-end message delivery from sender to receiver must occur within .
- High Reliability: Zero message loss tolerance once a message is accepted by the server.
2. Core Entities & Data Model
To structure the application database, we identify three foundational domain entities: Users, Groups, and Messages.
Core Entities Breakdown
- Users: Metadata profiles containing unique identifiers, credentials, and last-seen timestamps.
- Groups: Chat containers holding metadata (group name, description, creator) and member mapping relations.
- Messages: Individual chat entries containing sender IDs, receiver/group IDs, timestamps, payload types, and delivery status flags.
3. API Design & Communication Protocols
Designing APIs for a chat system requires a hybrid approach: standard REST endpoints for bootstrapping, onboarding, and group administration, paired with WebSockets for real-time messaging.
REST Endpoints for Onboarding & Groups
4. Why WebSockets Over HTTP/Long-Polling?
Evaluating communication protocols for real-time chat highlights why persistent duplex connections are mandatory.
5. High-Level System Architecture & Flow
The high-level architecture separates stateless HTTP traffic (onboarding, media uploads) from stateful WebSocket traffic (real-time chat messaging).
High-Level Data Flow Walkthrough
- Client Connection: A user opens the app and initiates an HTTP handshake request to the WebSocket Gateway.
- Protocol Upgrade: The gateway validates JWT credentials, authorizes the user, and upgrades the HTTP connection to a persistent WebSocket.
- Session Registration: The gateway registers the user ID and active connection socket inside a distributed cache registry.
- Message Dispatch: When User A sends a message to User B, it flows through the WebSocket to Chat Service 1, which queries the registry, routes it to the target Chat Service, and delivers it instantly over User B's open socket.
6. Low-Level Design (LLD): Deep Dive into Services & Components
6.1 User Service & Database (PostgreSQL)
The User Service handles registration, profile updates, and JWT issuance. Because user records are relational and require ACID guarantees, PostgreSQL is utilized.
User Table Schema (users):
CREATE TABLE users (
user_id UUID PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
phone_number VARCHAR(20) UNIQUE NOT NULL,
status VARCHAR(255),
last_seen TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
6.2 Group Service & Mapping Database (PostgreSQL)
Groups require relational consistency to manage membership mappings efficiently.
Group Tables Schema:
CREATE TABLE groups (
group_id UUID PRIMARY KEY,
group_name VARCHAR(100) NOT NULL,
description TEXT,
created_by UUID REFERENCES users(user_id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE group_members (
id SERIAL PRIMARY KEY,
group_id UUID REFERENCES groups(group_id) ON DELETE CASCADE,
user_id UUID REFERENCES users(user_id) ON DELETE CASCADE,
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (group_id, user_id)
);
6.3 Chat Service & Cassandra Storage
The Chat Service manages message routing, real-time dispatch, and offline buffering. Because chat applications generate immense write volumes (100B messages/day), relational databases fail under the load. We choose Apache Cassandra (a wide-column, write-optimized NoSQL database).
Chat Messages Schema (chat_messages):
CREATE KEYSPACE whatsapp_chat WITH replication = {
'class': 'SimpleStrategy',
'replication_factor': 3
};
CREATE TABLE whatsapp_chat.messages (
chat_id UUID,
message_id TIMEUUID,
sender_id UUID,
receiver_id UUID,
message_type TEXT, -- 'text', 'image', 'video'
message_payload TEXT, -- Text content or S3 URL
delivery_status TEXT, -- 'sent', 'delivered', 'read'
created_at TIMESTAMP,
PRIMARY KEY (chat_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
7. Advanced Engineering Deep Dives
A. Handling Offline Users & Message Queuing (Redis Streams)
When User 1 sends a message to User 2, but User 2 is offline (no active WebSocket connection in the registry), the system cannot deliver the message in real time.
- Event Buffering: The chat service pushes the message into a lightweight Redis Stream channel corresponding to the offline user.
- Persistence Consumer: A background worker (Message Service) consumes events from Redis Streams and persists undelivered messages into Cassandra.
- Push Notifications: Simultaneously, a notification triggers Firebase Cloud Messaging (FCM) for Android or Apple Push Notification Service (APNS) for iOS to wake up the recipient device.
B. Group Messaging Architecture
Contrary to intuition, group messages are not broadcast via a single network call. When User A sends a message to a group of 50 members:
- The Chat Service calls the Group Service to retrieve all member User IDs.
- The server iterates through the member list, checking the WebSocket Registry for active connections.
- For active users, the message is pushed directly over their open WebSocket.
- For offline users, the message is routed to Redis Streams for database persistence and mobile push notifications. Why? This granular approach ensures accurate per-user delivery and read-receipt tracking (single tick, double tick, blue tick).
C. Media Upload Workflow (S3 & CDN Integration)
Sending high-resolution images or videos through WebSockets would choke the connection. Instead, media follows an asynchronous HTTP workflow:
8. Real-World Pitfalls, Edge Cases, & Debugging
- WebSocket Connection Drops: Mobile networks drop frequently when entering elevators, tunnels, or switching cell towers. To handle this, clients implement automatic exponential backoff reconnection logic bundled with heartbeat ping/pong frames.
- Sticky Gateway Routing: Because WebSockets maintain state, load balancers must utilize sticky sessions mapped via the WebSocket Registry in Redis. If a gateway restarts, clients must seamlessly reconnect to an alternate node without dropping queued messages.
- Database Bloat & Archival: Storing trillions of messages in Cassandra requires TTL (Time-To-Live) policies or cold-storage archiving to Amazon S3 for chat histories older than a specified threshold.
9. Summary and Key Takeaways
- Dual-Protocol Strategy: Use HTTP/REST for stateless operations (sign-up, group creation, media uploads) and WebSockets for real-time bidirectional messaging.
- Massive Scale Demands NoSQL: Handle 100 billion messages daily using Cassandra for write-heavy performance and linear scalability.
- Lightweight Buffering: Use Redis Streams over heavy brokers like Kafka to minimize message delivery latency ().
- State Registry: Maintain user-to-server connection mappings in a distributed Redis Cache to route messages accurately across clustered chat servers.
- Offline Resilience: Buffer undelivered messages via event streams, persist them in the database, and trigger mobile push notifications (FCM/APNS).
- Asynchronous Media Handling: Offload heavy image and video transfers directly to Blob Storage (S3) and CDNs, passing only lightweight URLs through chat channels.