Messaging & social, an interactive architecture walkthrough
WhatsApp-style chat app: how the architecture works
How a chat system holds millions of persistent connections, routes end-to-end encrypted messages, and falls back to push when users are offline.
This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate.
Interactive diagram
Overview
A chat system holds one long-lived connection per online user, so its connection tier is stateful and needs a registry that says which server each user is on. Messages are encrypted on the sender's device, so servers only route ciphertext: they assign ordered ids, hold each message only until it is delivered, and park it in an inbox with a content-free push when the recipient is offline. Chat history lives on the devices. This is a generic reference design, not WhatsApp's internal architecture.
Trade-offs and what changes at scale
Keeping messages on the server only until delivery keeps storage small and fits end-to-end encryption, but history then lives on the devices, so backups and adding a new device need extra work; server-history apps (Telegram, Messenger) make the opposite choice. Each linked device has its own keys, so a 1:1 message is encrypted and delivered once per device. Group content is encrypted once with the sender's group key (Signal's Sender Keys), and the server fans out the same ciphertext. At scale the stateful tier is the hard part: spread connections across many servers, shard the session registry, and rate-limit reconnects so an outage does not turn into a reconnect storm. Ordering is kept per conversation, not globally, and delivery is at-least-once, so clients de-duplicate by message id, and very large groups need a cap or a different fan-out path.
How it works, step by step
The walkthrough above, as text. Each step lights up the components named under it.
The app opens a persistent WebSocket through the load balancer to one stateful chat server. Long-lived connections are why this tier is stateful and needs a registry.
At registration the app sends the phone number to the auth service over HTTPS, which has an SMS provider send a code; once it is verified, the app receives a token. On every connect the client presents that token; sockets without a valid one are dropped.
After auth, the chat server records user to server in Redis with a TTL refreshed by heartbeats. This is how anyone finds the user later.
Alice encrypts a 1:1 message on her device, using Bob's public prekeys that she fetched from the key service when the conversation started, so Bob can be offline. Clients upload their prekey bundles at registration and top up the one-time prekeys as they are used. The chat server forwards the opaque ciphertext to the message service, which never needs to read it.
The message service assigns an ordered per-conversation id and durably stores the ciphertext, then acks Alice (the 'sent' receipt). The server keeps it only until Bob's device acknowledges it; long-term history lives on the devices. Writes are cheap and partitioned by conversation.
The registry says Bob is on server B, so the message is delivered there and pushed over his socket. Bob's ack produces the 'delivered' receipt back to Alice.
If Bob has no session, the message stays in his offline inbox so nothing is lost. The push service asks APNs/FCM to prompt his device to reconnect with a notification that carries no plaintext. Push is best effort and may be delayed or dropped, so the inbox, not the push, is the source of truth. When he reconnects, the inbox (ids pointing into the message store, not a second copy) drains to his chat server.
When Bob opens the chat, his client sends a 'read' receipt. It travels the same route back as a small message, so Alice sees the blue ticks.
For a group message, the message service asks the group service for the members from the relational DB, then fans out one delivery per member, online or offline. Group content is encrypted once with the sender's group key (Signal's Sender Keys); that key was sent to each member device over its pairwise session. The server fans out the same ciphertext without re-encrypting or reading it. 1:1 chats and key distribution stay per device.
Media is encrypted on the device, uploaded to object storage via the media service, and only a reference plus key travels in the chat message. Chat servers stay light.
Recipients download the encrypted blob from the CDN, which caches from S3 close to the user, then decrypt locally.
Online/offline presence comes from the registry: connect and heartbeat refresh it, and expiry marks a user offline. 'Last seen' is written to a durable store when heartbeats stop or on a throttled schedule; a clean disconnect event cannot be relied on because mobile connections usually drop silently, and the expiring registry entry would take the timestamp with it.
Metrics and logs from each service flow to observability for alerting.
Sequence diagram
The same walkthrough as a UML sequence diagram, one lane per layer or group (19 components in all), left to right in the order a request reaches them, numbered by step. A loop on a lane is a step that stays inside that group. Solid arrows are synchronous calls, dashed are asynchronous.
Key components
Connection Tierlayer
Stateful gateway servers
- Chat Servers: hold WebSockets, push frames to users (websocket)
- Presence/Session Registry: user -> server map with TTL, presence (redis)
Load Balancer
- Load Balancer: TLS termination, WebSocket upgrade
Mobile Clients
- Mobile Clients: iOS/Android apps, persistent WebSocket
Identitydomain
- Auth Service: phone number verification, tokens
- Key Service: public prekey bundles, uploaded at registration
- SMS/OTP Provider: sends verification codes
Messagingdomain
- APNs / FCM: mobile push providers
- Group Service: membership lookup
- Message Service: persist, order ids, route, receipts
- Push Notification Service: wakes offline devices
Data Layerlayer
- Message Store: held until delivered; time-bucketed, TTL-expired (cassandra)
- Offline Inbox: pending message ids (not a copy), drained on reconnect (cassandra)
- Prekey Store: identity keys and one-time prekeys
- User & Group DB: profiles, last seen, groups (relational)
Mediadomain
- CDN: edge delivery of media
- Media Service: upload URLs, encrypted blobs
- Object Storage: encrypted images and video (s3)
Observability
- Observability: metrics, logs, tracing
Sources
Public writing this explainer draws on. Read these for what the real systems actually do.
Make it yours
Copy this diagram and its walkthrough into your own workspace, then change anything: swap Postgres for DynamoDB, add a cache, rewrite the narration. Free, and it uses no AI credits.
Related walkthroughs
- Chat application (1:1 and group)How a chat app keeps a WebSocket per online user, orders and stores messages per channel, and lets reconnecting clients catch up from history.
- Notification system at scaleHow a notification platform validates requests, applies user preferences, queues by priority, and delivers push, SMS and email with retries and fallbacks.
- Social news feed (Twitter-style timeline)How a social feed pushes posts into follower timelines, handles accounts with millions of followers by merging at read time, and ranks the result.
This is a learning explainer based on public engineering writing and general design practice. It is simplified, it is not the actual architecture of any company, and it may be inaccurate. Real systems differ in the details. Browse the library.