Messaging & social, an interactive architecture walkthrough

Chat application (1:1 and group): how the architecture works

How a chat app keeps a WebSocket per online user, orders and stores messages per channel, and lets reconnecting clients catch up from history.

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.

28 components, 33 connections, 12 steps. Updated 4 November 2026.

Interactive diagram

Overview

A chat application delivers messages between two people or within a group in near real time, and keeps the full history on the server so any device can load it. Each online client holds a WebSocket to a gateway server; a message is checked for membership, given the next sequence number in its channel, written to a store partitioned by channel, and then published to the members' topics so whichever gateway holds each member can push it.

The live push and the stored history play different roles. Push is fast but best effort, so clients that reconnect fetch everything after the last sequence number they have from the store. Offline mobile users are reached through APNs or FCM, and search and notifications run off an event stream. Unlike an end-to-end encrypted design, the server can read and index messages here. This is a simplified learning design, not any company's production system.

Trade-offs and what changes at scale

Server-side history makes multi-device sync and search simple but means the operator holds message content. Ordering is per channel, not global, which lets channels be spread across partitions. Publishing to per-user topics is simple for DMs and small groups, but a channel with tens of thousands of members multiplies every message, so large channels are better served by gateways subscribing to a per-channel topic. The gateway tier is stateful: deploys must drain sockets gradually and clients reconnect with jittered backoff so a restart does not become a reconnect storm.

How it works, step by step

The walkthrough above, as text. Each step lights up the components named under it.

  1. When the app opens, it upgrades a connection through the Load Balancer to one of the Gateway Servers, which checks the token with the Auth Service. A WebSocket lets the server push new messages without the client polling.

    Components Gateway Servers, Web Client, Load Balancer, Auth ServiceVia WebSocket

  2. The gateway records which server holds the user in the Session Registry with a TTL that heartbeats refresh, and subscribes to that user's topic on the Pub/Sub Bus. A gateway that crashes simply stops refreshing, and its entries expire.

    Components Session Registry, Pub/Sub BusVia register session

  3. A sent message travels over the socket to the Message Service, which asks the Channel Service whether the sender belongs to that DM or group. The client attaches its own message id, so a retry after a dropped connection is de-duplicated rather than posted twice.

    Components Message Service, Channel Service, Channel DBVia send message

  4. The Message Service assigns the next sequence number for the channel and appends the message to the Message Store, partitioned by channel and time bucket. One partition then serves "the latest 50 messages in this channel" in a single read, and buckets stop any partition growing without limit.

    Components Message StoreVia append

  5. After the write is acknowledged to the sender, the message is published on the Pub/Sub Bus to each member's user topic, and every gateway holding one of those users pushes it down the socket. The bus does not know about servers, so gateways can be added or restarted freely.

    Components Pub/Sub Bus, Gateway ServersVia publish

  6. Redis pub/sub is at-most-once, so a client that reconnects asks the REST API for messages after the last sequence number it has, read from the Message Store. The store, not the live push, is the source of truth.

    Components REST API, Mobile ClientVia history page

  7. The same message is written to the Message Events topic, and the Push Worker checks the Session Registry for members with no live socket and notifies them through APNs or FCM. Mobile apps close their sockets in the background, so push is how they learn about new messages.

    Components Push Worker, Message Events, Session Registry, APNs / FCMVia consume

  8. When a user reads a conversation, the client reports the highest sequence number seen and the Channel Service stores it as a last-read mark. Unread counts are the difference between the channel's latest sequence and that mark, so they need no per-message state.

    Components Channel Service, Channel DBVia read marks

  9. The Presence Service derives online and idle status from gateway heartbeats and the Session Registry, and publishes changes only to users who share a channel. Typing indicators use the same short-lived path and are never stored.

    Components Presence Service, Session Registry, Pub/Sub BusVia heartbeats

  10. The Search Indexer consumes message events into the Search Index, and the REST API filters results to channels the user can see. Indexing off the stream keeps search a few seconds behind without slowing sends.

    Components Search Index, Search Indexer, REST APIVia index

  11. Attachments are uploaded through the Media Service to the Attachment Store, and the message carries only a reference. Recipients download from the CDN, so files never pass through the socket tier.

    Components Media Service, Attachment Store, CDN, Mobile ClientVia upload URL

  12. Observability tracks open sockets per gateway, send-to-delivery latency and consumer lag on the event topic. A spike in reconnects after a deploy shows up here first.

    Components Observability, Message Service, Push WorkerVia metrics

Sequence diagram

The same walkthrough as a UML sequence diagram, one lane per layer or group (22 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.

Chat application (1:1 and group): sequence diagram of the walkthroughEdgeReal-time TierClientsMessagingAsync ProcessingAttachmentsObservabilityWebSocket1WSS1verify token1register session2subscribe user2send message3check members3SQL3append4publish5subscribe user5history page6HTTPS6consume7message created7online?7push7read marks8SQL8heartbeats9read sessions9presence events9index10consume10search10upload URL11store file11origin11download11metrics12metrics12metrics12

Key components

Clientslayer

  • Mobile Client: iOS/Android, WebSocket while open
  • Web Client: browser app, keeps one WebSocket

Edgelayer

  • Auth Service: sessions and access tokens
  • Load Balancer: TLS, WebSocket upgrade, spreads sockets
  • REST API: history, channels, search, uploads

Real-time Tierlayer

  • Gateway Servers: hold sockets, heartbeats, push frames (websocket)
  • Presence Service: online, idle, typing indicators
  • Pub/Sub Bus: per-user and per-channel topics (redis)
  • Session Registry: user to gateway map with TTL (redis)

Messagingdomain

  • Channel DB: channels, members, last-read marks (postgresql)
  • Channel Service: DMs, groups, members, read state
  • Message Service: membership check, sequence, persist
  • Message Store: partitioned by channel and time bucket (cassandra)

Async Processinglayer

  • APNs / FCM: mobile push providers
  • Message Events: message created / edited topic (kafka)
  • Push Worker: notifies users with no live socket
  • Search Index: per-user searchable history (elasticsearch)
  • Search Indexer: indexes message text

Attachmentsdomain

  • Attachment Store: files and images (s3)
  • CDN: attachment downloads
  • Media Service: upload URLs, virus scan, thumbnails

Observability

  • Observability: socket counts, delivery latency, lag

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.

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.