Markdown Editor

Notification Service System Design Template (Reference Design)

Push, email, SMS and in-app: templates, preferences and opt-out, priority queues, caps, provider failover, retries, dedup

使用方法: Use when centralising notifications. Separate critical, transactional and marketing traffic from the first page and fill the channel mix to see SMS cost. The main pitfall is letting campaigns share queues with one-time codes.

预览

System Design: [Notification service name]

Reference design for a multi-channel notification service that sends push, email, SMS and in-app messages on behalf of product teams. It covers templates and localisation, user preferences and opt-out, priority queues, per-user rate limits, provider failover, retries with backoff, deduplication and delivery tracking. It follows the structure of the System Design Document template and is pre-filled with a worked design to adapt. Values marked Example are assumptions. Consent and marketing rules vary by jurisdiction; this is an engineering format, not legal advice.

Document control

FieldValue
Document ID[SDD-NOTIF-001]
System / feature[Notification platform]
Version[0.1]
Status[Draft / In review / Approved / Implemented / Superseded]
Author(s)[@author]
Reviewers[@reviewer-1], [@reviewer-2]
Last updatedYYYY-MM-DD
Related[Preference centre PRD], [Privacy review], [Provider contracts]

Approvals

RoleNameDecisionDate
Tech lead[@name][Approved / Changes requested]YYYY-MM-DD
Staff / principal engineer[@name][Decision]YYYY-MM-DD
SRE / operations[@name][Decision]YYYY-MM-DD
Privacy / legal[@name][Decision]YYYY-MM-DD
Marketing operations[@name][Decision]YYYY-MM-DD

Revision history

VersionDateAuthorChange summary
0.1YYYY-MM-DD[@name]First draft for design review
0.2YYYY-MM-DD[@name]Example: Dedicated queue and providers for one-time codes
0.3YYYY-MM-DD[@name]Example: Quiet hours and per-user frequency caps
1.0YYYY-MM-DD[@name]Example: Approved

Contents

1. Overview

1.1 Problem statement

[Example: Nine product teams call email, SMS and push providers directly. Users get the same alert three times, unsubscribe links work for only some emails, a marketing campaign once delayed password-reset emails by 40 minutes, and nobody can answer "did this user receive the message?".]

1.2 Proposed solution

