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);
}
}