Back to LLD explorer
Distributed OOD• Patterns: Observer, Strategy, Singleton

WhatsApp / Chat System

Hard

Problem Summary

Design a real-time Chat and Messaging service supporting direct 1:1 and group conversations, delivery receipts (Sent, Delivered, Read), media compression, and offline mailbox queues.

Functional Scope

  • Direct 1:1 chats and multi-user group channels.
  • Real-time message dispatching via WebSockets when recipient is connected.
  • Offline message mailbox queuing with store-and-forward semantics.
  • Message delivery receipts: Sent (single check), Delivered (double check), Read (blue check).
  • Pluggable media compression and encryption strategies.

Entity-Relationship (ER) Schema

ChatRoom [1] --> [*] User
ChatRoom [1] --> [*] Message
Message [1] --> [1] MessageStatus
ConnectionManager [1] --> [*] ActiveSocketSession

Design Approach

Apply the Observer pattern for user presence and typing indicators, paired with a Strategy pattern to choose between direct socket dispatch and offline push notifications.

Core Classes & Models

ConnectionManager (Singleton tracking active WebSocket connections per userId)ChatRoom (Tracks members and routes incoming messages)Message (MessageId, sender, recipient, timestamp, content, status)DeliveryStrategy (WebSocketDeliveryStrategy, APNSPushDeliveryStrategy)PresenceObserver (Broadcasts typing status and last-seen timestamps)
Code Blueprint
64 lines
/**
 * Low-Level Design: WhatsApp Instant Messaging App
 * Design Patterns Applied:
 *  - Observer Pattern: Real-time user online presence, typing indicators, and message receipts (Sent, Delivered, Read)
 *  - Strategy Pattern: Message payload encryption and compression algorithms
 *  - Singleton Pattern: Connection gateway session registry
 */

import java.util.*;

enum MessageStatus { SENT, DELIVERED, READ }

// --- [DESIGN PATTERN: Strategy] Encryption strategy ---
interface EncryptionStrategy {
    String encrypt(String plainText);
    String decrypt(String cipherText);
}

class EndToEndEncryptionStrategy implements EncryptionStrategy {
    @Override public String encrypt(String plainText) { return "🔐[E2EE:" + plainText + "]"; }
    @Override public String decrypt(String cipherText) { return cipherText.replace("🔐[E2EE:", "").replace("]", ""); }
}

// --- [DESIGN PATTERN: Observer] Message delivery listener ---
interface MessageReceiptListener {
    void onReceiptUpdated(String msgId, MessageStatus status);
}

class ChatClient implements MessageReceiptListener {
    private final String userId;
    public ChatClient(String userId) { this.userId = userId; }
    @Override
    public void onReceiptUpdated(String msgId, MessageStatus status) {
        String tick = (status == MessageStatus.READ) ? "✓✓ (Blue)" : "✓";
        System.out.printf("💬 [User %s] Message %s status: %s %s%n", userId, msgId, status, tick);
    }
}

// --- [DESIGN PATTERN: Singleton] Chat server connection manager ---
class ChatServer {
    private static ChatServer instance;
    private final EncryptionStrategy encryption = new EndToEndEncryptionStrategy();

    public static synchronized ChatServer getInstance() {
        if (instance == null) instance = new ChatServer();
        return instance;
    }

    public void sendMessage(String msgId, String sender, String recipient, String content, MessageReceiptListener listener) {
        String encrypted = encryption.encrypt(content);
        System.out.printf("🚀 Routing message '%s' -> %s%n", encrypted, recipient);
        listener.onReceiptUpdated(msgId, MessageStatus.SENT);
        listener.onReceiptUpdated(msgId, MessageStatus.DELIVERED);
        listener.onReceiptUpdated(msgId, MessageStatus.READ);
    }
}

public class Main {
    public static void main(String[] args) {
        ChatServer server = ChatServer.getInstance();
        ChatClient sender = new ChatClient("Alice");
        server.sendMessage("MSG-101", "Alice", "Bob", "Are we meeting today?", sender);
    }
}