Bird
Raised Fist0
LLDsystem_design~25 mins

Notification to all parties in LLD - System Design Exercise

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Design: Notification to All Parties System
Design covers notification sending, scheduling, retry, and status tracking. Does not cover user management or content creation for notifications.
Functional Requirements
FR1: Send notifications to multiple parties (users, admins, external systems) simultaneously
FR2: Support multiple notification channels: email, SMS, push notifications
FR3: Allow scheduling notifications for future delivery
FR4: Provide delivery status tracking for each notification
FR5: Support retry mechanism for failed notifications
FR6: Allow priority levels for notifications (high, medium, low)
FR7: Handle up to 100,000 notifications per hour
FR8: Ensure notifications are delivered within 5 seconds for real-time messages
Non-Functional Requirements
NFR1: System must be highly available with 99.9% uptime
NFR2: Latency for sending notifications should be under 5 seconds for real-time notifications
NFR3: Support horizontal scaling to handle increased load
NFR4: Ensure data privacy and security for user contact information
NFR5: Design must be modular to add new notification channels easily
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
❓ Question 5
❓ Question 6
❓ Question 7
Key Components
API Gateway or Notification Request Handler
Notification Scheduler
Message Queue for decoupling
Notification Dispatcher for each channel
Delivery Status Tracker
Database for storing notification metadata and status
Retry Mechanism
Monitoring and Alerting
Design Patterns
Publish-Subscribe pattern for distributing notifications
Queue-based asynchronous processing
Circuit Breaker for external channel failures
Bulkhead pattern to isolate failures
Priority Queue for handling notification priorities
Reference Architecture
  +-------------------+       +------------------+       +---------------------+
  | Notification      |       | Notification     |       | Notification        |
  | Request Handler   | ----> | Scheduler        | ----> | Message Queue       |
  +-------------------+       +------------------+       +---------------------+
                                                                |
                                                                v
  +---------------------+    +---------------------+    +---------------------+
  | Email Dispatcher    |    | SMS Dispatcher      |    | Push Notification   |
  | (SMTP or API)       |    | (SMS Gateway API)   |    | Dispatcher          |
  +---------------------+    +---------------------+    +---------------------+
            |                        |                          |
            v                        v                          v
  +---------------------+    +---------------------+    +---------------------+
  | Delivery Status     |    | Delivery Status     |    | Delivery Status     |
  | Tracker DB          |    | Tracker DB          |    | Tracker DB          |
  +---------------------+    +---------------------+    +---------------------+

  Retry Mechanism monitors failures and resubmits messages.

  Monitoring & Alerting watches system health and failures.
Components
Notification Request Handler
REST API Server (Node.js/Express or Python Flask)
Receives notification requests from clients and validates them
Notification Scheduler
Scheduler Service (Cron jobs or Quartz Scheduler)
Schedules notifications for immediate or future delivery
Message Queue
RabbitMQ or Apache Kafka
Decouples request handling from notification dispatching for scalability
Notification Dispatchers
Separate microservices for Email, SMS, Push (using SMTP, Twilio API, Firebase Cloud Messaging)
Send notifications through respective channels
Delivery Status Tracker
Relational DB (PostgreSQL) or NoSQL (MongoDB)
Stores notification metadata and delivery status updates
Retry Mechanism
Background worker or scheduled job
Retries failed notifications based on configurable policies
Monitoring and Alerting
Prometheus + Grafana or Cloud monitoring tools
Tracks system health, failures, and performance metrics
Request Flow
1. Client sends notification request to Notification Request Handler via REST API.
2. Request Handler validates and stores notification metadata in DB.
3. If scheduled for future, Notification Scheduler queues the notification at the right time.
4. Notification Scheduler pushes notification message to Message Queue.
5. Notification Dispatchers consume messages from the queue based on channel type.
6. Dispatchers send notifications via external services (SMTP, SMS gateway, push service).
7. Dispatchers update Delivery Status Tracker with success or failure.
8. Retry Mechanism monitors failures and resubmits messages to the queue.
9. Monitoring system collects metrics and alerts on failures or delays.
Database Schema
Entities: - Notification: id (PK), content, channel, priority, scheduled_time, created_at - Recipient: id (PK), notification_id (FK), party_id, contact_info - DeliveryStatus: id (PK), recipient_id (FK), status (pending, sent, failed), last_attempt_time, retry_count Relationships: - Notification 1:N Recipient - Recipient 1:1 DeliveryStatus
Scaling Discussion
Bottlenecks
Message Queue throughput limits under high load
Notification Dispatchers overwhelmed by large volume
Database write/read bottlenecks for delivery status tracking
Retry mechanism causing duplicate or delayed notifications
External channel rate limits or failures
Solutions
Partition or shard message queues; use multiple queues per channel or priority
Scale dispatchers horizontally with load balancing
Use database sharding or caching for delivery status; consider NoSQL for high write throughput
Implement idempotency and exponential backoff in retry logic
Use circuit breakers and fallback channels; monitor external service limits
Interview Tips
Time: Spend 10 minutes understanding requirements and clarifying scope, 20 minutes designing components and data flow, 10 minutes discussing scaling and trade-offs, 5 minutes summarizing.
Clarify notification channels and delivery guarantees
Explain asynchronous processing with message queues
Discuss retry and failure handling strategies
Highlight modular design for adding new channels
Address scaling challenges and solutions
Mention security and privacy considerations

