sent → delivered → read, tracked500M registered users total - this data can sit on a PostgersqlDB
100M DAU active each day
Peak throughput ≈ 3.3M messages/sec writes (100M concurrent × 2 msg/min ÷ 60s) — plus the same order of read/fan-out traffic
/api/user/:id/group [POST]
/api/user/:id/group/:groupId [DELETE]
/api/user/:id/message/:otherUserId [POST]
/api/user/:id/message/:groupId [POST]
/api/user/:id/media/:groupId [POST]
/api/user/:id/messages/:group:id [GET]
GroupId can be created for every 1on1 as well
Join chat / subscribe
A chat system's defining API is the real-time channel — e.g., WebSocket: ws://server/ws?userId=...
Authentication Service - to make sure that client can be supported to sending
SendingService - responsible for passing messages between users and users & groups. This is responsible for establishing real time channels (WebSockets) as well.
ArchiveService - responsible for moving older messages to historicMessagesDB
GroupsDB, MessagesDB, UsersDB, HistoricMessagesDB
MessagesQueue - storing data to be pushed for sending
MessagesWorker - job that pulls messages from the queue and forwards this to Sending Provider
LocalMessagesCache - Every message in a conversation gets a monotonic ID (per-conversation sequence number).
The client stores the last message ID it received locally.
On reconnect: client re-opens the WebSocket, then pulls the gap via REST — GET /messages?after=<last_id>.
Delivery is at-least-once; the client dedupes by message ID.
RateLimiter (API Gateway) - A rate-limiter rejects with a trivial check (a Redis counter). Letting abuse traverse the LB, routing layer, and land at SenderService means you've spent real CPU/network on traffic you're about to throw away.
"Messages land on a conversation topic once; all WS nodes subscribed to that topic consume and deliver in parallel."
USERS (id, name, email, hashed_pwd, prefs)
GROUPS (is, name, owner_id, type: 1on1 vs group)
GROUP_MEMBERS (id, userid, role, joined_at, last read msssg)
MESSAGES (id, senderId, groupid, timestamp, text, medialinks)
read-receipt tracking: store last_read_msg_id on GROUP_MEMBERS — that gives per-recipient read state for free.
Shard Messages by sender_id with consistent hashing — a hot user's writes stay on one partition, and consistent hashing redistributes load evenly as nodes are added/removed.
last_message_id per conversation; on reconnect it re-subscribes and pulls GET /messages?after=<id>, deduping by msg_id — no gaps, no duplicates.3. Pub/Sub bus (Kafka/Pulsar)
group_id (single partition per group) → messages in one chat arrive in order despite fan-out and retries.msg_id; producers/consumers are idempotent, so retries don't duplicate.4. Scaling at peak (the 3.3M msg/s answer)