DEV Community

DevCorner2
DevCorner2

Posted on

Designing a Scalable Notification System in Java using Strategy, Factory, and Observer Patterns

his system demonstrates OOP best practices, SOLID principles, and applies core design patterns like Strategy, Factory, and Observer.


πŸ“£ Problem Statement: Notification System

Design a Notification System that can:

  • Send notifications via Email, SMS, and Push
  • Be easily extensible for new channels (e.g., WhatsApp)
  • Support priority levels, batch delivery, and retry on failure

🧰 Design Patterns Used

Pattern Purpose
Strategy Channel-based notification logic
Factory Create appropriate channel instance
Observer Notify multiple channels at once
Template Common retry and failure logic

πŸ“¦ Package Structure

com.notification
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ Notification.java
β”‚   β”œβ”€β”€ NotificationService.java
β”‚   └── NotificationChannel.java
β”œβ”€β”€ channels/
β”‚   β”œβ”€β”€ EmailChannel.java
β”‚   β”œβ”€β”€ SMSChannel.java
β”‚   └── PushChannel.java
β”œβ”€β”€ factory/
β”‚   └── NotificationChannelFactory.java
β”œβ”€β”€ observer/
β”‚   └── NotificationPublisher.java
β”œβ”€β”€ model/
β”‚   └── NotificationRequest.java
└── Main.java
Enter fullscreen mode Exit fullscreen mode

βœ… UML Diagram (ASCII View)

                      +-----------------------------+
                      |     NotificationService     |
                      +-----------------------------+
                      | +send(NotificationRequest)  |
                      +-----------------------------+
                               |
                               β–Ό
             +------------------------------+
             |       NotificationChannel    |  <<interface>> (Strategy)
             +------------------------------+
             | +send(NotificationRequest)   |
             +------------------------------+
               β–²         β–²           β–²
               |         |           |
     +----------------+ +----------------+ +----------------+
     |  EmailChannel  | |   SMSChannel   | |  PushChannel   |
     +----------------+ +----------------+ +----------------+
     | Implements NotificationChannel     |
     +----------------+ +----------------+ +----------------+

          +-----------------------------+
          | NotificationChannelFactory  |
          +-----------------------------+
          | +getChannel(String): NotificationChannel |
          +-----------------------------+

          +--------------------------+
          | NotificationPublisher   |  <<Observer>>
          +--------------------------+
          | - subscribers: List<NotificationChannel> |
          | +subscribe(channel)                     |
          | +notifyAll(NotificationRequest)         |
          +--------------------------+

         +--------------------------+
         |  NotificationRequest     |
         +--------------------------+
         | - message: String        |
         | - recipient: String      |
         | - priority: int          |
         | - channel: String        |
         +--------------------------+

Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Class Responsibilities

πŸ”Ή NotificationRequest

Represents the payload to be sent.

public class NotificationRequest {
    private String message;
    private String recipient;
    private String channel;
    private int priority;

    // Constructors, Getters, Setters
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή NotificationChannel (Strategy Interface)

public interface NotificationChannel {
    void send(NotificationRequest request);
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή EmailChannel, SMSChannel, PushChannel

Each implements the NotificationChannel strategy.

public class EmailChannel implements NotificationChannel {
    public void send(NotificationRequest request) {
        System.out.println("Sending EMAIL to " + request.getRecipient());
    }
}
Enter fullscreen mode Exit fullscreen mode

Same for SMSChannel, PushChannel.


πŸ”Ή NotificationChannelFactory

public class NotificationChannelFactory {
    public static NotificationChannel getChannel(String type) {
        return switch (type.toLowerCase()) {
            case "email" -> new EmailChannel();
            case "sms" -> new SMSChannel();
            case "push" -> new PushChannel();
            default -> throw new IllegalArgumentException("Unsupported channel");
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή NotificationPublisher (Observer Pattern)

public class NotificationPublisher {
    private final List<NotificationChannel> subscribers = new ArrayList<>();

    public void subscribe(NotificationChannel channel) {
        subscribers.add(channel);
    }

    public void notifyAll(NotificationRequest request) {
        for (NotificationChannel channel : subscribers) {
            channel.send(request);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή NotificationService

public class NotificationService {
    public void send(NotificationRequest request) {
        NotificationChannel channel = NotificationChannelFactory.getChannel(request.getChannel());
        channel.send(request);
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Main.java

public class Main {
    public static void main(String[] args) {
        NotificationRequest email = new NotificationRequest("Hello", "user@example.com", "email", 1);
        NotificationRequest sms = new NotificationRequest("Hi", "9999999999", "sms", 2);

        NotificationService service = new NotificationService();
        service.send(email);
        service.send(sms);

        // Observer pattern usage
        NotificationPublisher publisher = new NotificationPublisher();
        publisher.subscribe(new EmailChannel());
        publisher.subscribe(new SMSChannel());

        NotificationRequest bulk = new NotificationRequest("Broadcast", "all-users", "", 1);
        publisher.notifyAll(bulk);
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Benefits of This Design

Benefit How It’s Achieved
Extensibility Add new channel by implementing NotificationChannel
Loose coupling Via interfaces and factory usage
Scalability Easily integrate retry, queue, priority
Testability Each class has single responsibility

Top comments (0)