System Design: Chat Application — System Design

Mari desain sistem chat real-time seperti WhatsApp atau Slack. Ini contoh system design yang menggabungkan banyak konsep yang sudah kita pelajari. Step 1: Requi

Mari desain sistem chat real-time seperti WhatsApp atau Slack. Ini contoh system design yang menggabungkan banyak konsep yang sudah kita pelajari.

Step 1: Requirements

Functional:

Non-functional:

Step 2: Estimasi

500 juta user aktif, rata-rata 50 pesan/hari
Total: 25 miliar pesan/hari
= 290.000 pesan/detik

Storage per pesan: ~200 bytes
Per hari: 25B × 200 = 5 TB/hari
Per tahun: 1.8 PB (petabyte!)

Step 3: Arsitektur High-Level

┌─────────┐     WebSocket     ┌─────────────┐
│ Client  │ ◄───────────────► │ Chat Server │
│ (App)   │                   │ (Stateful)  │
└─────────┘                   └──────┬──────┘
                                     │
                              ┌──────┴──────┐
                              │ Message     │
                              │ Queue       │
                              └──────┬──────┘
                                     │
              ┌──────────────┬───────┴────────┐
              │              │                │
        ┌─────┴─────┐ ┌─────┴─────┐  ┌──────┴──────┐
        │ Message   │ │ Presence  │  │ Notification│
        │ Store     │ │ Service   │  │ Service     │
        └───────────┘ └───────────┘  └─────────────┘

Step 4: Real-time Messaging (WebSocket)

// Server: manage WebSocket connections
const connections = new Map(); // userId → WebSocket

wss.on("connection", (ws, req) => {
  const userId = authenticate(req);
  connections.set(userId, ws);

  ws.on("message", async (data) => {
    const msg = JSON.parse(data);

    // Simpan ke database
    const saved = await saveMessage(msg);

    // Kirim ke penerima jika online
    const recipientWs = connections.get(msg.recipientId);
    if (recipientWs) {
      recipientWs.send(JSON.stringify(saved));
    } else {
      // Offline → push notification
      await pushService.notify(msg.recipientId, saved);
    }

    // Kirim ack ke pengirim
    ws.send(JSON.stringify({ type: "ack", messageId: saved.id }));
  });

  ws.on("close", () => {
    connections.delete(userId);
    updatePresence(userId, "offline");
  });
});

Step 5: Message Storage

Pilihan database:
- Cassandra/ScyllaDB → write-heavy, partitioned by conversation
- PostgreSQL → kuat di query, cocok untuk fitur search

Schema:
messages:
  message_id: UUID (time-based, e.g. Snowflake ID)
  conversation_id: UUID
  sender_id: BIGINT
  content: TEXT (encrypted)
  type: ENUM(text, image, file)
  created_at: TIMESTAMP
  read_by: JSON

Partition key: conversation_id
Sort key: created_at (message ordering)

Step 6: Group Chat

Group:
  group_id, name, members[], admin[]

Kirim pesan ke group:
1. Simpan pesan 1x di database
2. Fan-out: kirim ke semua member yang online via WebSocket
3. Member offline → simpan di "undelivered" queue

Optimasi fan-out:
- Small group (< 100): fan-out on write (kirim langsung)
- Large group (> 100): fan-out on read (lazy loading)

Step 7: Presence (Online/Offline Status)

// Heartbeat approach:
// Client kirim ping setiap 30 detik
// Jika tidak ada ping dalam 60 detik → anggap offline

setInterval(() => {
  redis.set("presence:" + userId, "online", "EX", 60);
}, 30000);

// Cek status user:
async function isOnline(userId) {
  return await redis.exists("presence:" + userId);
}

Step 8: End-to-End Encryption

Setiap user punya key pair (public + private):
1. Sender encrypt pesan dengan public key penerima
2. Hanya penerima yang bisa decrypt dengan private key-nya
3. Server hanya menyimpan ciphertext — tidak bisa baca pesan!

Scaling:

System design chat app menggabungkan: WebSocket, database scaling, caching, message queue, CDN, dan real-time architecture.

🎭 Analogi sehari-hari: Chat = walkie-talkie zaman dulu. Tekan tombol kirim suara langsung ke channel — semua yang tune ke channel itu denger. WebSocket = "channel selalu terbuka", beda dari HTTP yang "tanya-jawab terus tutup". Tanpa WebSocket, client harus polling tiap detik = boros battery + traffic.

💡 Mengapa WebSocket bukan polling? HTTP polling = tanya server tiap 1 detik "ada pesan baru?" — 99% jawab "tidak" = waste. WebSocket = koneksi tetap terbuka, server push pas ada pesan. Beda urutan magnitudo: polling 86400 req/hari per user, WebSocket 0 (kecuali ada event).

⚠️ Jebakan chat system klasik:

🎯 Komponen kritis:

TL;DR: Chat app = WebSocket (real-time) + message store (Cassandra) + Redis Pub/Sub (cross-server) + push notif (offline). Order kritis (Snowflake ID). E2E encryption optional. Group besar pakai fan-out on read.