[Example: Product teams publish a notification request (recipient, template, data, category, priority) to one API. The service deduplicates the request, checks the user's preferences, quiet hours and frequency caps, renders the template per channel and locale, and places one job per channel on priority queues. Channel workers send through the primary provider with retries and fail over to a secondary provider. Provider callbacks and webhooks update a delivery log that support and product teams can query.]

1.3 Decisions requested from reviewers

IDQuestionRecommended optionSection
Q-01Example: Separate infrastructure for one-time codes?Yes: own queue, workers and SMS route7.2
Q-02Example: Who owns templates?Product teams, reviewed in a template registry7.1
Q-03Example: Deduplication window?24 h by caller key; 10 min by content hash7.5
Q-04Example: Retention of delivery logs?90 days detailed, 13 months aggregated5.2
[Q-nn][Question][Option][Section]

2. Requirements

2.1 Functional requirements

IDRequirementPriority
FR-01Example: Send via push (iOS, Android, web), email, SMS and in-app inboxMust
FR-02Example: Versioned, localised templates per channelMust
FR-03Example: Respect per-category, per-channel preferences and global opt-outMust
FR-04Example: Quiet hours in the user's time zone for non-urgent categoriesShould
FR-05Example: Per-user frequency caps (for example 3 marketing pushes per day)Must
FR-06Example: Fallback channel when a channel is unavailable for the userShould
FR-07Example: Delivery status per notification and channel, queryable by supportMust
FR-08Example: Scheduled and batched sends (digests, campaigns)Should
[FR-nn][Requirement][Must / Should / Could]

2.2 Non-functional requirements

IDQualityTargetMeasured as
NFR-01Latency (critical)Example: One-time code handed to provider within 2 s p99Accept-to-provider timing
NFR-02Latency (normal)Example: Transactional within 30 s p99Same
NFR-03AvailabilityExample: 99.95% for the accept APISynthetic checks
NFR-04No lossExample: Every accepted request reaches a final state (sent, suppressed or failed)Reconciliation of states
NFR-05No duplicatesExample: Duplicate deliveries below 0.01%Dedup audit
NFR-06ComplianceExample: Unsubscribe honoured within 24 h at most, target 1 minPreference audit
[NFR-nn][Quality][Target][Measurement]

2.3 Out of scope

  • [Example: campaign authoring UI and audience segmentation]
  • [Example: two-way SMS conversations]
  • [Example: voice calls]
  • [Item deferred, with the release that will take it]

3. Capacity estimation

3.1 Assumptions

InputSymbolValueSource
Notification requests per dayNNExample: 500 millionSum of team forecasts
Channel mix: push / email / in-app / SMSExample: 70% / 20% / 8% / 2%Current usage
Campaign burst factorkkExample: 10 (campaigns released at once)Marketing calendar
Delivery log record incl. status eventsssExample: 1 KBSchema in 5.2
Detailed log retentionTTExample: 90 daysDecision Q-04
SMS price per messagepsmsp_{sms}Example: $0.0075 averageProvider contracts

3.2 Throughput

QPSavg=5×10886 400≈5 800 req/s,QPSburst=k×QPSavg≈58 000 req/sQPS_{avg} = \frac{5 \times 10^{8}}{86\,400} \approx 5\,800 \ \text{req/s}, \qquad QPS_{burst} = k \times QPS_{avg} \approx 58\,000 \ \text{req/s}

Per channel at burst:

ChannelShareBurst rateImplication
Push70%~40 600/sProvider connections pooled; batch APIs where offered
Email20%~11 600/sProvider send rate contracts; warm-up of sending IPs and domains
In-app8%~4 600/sOwn store write
SMS2%~1 160/sProvider throughput per sender ID; cost control

Bursts come from campaigns, which are low priority, so they can be smoothed over minutes without breaking any requirement.

3.3 Storage

S=N×s×T=5×108×1 KB×90=45 TB,RF=3:135 TB rawS = N \times s \times T = 5 \times 10^{8} \times 1 \ \text{KB} \times 90 = 45 \ \text{TB}, \qquad \text{RF} = 3: 135 \ \text{TB raw}

3.4 SMS cost

Csms=0.02×5×108×30×0.0075≈$2.25 million/monthC_{sms} = 0.02 \times 5 \times 10^{8} \times 30 \times 0.0075 \approx \$2.25 \ \text{million/month}

This is the dominant cost and justifies push-first fallback ordering and strict SMS eligibility rules (7.2).

3.5 Summary

QuantityEstimateDesign consequence
Requests~5 800/s average, ~58 000/s campaign burstPriority queues; campaigns throttled
Delivery log~45 TB (90 days), 135 TB rawWide-column store with TTL
SMS cost~$2.25M/month at assumed mixChannel policy and fallback order are cost controls

4. API design

4.1 Endpoints

MethodPathPurposeAuthIdempotent
POST/v1/notificationsRequest a notification for one userService tokenRequired Idempotency-Key
POST/v1/notifications/batchUp to 1 000 recipients, same templateService tokenRequired Idempotency-Key
GET/v1/notifications/{id}Status per channel with eventsService or support tokenYes
GET / PUT/v1/users/{id}/preferencesRead or update preferencesUser or service tokenYes
GET/v1/users/{id}/inbox?cursor=In-app inboxUser tokenYes
POST/v1/providers/{p}/callbacksProvider delivery eventsProvider signatureDedup by provider event ID

4.2 Request and response examples

A product team asks for a transactional notification; the service decides channels from the category and user preferences:

POST /v1/notifications HTTP/1.1
Content-Type: application/json
Idempotency-Key: order-shipped:ord_88123

{"userId": "u_4410", "category": "order_updates", "priority": "transactional", "template": "order_shipped", "templateVersion": 3, "data": {"orderId": "ord_88123", "carrier": "DHL", "eta": "2026-03-16"}, "channels": ["push", "email"]}
HTTP/1.1 202 Accepted
Content-Type: application/json

{"id": "ntf_01JA9", "state": "accepted", "dedupOf": null}

4.3 Errors and limits

StatusProblem typeWhen
400template-data-invalidData does not match the template's schema
404template-not-foundTemplate or version missing or not approved
409duplicateSame idempotency key with different body
429caller-rate-limitedCalling service exceeded its quota

Suppression by preference is not an error: the request is accepted and its final state becomes suppressed with a reason.

5. Data model

5.1 Entities

A notification request produces one delivery per channel; each delivery records attempts and provider events.

5.2 Storage choices

DataStoreKeyMain queriesWhy
TemplatesRelational database + cachetemplate_id, versionGet approved versionSmall, reviewed, versioned
Preferences, contact pointsRelational database + cacheuser_idAll preferences for a userConsistency for opt-out
Notifications, deliveries, eventsExample: Wide-column store with 90-day TTLnotification_id; secondary by user_id + dayStatus by ID; history by userHigh write rate, time-bounded
Dedup keysIn-memory store with TTLcaller:idem_key, content hashExists?Fast check, short lifetime
In-app inboxWide-column storeuser_id, clustered by timeLatest items, unread countPer-user range reads
QueuesDurable message brokerTopic per channel and priorityConsume in order of priorityBuffer bursts, retries

6. High-level design

The accept path is short and synchronous; everything after the queue is asynchronous and retryable.

ComponentResponsibilityScales by
Notification APIAuthenticate caller, validate, dedup by key, enqueue, return 202Stateless replicas
OrchestratorPreferences, caps, quiet hours, channel selection, rendering, per-channel jobsConsumer partitions by user_id
Channel workersSend through providers, retry, fail over, record attemptsWorker pools per channel and priority
Callback receiverVerify provider signatures, map events to delivery statesStateless replicas
[Component][Responsibility][Scaling unit]

7. Detailed design

7.1 Templates

Templates are stored per channel and locale in a registry, written in a logic-less language (variables, conditionals, loops only), validated against a JSON Schema for their data, and require review before activation. Email templates are rendered to HTML and plain text; push templates are checked against platform payload limits; SMS templates are checked for segment count, since a message above 160 GSM-7 characters (70 in UCS-2) is billed as several segments.

7.2 Priorities and channel policy

PriorityExamplesQueueQuiet hoursCapsChannels
P0 criticalOne-time codes, security alertsDedicated, never shared with campaignsIgnoredAbuse limits onlySMS or email as requested; push for app approvals
P1 transactionalReceipts, shipping, mentionsShared transactionalRespected for push, not emailPer categoryPush first, email fallback
P2 marketingCampaigns, digests, recommendationsThrottledRespectedStrict (Example: 3 push/day, 1 email/day)Only with explicit opt-in where required

Workers consume P0 first, then P1, then P2, with P2 limited to a configurable share of provider capacity so campaigns can never starve transactional mail.

7.3 Preferences, consent and opt-out

  • Preferences are stored per category and channel, defaulting by category (marketing off unless consent is recorded where the market requires it).
  • Every email carries a one-click unsubscribe (the List-Unsubscribe and List-Unsubscribe-Post headers per RFC 8058) and SMS honours STOP keywords through the provider's opt-out handling.
  • Opt-out writes are synchronous to the preference store, and the orchestrator reads preferences at send time, not at request time, so a scheduled campaign respects an unsubscribe made after scheduling.
  • Hard bounces and invalid push tokens mark the contact point invalid.

7.4 Send path with retries and failover (critical path)

The sequence below shows a transactional push with an email fallback.

Retry delay for attempt nn uses capped exponential backoff with full jitter:

delayn=rand(0, min⁡(cap, base×2n))\text{delay}_n = \text{rand}\left(0,\ \min(\text{cap},\ \text{base} \times 2^{n})\right)

With base 1 s and cap 5 min, attempt 6 waits up to 64 s and attempt 9 up to 5 minutes. P0 jobs use base 200 ms and give up after 30 s, because a late one-time code is useless; the user can request another.

Provider failover uses a circuit breaker per provider and region: when the error rate exceeds 20% over 1 minute, new sends route to the secondary until probes succeed. Only errors that are safe to retry (timeouts, 5xx, throttling) trigger a retry; permanent errors (invalid number, unsubscribed address) end the delivery as failed.

7.5 Deduplication

LayerKeyWindowCatches
Caller idempotencycaller:Idempotency-Key24 hRetries by the calling service
Content hashuser + template + data hash10 minTwo services emitting the same event
Worker senddelivery_id + attempt with provider idempotency where offeredPer deliveryWorker crash after send, before ack

A residual duplicate is possible when a provider accepts a send but the response is lost and the provider offers no idempotency. This is accepted and measured (NFR-05).

7.6 Delivery tracking and consistency model

Each delivery moves through states driven by workers and provider callbacks; callbacks can arrive late or out of order, so transitions only move forward.

The delivery log is eventually consistent with providers: "delivered" may appear minutes after "sent", and some channels (SMS in some countries, push on some platforms) never report delivery. Preferences are strongly consistent (read from the primary on opt-out paths). Open tracking in email is approximate because many clients pre-fetch or block images.

8. Scalability and performance

ConcernApproachLimit before redesign
Campaign burstsP2 throttled to share of capacity; scheduled release over a windowProvider contract rates
Provider rate limitsToken buckets per provider account in workersContracted throughput
Per-user capsCounters per user and category in in-memory store with daily TTL[Limit]
Hot users (shared accounts, bots)Abuse limits per recipient100 notifications/hour per user
Delivery log writesWide-column store, TTL, partition by dayExample: ~200 000 writes/s per cluster

Latency budget for a P0 one-time code (target 2 s to provider):

StepBudget
API accept and dedup50 ms
Intake to orchestrator200 ms
Preferences and rendering100 ms
P0 queue to worker200 ms
Provider API call800 ms
Headroom650 ms

9. Reliability, security and operations

9.1 Failure modes

FailureImpactDetectionMitigation
Primary SMS provider outageCodes not deliveredCircuit breaker, delivery receipts dropFailover to secondary route per country
Email IP reputation dropMail to spamInbox placement and complaint ratesSeparate IPs and domains for transactional and marketing
Queue backlogDelaysAge of oldest message per queueAutoscale workers; shed P2 first
Preference store unavailableCannot check consentError rateP2 paused (fail closed); P0 and P1 proceed with cached preferences
Callback floodLog write pressureCallback rateBuffer in queue; batch writes
[Failure][Impact][Signal][Mitigation]

9.2 Security, privacy and abuse

ThreatControl
Phishing using our domainsSPF, DKIM and DMARC with enforcement; BIMI optional
SMS pumping fraud (fake numbers generating paid traffic)Per-country allowlists, velocity limits on code requests, anomaly alerts on cost
Sensitive data in notificationsTemplate review; no secrets in push previews; codes only in P0 templates
Forged provider callbacksSignature verification and replay protection
Data retentionDelivery logs deleted after 90 days; message bodies not stored after send unless required
[Threat][Control]

9.3 Observability

SignalMetric or logAlert threshold
P0 latencyAccept to provider p99Above 2 s for 5 min
Queue ageOldest message per priorityP1 above 30 s, P2 above 30 min
Provider healthError rate per provider and countryAbove 5% for 5 min
Delivery rateDelivered / sent per channelDrop of 5 points vs. baseline
Spam complaintsComplaints / delivered emailAbove 0.1%
SMS spendCost per hour per countryAbove 2x forecast

9.4 Cost estimate

ItemUnit costQuantityMonthly cost
SMSExample: $0.0075 averageExample: 300 millionExample: $2.25 million
Email[$/1 000]Example: 3 billion[$]
PushPlatform services usually freeExample: 10.5 billion[$]
Delivery log storage[$/TB-month]Example: 135 TB raw[$]
Compute and queues[$/month][n][$]
Total[$]

10. Trade-offs and alternatives

DecisionOptionsChosenWhyWhat we give up
OwnershipEach team integrates providers; central serviceCentral serviceOne place for consent, caps and trackingTeams depend on platform roadmap
QueueingSingle queue; per-channel; per-channel and priorityPer channel and priorityCampaigns cannot delay codesMore queues to operate
Preference check timeAt request; at sendAt sendHonours late opt-outsExtra read per send
ProvidersSingle per channel; multi-provider with failoverMulti-provider for SMS and emailOutages and country coverageIntegration and contract overhead
Delivery guaranteeAt-most-once; at-least-once with dedupAt-least-once with dedupNo silent loss of important messagesRare duplicates remain possible
Build or buyHosted notification platform; in-house[In-house / Hosted][Reason: cost at volume, data residency, control][Cost]

11. Delivery plan and risks

11.1 Milestones

MilestoneScopeExit criterionTarget date
M1API, email and push, preferences, delivery logTwo teams migrated; zero lost requests in reconciliationYYYY-MM-DD
M2P0 path with SMS and failoverFailover drill under 1 minYYYY-MM-DD
M3Caps, quiet hours, in-app inboxComplaint rate down 30% for migrated teamsYYYY-MM-DD
M4All teams migrated; direct provider keys revokedNo direct provider traffic for 30 daysYYYY-MM-DD

11.2 Rollout and rollback

[Example: Migrate one category at a time behind a flag in the calling service. During migration, the old path stays available; rollback flips the flag. Provider credentials for direct use are revoked only after M4.]

11.3 Risks

IDRiskLikelihoodImpactMitigationOwner
R-01Example: Central service outage stops all notificationsLowHighMulti-zone, P0 isolation, runbooks[@name]
R-02Example: SMS fraud spikes costMediumHighCountry allowlist, velocity limits, spend alerts[@name]
R-03Example: Teams bypass the platformMediumMediumRevoke direct credentials; good SDK[@name]
[R-nn][Risk][L / M / H][L / M / H][Mitigation][@name]

12. Appendices

12.1 Open questions

IDQuestionOwnerDue
OQ-01Example: Do we need per-tenant sender domains for B2B customers?[@name]YYYY-MM-DD
OQ-02Example: Which countries require pre-registered SMS sender IDs?[@name]YYYY-MM-DD
[OQ-nn][Question][@name]YYYY-MM-DD

12.2 Glossary

TermMeaning
TransactionalMessage triggered by a user action or account event
SuppressionDecision not to send because of preference, cap or invalid contact
Hard bouncePermanent email delivery failure
Circuit breakerMechanism that stops calls to a failing dependency for a time
Quiet hoursUser-local period when non-urgent notifications are held
[Term][Meaning]

12.3 References

ReferenceUse
RFC 8058 Signaling One-Click Functionality for List Email HeadersOne-click unsubscribe
RFC 7489 DMARC; RFC 7208 SPF; RFC 6376 DKIMEmail authentication
Apple Push Notification service, Firebase Cloud Messaging, Web Push (RFC 8030)Push channels
[Provider documentation and contracts]Rates and callbacks

Standard and licence note

Based on common industry practice for notification platforms and on the specifications cited in 12.3. This is a reference design to adapt, with illustrative numbers and prices; it is a format, not legal advice on consent or marketing rules. Original text written for this collection. Template licence: MIT.

相关模板