Notification Service System Design
Problem Statement
Section titled “Problem Statement”Design a Notification Service that can send notifications through multiple channels:
- SMS
- Push Notifications
- In-app notifications
Example use cases:
- Amazon order updates
- OTP messages
- Social media notifications
- Payment alerts
The service should support millions of notifications per day.
Functional Requirements
Section titled “Functional Requirements”- Send notifications via multiple channels.
- Support Email, SMS, Push, and In-app notifications.
- Support template-based notifications.
- Allow user notification preferences.
- Retry failed notifications.
- Support bulk notifications.
- Provide notification history.
Non-Functional Requirements
Section titled “Non-Functional Requirements”- High scalability
- Low latency
- Fault tolerance
- Reliable delivery
- Retry support
- Idempotency
High-Level Design
Section titled “High-Level Design”flowchart TD
A["Client Services<br/>Order Service<br/>Payment Service"] --> B[Notification API]
B --> C[Notification Service]
C --> D[Kafka Queue]
C --> E[Template Service]
C --> F[User Preference Service]
D --> G[Notification Workers]
G --> H[Email Service]
G --> I[SMS Service]
G --> J[Push Service]
H --> K[SendGrid]
I --> L[Twilio]
J --> M[Firebase]
Core Components
Section titled “Core Components”Notification API
Section titled “Notification API”Entry point responsible for:
- Receiving notification requests
- Validating requests
- Publishing events to the queue
POST /notificationsMessage Queue
Section titled “Message Queue”Examples:
- Kafka
- RabbitMQ
- AWS SQS
Purpose:
- Asynchronous processing
- Decouple producers and consumers
- Handle traffic spikes
Notification Workers
Section titled “Notification Workers”Responsibilities:
- Fetch user preferences
- Fetch templates
- Render message
- Send notifications
- Retry on failure
Template Service
Section titled “Template Service”Stores reusable templates.
Example:
Hi {name}, your order {orderId} has been shippedUser Preference Service
Section titled “User Preference Service”Users configure enabled channels.
UserPreference--------------userIdemailEnabledsmsEnabledpushEnabledChannel Services
Section titled “Channel Services”Separate integrations for each delivery channel.
- Email -> SendGrid / SES
- SMS -> Twilio
- Push -> Firebase
Database Design
Section titled “Database Design”Notification
Section titled “Notification”Notification------------iduser_idtypechannelstatuspayloadcreated_atsent_atretry_countTemplate
Section titled “Template”Template--------template_idchanneltemplate_nametemplate_bodylanguagecreated_atUserPreference
Section titled “UserPreference”UserPreference--------------user_idemail_enabledsms_enabledpush_enabledupdated_atLow-Level Design
Section titled “Low-Level Design”NotificationRequest
Section titled “NotificationRequest”class NotificationRequest {
String userId; NotificationType type; Channel channel; Map<String, String> data;}NotificationService
Section titled “NotificationService”interface NotificationService {
void sendNotification(NotificationRequest request);}Implementation:
class NotificationServiceImpl implements NotificationService {
QueuePublisher queuePublisher;
public void sendNotification(NotificationRequest request) {
queuePublisher.publish(request); }}Worker
Section titled “Worker”class NotificationWorker {
TemplateService templateService; ChannelFactory channelFactory;
public void process(NotificationRequest request) {
Template template = templateService.getTemplate(request.type);
String message = TemplateEngine.render( template, request.data );
Channel channel = channelFactory.getChannel(request.channel);
channel.send(message); }}Channel Interface
Section titled “Channel Interface”interface Channel {
void send(String message);}Email Channel
Section titled “Email Channel”class EmailChannel implements Channel {
EmailProvider provider;
public void send(String message) {
provider.sendEmail(message); }}SMS Channel
Section titled “SMS Channel”class SMSChannel implements Channel {
SMSProvider provider;
public void send(String message) {
provider.sendSMS(message); }}Channel Factory
Section titled “Channel Factory”class ChannelFactory {
public Channel getChannel(ChannelType type) {
switch(type) {
case EMAIL: return new EmailChannel();
case SMS: return new SMSChannel();
case PUSH: return new PushChannel(); } }}Pattern used: Factory Pattern
API Design
Section titled “API Design”Send Notification
Section titled “Send Notification”POST /notificationsRequest:
{ "userId": "123", "type": "ORDER_SHIPPED", "channel": "EMAIL", "data": { "name": "John", "orderId": "A123" }}Notification History
Section titled “Notification History”GET /notifications/{userId}Retry Strategy
Section titled “Retry Strategy”Use exponential backoff.
1st retry -> 1 min2nd retry -> 5 min3rd retry -> 15 minRetry topic:
notification_retryIdempotency
Section titled “Idempotency”Use a unique notification ID.
if (notificationAlreadySent) skip;Scaling
Section titled “Scaling”- Horizontally scale workers.
- Partition Kafka topics.
- Use worker pools.
- Batch large campaigns (e.g., 10M notifications).
Failure Handling
Section titled “Failure Handling”Dead Letter Queue:
notification_dlqUsed after maximum retries are exhausted.
Observability
Section titled “Observability”Monitor:
- Notifications per second
- Failure rate
- Retry count
- Provider latency
Tools:
- Prometheus
- Grafana
- ELK
Real-World Systems
Section titled “Real-World Systems”- Amazon SNS
- Firebase Cloud Messaging
- Uber Notification Platform
- LinkedIn Kafka-based notification systems
Common Interview Follow-ups
Section titled “Common Interview Follow-ups”- Prevent notification spam.
- Handle 10M push notifications in one minute.
- Prioritize OTP notifications over marketing notifications.
- Ensure exactly-once delivery.
- Support multi-language notifications.
Summary
Section titled “Summary”- Asynchronous processing with queues enables scalability and resilience.
- Separate workers and channel integrations isolate provider-specific logic.
- Templates and user preferences improve flexibility and personalization.
- Retries, DLQs, and idempotency improve reliability.
- Horizontal scaling and partitioned queues support very high throughput.