Lesson 1: Design a Chat Application
WhatsApp: WebSockets, Pub/Sub, Message Persistence.
Lesson 1: Design a Chat Application
Goal: Design a real-time chat service like WhatsApp or Slack that supports 1-on-1 and Group messaging.
Requirements
Functional
- Send/Receive messages in real-time.
- See user status (Online/Offline).
- Message history (persistent storage).
Non-Functional
- Low Latency: Messages must appear instantly.
- Consistency: Messages must be delivered in order.
- Availability: High uptime.
Core Design
1. Communication Protocol
HTTP is request/response (pull). For chat, we need push.
- WebSockets: Keeps a persistent connection open between client and server.
2. Message Flow
- User A sends message to Chat Server.
- Chat Server finds which server User B is connected to (using a Session Store like Redis).
- Chat Server pushes message to User B.
3. Storage
- Chat History: Write-heavy. Cassandra or HBase (Wide-column stores) are good for time-series data.
- User Status: Key-Value store (Redis) with TTL.
🛠️ Sruja Perspective: Modeling Real-Time Flows
We can use Sruja to model the WebSocket connections and the async message processing.
import { * } from 'sruja.ai/stdlib'
requirement R1 functional "Real-time messaging"
requirement R2 functional "Message history"
requirement R3 latency "Instant delivery"
requirement R4 consistency "Ordered delivery"
ChatApp = system "WhatsApp Clone" {
ChatServer = container "Chat Server" {
technology "Node.js (Socket.io)"
description "Handles WebSocket connections"
scale {
min 10
max 100
metric "connections > 10k"
}
}
SessionStore = database "Session Store" {
technology "Redis"
description "Maps UserID -> WebSocketServerID"
}
MessageDB = database "Message History" {
technology "Cassandra"
description "Stores chat logs"
}
MessageQueue = queue "Message Queue" {
technology "Kafka"
description "Buffers messages for group chat fan-out"
}
ChatServer -> SessionStore "Reads/Writes"
ChatServer -> MessageDB "Persists messages"
ChatServer -> MessageQueue "Async processing"
}
UserA = person "Alice"
UserB = person "Bob"
// Scenario: 1-on-1 chat (user online)
scenario SendMessageOnline "Send Message - Recipient Online" {
UserA -> ChatApp.ChatServer "Send 'Hello'"
ChatApp.ChatServer -> ChatApp.MessageDB "Persist message"
ChatApp.ChatServer -> ChatApp.SessionStore "Lookup Bob's connection"
ChatApp.SessionStore -> ChatApp.ChatServer "Bob is on Server-2"
ChatApp.ChatServer -> UserB "Push 'Hello' via WebSocket"
UserB -> ChatApp.ChatServer "ACK received"
}
// Scenario: 1-on-1 chat (user offline)
scenario SendMessageOffline "Send Message - Recipient Offline" {
UserA -> ChatApp.ChatServer "Send 'Hello'"
ChatApp.ChatServer -> ChatApp.MessageDB "Persist message"
ChatApp.ChatServer -> ChatApp.SessionStore "Lookup Bob's connection"
ChatApp.SessionStore -> ChatApp.ChatServer "Bob is offline"
ChatApp.ChatServer -> ChatApp.MessageDB "Mark as pending delivery"
}
// Scenario: Group chat (fan-out)
scenario SendGroupMessage "Send Group Message" {
UserA -> ChatApp.ChatServer "Send 'Hello' to Group"
ChatApp.ChatServer -> ChatApp.MessageDB "Persist message"
ChatApp.ChatServer -> ChatApp.MessageQueue "Enqueue for fan-out"
ChatApp.MessageQueue -> ChatApp.ChatServer "Process for each member"
ChatApp.ChatServer -> ChatApp.SessionStore "Lookup each member's server"
ChatApp.ChatServer -> UserB "Push to member 1"
ChatApp.ChatServer -> UserC "Push to member 2"
ChatApp.ChatServer -> UserD "Push to member 3"
}
// Scenario: Message history retrieval
scenario GetMessageHistory "Retrieve Message History" {
UserA -> ChatApp.ChatServer "Request chat history"
ChatApp.ChatServer -> ChatApp.MessageDB "Query messages"
ChatApp.MessageDB -> ChatApp.ChatServer "Return messages"
ChatApp.ChatServer -> UserA "Send history"
}
view index {
include *
}