High Level Design

Design a Notification System

End-to-end design of a scalable notification system — iOS/Android push, SMS, email, message queues for async delivery, reliability, deduplication, and the improved multi-server architecture.

August 10, 2026·Updated September 17, 2026

A notification system sends notifications to users through various channels — push notifications, email, and SMS — triggered by services or scheduled on the server side.


Step 1: Requirements#

Clarifying Questions#

QuestionAnswer
Notification typesPush (iOS + Android), Email, SMS
Real-time or batch?Soft real-time — deliver ASAP; slight delays acceptable
Supported platformsiOS, Android, web browsers
Triggered byClient applications or server-side schedulers
Volume10M mobile push/day, 1M SMS/day, 5M emails/day

Functional Requirements#

#Requirement
1Send notifications via multiple channels: iOS push, Android push, SMS, email
2Notifications triggered by services (e.g. order placed, friend request) or scheduled server-side
3Support multiple platforms: iOS, Android, web (desktop/laptop)
4Collect and store user contact info: email, phone number, device tokens
5Per-user notification preferences: opt-in/opt-out per channel and notification type
6Reusable notification templates stored in DB
7Event tracking: open rate, click rate, delivery status
8Retry failed notifications; alert if problem persists

Non-Functional Requirements#

#RequirementDetail
1Soft real-timeDeliver ASAP; small delays acceptable — not strictly real-time
2Scale10M push/day, 5M emails/day, 1M SMS/day
3High availabilityNo SPOF; notification servers must be horizontally scalable
4ReliabilityAt-least-once delivery — notifications must never be silently lost
5IdempotencyHandle duplicates on the client side using unique notification IDs
6ExtensibilityEasy to add/swap third-party providers (e.g. FCM → Jpush for China)
7SecurityOnly authenticated/verified clients can trigger notifications
8Rate limitingCap notifications per user to prevent spam and notification fatigue

Step 2: Entities#

EntityResponsibility
UserIdentity and contact details — email address, phone number
DeviceA user's registered device; stores device token and platform (iOS / Android / Web)
NotificationThe notification event itself — type (push/SMS/email), content/payload, status, target user, timestamp
NotificationTemplateReusable content blueprint for a notification type; reduces errors and keeps formatting consistent
NotificationSettingPer-user, per-channel opt-in/out preferences; checked before every notification is sent
NotificationLogAudit record of every dispatched notification; drives retry logic, deduplication, and event tracking (open rate, click rate)

Key relationships:

  • One User has many Devices (phone + laptop + tablet)
  • One User has one NotificationSetting per channel
  • Each Notification references a NotificationTemplate and a target User
  • Each Notification produces one NotificationLog entry per delivery attempt

Step 3: APIs#

Send Notification#

Internal only — accessible by verified services via appKey + appSecret. Never exposed publicly.

POST /v1/notifications

ParamDescription
user_idTarget user
channelpush / sms / email
template_idWhich template to use
dataKey-value pairs to interpolate into the template

Contact Registration#

POST /v1/users/contact — Register or update a user's contact details (email, phone, device token).

ParamDescription
user_idThe user being registered
emailEmail address (verified before use)
phonePhone number with country code (verified before use)
device_tokenUnique token issued by APNs / FCM
platformios / android / web
auth_tokenAuthenticates the request

All three contact fields are optional per call — a client only sends what it has. A web browser sends email but no device token; a mobile app sends all three.

DELETE /v1/devices/{device_token} — Deregister a device on logout or uninstall.


Notification Preferences#

GET /v1/users/{user_id}/settings — Fetch a user's opt-in/out preferences per channel.

PUT /v1/users/{user_id}/settings — Update preferences for a specific channel or notification type.

ParamDescription
channelpush / sms / email
notification_typee.g. marketing, transactional, alerts
enabledtrue / false

Step 4: High Level Design#

Three main concerns:

  1. Types of notifications — how each channel works end-to-end
  2. Contact info gathering — how we collect and store delivery addresses
  3. Notification sending/receiving flow — the core pipeline

Types of Notifications#

iOS Push Notification#

Three components:

ComponentRole
ProviderBuilds and sends requests to APNs, including device token and payload
APNsApple's service that routes the notification to the target device
DeviceReceives and displays the notification

iOS push notification flow

Android Push Notification#

Same flow as iOS, but uses Firebase Cloud Messaging (FCM) instead of APNs.

Android push notification flow

SMS#

Uses commercial services with global carrier connectivity:

ServiceArchitecture
TwilioMicroservices on AWS, Kafka for reliability, SIP/PSTN gateways to carriers
NexmoRESTful APIs, SMPP/SIP carrier connections, message brokers for delivery guarantees

SMS flow

Email#

Companies typically use commercial email services:

ServiceArchitecture
SendGridDistributed delivery via Kafka/RabbitMQ, global SMTP relays
MailchimpMarketing automation, async campaign processing

Email flow

Pattern across all channels: Provider → Third-Party Service (APNs / FCM / Twilio / SendGrid) → Device/Recipient