Practice

(1/5)
1.

What is the main purpose of a notification system that sends messages to all parties?

easy
A. To quickly share important messages with everyone involved
B. To store large amounts of data securely
C. To perform complex calculations on user data
D. To create user profiles and preferences

Solution

  1. Step 1: Understand the role of notifications

    Notifications are designed to deliver messages to users or parties quickly and efficiently.
  2. Step 2: Identify the main goal

    The main goal is to share important information with all involved parties without delay.
  3. Final Answer:

    To quickly share important messages with everyone involved -> Option A
  4. Quick Check:

    Notification purpose = quick message sharing [OK]
Hint: Notifications = fast message delivery to all involved [OK]
Common Mistakes:
  • Confusing notifications with data storage
  • Thinking notifications perform data processing
  • Assuming notifications create user profiles
2.

Which of the following is the correct way to represent a notification service that sends messages to all parties in pseudocode?

function notifyAll(parties, message) {
  for (let i = 0; i < parties.length; i++) {
    parties[i].send(message);
  }
}
easy
A. Call send(message) once without looping
B. Send message only to the first party
C. Loop through parties and call send(message) on each
D. Loop through parties but do not send any message

Solution

  1. Step 1: Analyze the pseudocode loop

    The code loops through each party in the parties list using a for loop.
  2. Step 2: Check the send method call

    Inside the loop, it calls send(message) on each party, ensuring all get notified.
  3. Final Answer:

    Loop through parties and call send(message) on each -> Option C
  4. Quick Check:

    Loop + send call = notify all [OK]
Hint: Loop through all parties to send message [OK]
Common Mistakes:
  • Sending message only once
  • Not looping through all parties
  • Calling send outside the loop
3.

Consider this code snippet for notifying parties:

parties = ["Alice", "Bob", "Charlie"]
function notifyAll(parties, message) {
  let notified = []
  for (const person of parties) {
    notified.push(person + ": " + message)
  }
  return notified
}

console.log(notifyAll(parties, "Meeting at 5 PM"))

What will be the output?

medium
A. ["Alice: Meeting at 5 PM", "Bob: Meeting at 5 PM", "Charlie: Meeting at 5 PM"]
B. ["Meeting at 5 PM", "Meeting at 5 PM", "Meeting at 5 PM"]
C. ["Alice", "Bob", "Charlie"]
D. Error: notifyAll is not defined

Solution

  1. Step 1: Understand the loop behavior

    The function loops over each person in parties and creates a string combining their name and the message.
  2. Step 2: Check the returned list

    The notified list contains strings like "Alice: Meeting at 5 PM" for each party.
  3. Final Answer:

    ["Alice: Meeting at 5 PM", "Bob: Meeting at 5 PM", "Charlie: Meeting at 5 PM"] -> Option A
  4. Quick Check:

    Loop + string concat = list of personalized messages [OK]
Hint: Each party gets message with their name prefixed [OK]
Common Mistakes:
  • Returning only messages without names
  • Returning original party list
  • Assuming function is undefined
4.

Identify the bug in this notification function and select the fix:

function notifyAll(parties, message) {
  for (let i = 0; i < parties.length; i++) {
    parties.send(message)
  }
}
medium
A. Remove the loop and call parties.send(message) once
B. Add a return statement inside the loop
C. Change i < parties.length to i <= parties.length
D. Change parties.send(message) to parties[i].send(message)

Solution

  1. Step 1: Identify incorrect method call

    The code calls send on the entire parties array instead of individual party objects.
  2. Step 2: Fix by indexing the array

    Use parties[i].send(message) to call send on each party in the loop.
  3. Final Answer:

    Change parties.send(message) to parties[i].send(message) -> Option D
  4. Quick Check:

    Call send on each party object [OK]
Hint: Call send on parties[i], not parties array [OK]
Common Mistakes:
  • Calling send on the whole array
  • Using wrong loop condition
  • Adding unnecessary return inside loop
5.

You are designing a notification system to alert all parties involved in a project. Which design choice best ensures scalability and reliability?

  • A. Use a single server to send notifications sequentially to all parties.
  • B. Send notifications only to a random subset of parties to reduce load.
  • C. Store all notifications in a database and send them manually when needed.
  • D. Use a message queue to distribute notification tasks to multiple worker servers.
hard
A. Single server sending sequentially
B. Message queue with multiple workers
C. Store notifications and send manually
D. Send to random subset to reduce load

Solution

  1. Step 1: Evaluate single server approach

    Sending sequentially from one server limits scalability and can cause delays or failures.
  2. Step 2: Consider message queue with workers

    Using a message queue allows distributing notification tasks to multiple workers, improving scalability and reliability.
  3. Step 3: Assess other options

    Storing notifications for manual sending is slow; sending to random subset misses parties.
  4. Final Answer:

    Message queue with multiple workers -> Option B
  5. Quick Check:

    Queue + workers = scalable, reliable notifications [OK]
Hint: Use queues and workers for scalable notifications [OK]
Common Mistakes:
  • Relying on single server for all notifications
  • Sending notifications manually
  • Skipping parties to reduce load