📂
Interview Prep
LLD Questions
LLD Questions
  • LLD Introduction
  • Meeting Scheduler
  • Distributed Cache System
  • Rate limiter
  • Multi-Tier Elevator System
  • MyGate System
  • Uber Sytem LLD
  • Parking Lot System
  • Online book store
  • Library Management System
  • Movie Ticket Booking System
  • Hotel Management System
  • File Storage System
    • Solution 2
  • Chat Application
  • Social Media Platform
  • Notification System
  • Airline Reservation System
  • ATM System
  • E-commerce Website
  • Food Delivery System
  • Shopping Cart System
  • URL Shortener system
  • Chess Tournament System
  • Threading therory
  • OOP questions
Powered by GitBook
On this page
  • 1. Overview
  • 2. Detailed Java Code
  • 3. Explanation of Key Points & Design Patterns
  • 4. Conclusion

Notification System

Below is a comprehensive low-level design (LLD) for a Notification System in Java. In this design we use:

  • Singleton Pattern: To ensure a single instance of the NotificationManager.

  • Factory Pattern: To centralize the creation of Notification objects.

  • Strategy Pattern: To choose the appropriate delivery mechanism (Email, SMS, Push) at runtime.

  • Observer Pattern: To decouple notification events from additional processing (for example, logging or other side effects).

We also address key requirements:

  • Modeling Notifications and Users: Representing users with various contact details and notifications with type and content.

  • Delivery Mechanism: Using different strategies to deliver notifications via email, SMS, or push.

  • Reliable Delivery: Leveraging asynchronous execution (using ExecutorService) and potential retry mechanisms.


1. Overview

  • User: Represents a recipient with contact details (email, phone, device token).

  • Notification: Encapsulates a message with content and type (EMAIL, SMS, PUSH).

  • NotificationManager: Centralizes sending notifications reliably using asynchronous delivery and selects the proper delivery strategy.

  • NotificationFactory: Creates notifications in a standardized manner.

  • Delivery Strategies: Different implementations (Email, SMS, Push) encapsulate the logic of sending a notification.

  • Observer: Allows additional components (like a logger) to observe notification events without coupling to the NotificationManager.


2. Detailed Java Code

import java.util.*;
import java.util.concurrent.*;

// --------------------- ENTITIES ---------------------

// User entity representing a recipient of notifications.
class User {
    private String id;
    private String name;
    private String email;
    private String phone;
    private String deviceToken; // For push notifications

    public User(String id, String name, String email, String phone, String deviceToken) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.phone = phone;
        this.deviceToken = deviceToken;
    }

    // Getters for user contact details.
    public String getEmail() { return email; }
    public String getPhone() { return phone; }
    public String getDeviceToken() { return deviceToken; }
    public String getName() { return name; }
}

// Notification entity representing a message to be delivered.
class Notification {
    private String notificationId;
    private String content;
    private NotificationType type;
    private boolean delivered;

    public Notification(String content, NotificationType type) {
        // Factory Pattern creates these objects uniformly.
        this.notificationId = UUID.randomUUID().toString();
        this.content = content;
        this.type = type;
        this.delivered = false;
    }

    public String getContent() { return content; }
    public NotificationType getType() { return type; }
    public void markAsDelivered() { delivered = true; }
    public boolean isDelivered() { return delivered; }
}

// Enum representing notification types.
enum NotificationType {
    EMAIL, SMS, PUSH
}

// --------------------- FACTORY ---------------------

// Factory Pattern: Centralizes creation of Notification objects.
class NotificationFactory {
    public static Notification createNotification(String content, NotificationType type) {
        return new Notification(content, type);
    }
}

// --------------------- STRATEGY INTERFACE ---------------------

// Strategy Pattern: Defines a contract for delivery mechanisms.
interface DeliveryStrategy {
    // Deliver the notification to the given user.
    void deliver(Notification notification, User user);
}

// Concrete strategy for Email delivery.
class EmailDeliveryStrategy implements DeliveryStrategy {
    @Override
    public void deliver(Notification notification, User user) {
        // Simulate sending an email.
        System.out.println("Sending EMAIL to " + user.getEmail() + ": " + notification.getContent());
        // Here you would integrate with an email service API.
    }
}

// Concrete strategy for SMS delivery.
class SMSDeliveryStrategy implements DeliveryStrategy {
    @Override
    public void deliver(Notification notification, User user) {
        // Simulate sending an SMS.
        System.out.println("Sending SMS to " + user.getPhone() + ": " + notification.getContent());
        // Here you would integrate with an SMS gateway.
    }
}

// Concrete strategy for Push notification delivery.
class PushDeliveryStrategy implements DeliveryStrategy {
    @Override
    public void deliver(Notification notification, User user) {
        // Simulate sending a push notification.
        System.out.println("Sending PUSH to device " + user.getDeviceToken() + ": " + notification.getContent());
        // Here you would integrate with a push notification service.
    }
}

// --------------------- OBSERVER INTERFACE ---------------------

// Observer Pattern: Allows decoupled components to react to notification events.
interface NotificationObserver {
    void update(Notification notification, User user);
}

// Concrete observer that logs notifications.
class LoggingObserver implements NotificationObserver {
    @Override
    public void update(Notification notification, User user) {
        System.out.println("Logging: Notification [" + notification.getNotificationId() + "] sent to " + user.getName());
    }
}

// --------------------- NOTIFICATION MANAGER ---------------------