Contact Info Gathering#

To send notifications, we need to know how to reach each user. This info is collected at registration or profile update and stored securely in the database.

DataWhen collectedAPI
Email addressUser registration / profile updatePOST /v1/users/contact
Phone numberUser registration / profile updatePOST /v1/users/contact
Device tokenApp install / first openPOST /v1/users/contact
  • Email and phone must be verified before use.
  • A user can have multiple devices — each device token is stored separately and linked to the user.
  • On logout or uninstall, the device token is removed via DELETE /v1/devices/{device_token} to prevent stale-token delivery failures.

contact info gathering


Initial Design (Single Server)#

initial high level flow

ComponentRole
Services 1–NTrigger notifications (order service, chat service, cron jobs)
Notification SystemSingle server; provides APIs and builds payloads for third-party services
Third-Party ServicesAPNs, FCM, Twilio, SendGrid
UsersReceive notifications on their devices

Problems with the Initial Design#

ProblemImpact
Single point of failureOne notification server = one bottleneck; one crash = no notifications
ScalabilityCannot scale DB, cache, and processing components independently
Performance bottleneckResource-intensive processing on a single server causes delivery delays

Improved Design#

Three key changes from the initial design:

  1. DB and Cache moved out of the notification server.
  2. Multiple notification servers with auto horizontal scaling.
  3. Message queues added for async processing.

improved high level flow

ComponentRole
Services 1–NTrigger notifications via APIs
Notification ServersValidate requests, query DB/Cache for contact info, push events to queues
CacheStores user info, device info, notification templates for fast access
DatabaseStores contact info, notification logs, templates
Message QueuesDecouples submission from processing; one queue per channel (APNs, FCM, SMS, Email)
WorkersPull events from queues; deliver via third-party services; scale independently
Third-Party ServicesAPNs, FCM, Twilio, SendGrid

End-to-End Flow#

  1. A service calls Notification Server APIs.
  2. Notification servers fetch user metadata (device token, contact info) from cache or DB.
  3. Event pushed to the appropriate queue (iOS queue → APNs, etc.).
  4. Workers pull events from queues.
  5. Workers deliver via third-party services.
  6. Third-party services push to user devices.

Step 5: Detailed Design#

Reliability#

How to prevent data loss?

Notification events can be delayed or reordered — but must never be lost. Notification data is persisted to DB before processing, and a retry mechanism handles failures.

reliability

Will users receive duplicate notifications?

Yes — distributed systems cannot guarantee exactly-once delivery. Mitigate on the client side:

  1. Each notification carries a unique ID.
  2. On arrival, client checks a local cache for the ID.
  3. If seen before → discard. Otherwise → display and cache the ID.

Exactly-once delivery is impossible in distributed systems. Network failures, dropped acknowledgments, and crashed nodes make it provably unachievable (Two Generals Problem, FLP impossibility). Design for at-least-once delivery with client-side deduplication.


Additional Components#

ComponentPurpose
Notification TemplatesPredefined formats stored in DB; consistent, error-free, saves time
Notification SettingsPer-user opt-in/opt-out preferences; checked before every notification is sent
Rate LimitingCap notifications per user to avoid overwhelming them
Retry MechanismOn third-party failure, re-add to queue; alert if problem persists
Security (Push)iOS/Android APIs secured with appKey + appSecret; only verified clients can send
MonitoringTrack queue size — large queues indicate slow processing; scale workers up
Event TrackingOpen rate, click rate, engagement metrics; add stage tracking to the pipeline

With all these components in place, the full system looks like this:

final design


Step 6: DB Choices#

ComponentDatabaseWhy
User contact info (email, phone)MySQL / PostgreSQLStructured, relational; ACID guarantees for verified contact data
Device tokensMySQL / PostgreSQLOne-to-many with User; foreign key enforces ownership; indexed lookup by user_id
Notification templatesMySQL / PostgreSQLSmall, infrequently updated structured data; easy to query and manage
Notification settings (opt-in/out)MySQL / PostgreSQLTight relational coupling to User; checked on every send, so indexed reads matter
Notification log (delivery audit, retries)CassandraWrite-heavy — every delivery attempt appends a row; high throughput, time-series-like access pattern
Event tracking (open rate, click rate)Cassandra / ClickHouseAppend-only analytics; optimised for time-range aggregation over large volumes
Cache (user info, device tokens, templates)RedisSub-millisecond lookups; avoids hitting the DB on every notification dispatch
Message queue (worker jobs)KafkaDurable, high-throughput pub/sub; one topic per channel (APNs, FCM, SMS, email)

Why Cassandra for logs? Every notification attempt writes a row — never updates one. At 10M push + 5M email + 1M SMS per day, that's 16M+ appends daily. Cassandra's LSM-tree storage is built for exactly this: high write throughput, append-only, time-ordered queries.

Why Redis in front of Postgres? Device tokens and notification settings are read on every single dispatch. Redis keeps these hot so the relational DB never gets hammered.