// Singleton Pattern: Ensures a single instance of NotificationManager.
// Also uses the Strategy Pattern to choose the proper delivery mechanism.
// Additionally, incorporates the Observer Pattern to notify registered observers.
class NotificationManager {
    private static NotificationManager instance;
    
    // ExecutorService for asynchronous and reliable delivery.
    private ExecutorService executor = Executors.newFixedThreadPool(5);
    
    // Map associating notification types with their delivery strategies.
    private Map<NotificationType, DeliveryStrategy> strategyMap;
    
    // List of observers for notification events.
    private List<NotificationObserver> observers;

    // Private constructor (Singleton Pattern).
    private NotificationManager() {
        strategyMap = new HashMap<>();
        observers = new ArrayList<>();
        // Configure strategies.
        strategyMap.put(NotificationType.EMAIL, new EmailDeliveryStrategy());
        strategyMap.put(NotificationType.SMS, new SMSDeliveryStrategy());
        strategyMap.put(NotificationType.PUSH, new PushDeliveryStrategy());
    }

    // Public method to retrieve the singleton instance.
    public static synchronized NotificationManager getInstance() {
        if (instance == null) {
            instance = new NotificationManager();
        }
        return instance;
    }

    // Method to register an observer (Observer Pattern).
    public void registerObserver(NotificationObserver observer) {
        observers.add(observer);
    }
    
    // Method to notify all observers about a delivered notification.
    private void notifyObservers(Notification notification, User user) {
        for (NotificationObserver observer : observers) {
            observer.update(notification, user);
        }
    }

    // Reliable, asynchronous delivery of notifications.
    public void sendNotification(final Notification notification, final User user) {
        // Strategy Pattern: Select the delivery mechanism based on notification type.
        DeliveryStrategy strategy = strategyMap.get(notification.getType());
        if (strategy == null) {
            System.out.println("No delivery strategy found for type: " + notification.getType());
            return;
        }

        // Submit delivery task asynchronously.
        executor.submit(() -> {
            try {
                // Attempt to deliver the notification.
                strategy.deliver(notification, user);
                notification.markAsDelivered();
                // After delivery, notify observers (Observer Pattern).
                notifyObservers(notification, user);
            } catch (Exception e) {
                // In case of delivery failure, log and potentially retry.
                System.out.println("Delivery failed for " + user.getName() + ": " + e.getMessage());
                // Retry logic can be implemented here.
            }
        });
    }

    // Shutdown the executor service gracefully.
    public void shutdown() {
        executor.shutdown();
    }
}

// --------------------- MAIN APPLICATION ---------------------

public class NotificationSystemApp {
    public static void main(String[] args) {
        // Create sample users.
        User alice = new User(UUID.randomUUID().toString(), "Alice", "alice@example.com", "1234567890", "device_token_alice");
        User bob = new User(UUID.randomUUID().toString(), "Bob", "bob@example.com", "0987654321", "device_token_bob");

        // Create notifications using the Factory Pattern.
        Notification emailNotification = NotificationFactory.createNotification("Welcome to our service, Alice!", NotificationType.EMAIL);
        Notification smsNotification = NotificationFactory.createNotification("Your verification code is 1234", NotificationType.SMS);
        Notification pushNotification = NotificationFactory.createNotification("You have a new friend request", NotificationType.PUSH);

        // Get the NotificationManager singleton instance.
        NotificationManager manager = NotificationManager.getInstance();

        // Register a LoggingObserver to observe notification events (Observer Pattern).
        manager.registerObserver(new LoggingObserver());

        // Send notifications reliably using asynchronous execution.
        manager.sendNotification(emailNotification, alice);
        manager.sendNotification(smsNotification, bob);
        manager.sendNotification(pushNotification, alice);

        // Allow some time for asynchronous tasks to complete.
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Shutdown the NotificationManager's executor.
        manager.shutdown();
    }
}

3. Explanation of Key Points & Design Patterns

Modeling Notifications and Users

  • User Class: Contains contact details (email, phone, device token) needed for various delivery channels.

  • Notification Class: Encapsulates notification content and type, with a unique ID and delivery status.

Delivery Mechanism

  • DeliveryStrategy Interface (Strategy Pattern): Defines a common method to deliver notifications.

  • Concrete Strategies: EmailDeliveryStrategy, SMSDeliveryStrategy, and PushDeliveryStrategy implement the delivery logic for each channel.

Reliable Delivery

  • NotificationManager (Singleton Pattern): Manages sending notifications reliably using an ExecutorService for asynchronous processing.

  • Retry Logic (Extension Point): In the delivery task, exceptions can be caught to implement retry mechanisms if needed.

Additional Patterns

  • Factory Pattern: NotificationFactory is used to create Notification objects uniformly, ensuring consistent instantiation.

  • Observer Pattern: NotificationManager maintains a list of observers (like LoggingObserver) that are notified after a successful delivery. This decouples side-effect operations (such as logging) from the main delivery logic.


4. Conclusion

This LLD for a Notification System demonstrates how to:

  • Model users and notifications with clear entity classes.

  • Handle multiple delivery mechanisms using the Strategy Pattern.

  • Ensure reliable and asynchronous delivery using the Singleton-managed NotificationManager and ExecutorService.

  • Incorporate additional behaviors (e.g., logging) using the Observer Pattern.

  • Centralize object creation using the Factory Pattern.

This design is modular, extensible, and would serve as a strong discussion point in an interview setting.

PreviousSocial Media PlatformNextAirline Reservation System

Last updated 2 months ago