3

Looking for a best design for the following use case.

Use case: In my application, I have a scheduler running every one hour and exposing a service.

The scheduler runs every one hour and retrieves records from a particular table and sends notification if any record processing fails. The notification could be of email or SMS.

Also, this service could be invoked by other services for record processing failure notification( Email or SMS).

The failure type will be of batchFailure or serviceFailure.

Also, there are some prop settings class to enable or disable SMS & Email notification.

public class Settings {
 private boolean emailEnabled;
 private boolean smsEnabled;
}

The email subject should contain Subject as "Batch failure" or "Service failure" based on the type. The other content remains same for both batch job and service failure.

I created the following classes:-

  1. Subject - I created a common subject for both.

    public class Notification {
     private int recordId;
     private String recordName;
     private String email; // Related to email
     private String smsNumber; // Related to sms
    }
    
  2. Listener class

    public interface RecordFailureListener {
     void sendNotification(Notification notification);
    }
    
  3. EmailListener

    public class EmailListener implements RecordFailureListener {
     void sendNotification(Notification notification) {
     // Code to send email here
     }
    }
    
  4. SMSListener

    public class SMSListener implements RecordFailureListener {
     void sendNotification(Notification notification) {
     // Code to send SMS here
     }
    }
    
  5. Service class

    public class NotificationService {
     List<RecordFailureListener> listeners = new ArrayList(); // This list has emailListener and smsListener
     void sendNotification(Notification notification) {
     listeners.forEach(listener -> listener.sendNotification(notification));
     }
    }
    

This notification service is being invoked from scheduler for any failure. Also, from the failure service being exposed.

Question:

1) Here, the subject seems to be having the properties required for email and sms. Is there any other better way so that email notificaiton will have properties required for email and sms notification will have properties required for sms? and there are some common properties also.

2) Check if email is been enabled or not in the email listener before calling send email and same in sms listener. Is this the right place?

Any other better design?

Thanks

Holger
300k43 gold badges479 silver badges825 bronze badges
asked Feb 13, 2018 at 4:47
0

2 Answers 2

2

You problem is the classic notifier/listener problem. I would suggest using an event bus as well, as a means of decoupling the lisetner and the notifier. The idea is that the notifier will notify the event bus, and the event bus will go over the listeners and update them on notification. The design is as follows : (This is a bare skeleton, with some convenient short cuts, such as using an enum singleton for the EventBus. the fine details are up to you).

public interface Event {
 // general event information - date, message, meta data, etc....
}
public interface Listener { 
 public void listen(Event event);
}
public enum EventBus {
 INSTANCE;
 private List<Listener> listeners = new ArrayList<>;
 public void addListener(Listener newListener) {
 listeners.add(newListener);
 }
 public void notify(Event event) {
 for (Listener listener : listeners) {
 listener.listen(event);
 }
 }
}
public class Notifier {
 public void handlePointOfInterest(.....) {
 // do something interesting with the parameters, etc...
 Event event = // create a new event based on your needs
 EventBus.INSTANCE.notify(event);
 ...
 }
}

Your event interface can also use generics in order to support specific meta data types - such as your settings. You Listener implementations can use generics in order to handle specific types of events.

Hope this helps, feel free to ask for clarifications.

answered Feb 13, 2018 at 5:20
4
  • This is what have been implemented? How can i have different subjects as listeners are called in the for loop, but email lister needs email related property and ticket listener needs ticket related properties. Commented Feb 13, 2018 at 5:23
  • @user1578872: The properties should be an attribute of your event. Furthermore, if you use generics with your event interface, you can also bind specific events to specific listeners. Like i wrote, the design posted here is a skeleton, there is a lot that can be added to it in order to fine tune and handle specific demands. Commented Feb 13, 2018 at 5:28
  • I am confused here on using Generics for sending events for different listeners. Can you please be clear on this part? Commented Feb 13, 2018 at 5:42
  • @user1578872 : sure. the idea is to implement the specific event via a YourSpecificEvent class. Then you can create a dedicated listener family which is designed to handle only this kind of event - SpecificEventListener<YourSpecificEvent> and SubhandlingEventListener<YourSpecificEvent> listeners, for example. The idea behind using generics is to bind the listener to a specific event type. The implementations of the event listeners is unique to each listener. This is just one way of handling the event to listener binding problem, it is buy no means a necessity. Commented Feb 13, 2018 at 5:56
0

Just sharing my design pattern,little old question though.

Settings can be validated in NotificationService itself, since this is where we are starting notification flow. Assuming NotificationService is being invoked from Scheduler for failure notification

public class NotificationService {
 Notification notification;
 static List<RecordFailureListener> listenerList = new ArrayList<>(); // holds email & sms listeners
 public NotificationService (Notification notification){
 this.notification=notification;
 }
 public void sendNotification(){
 if(Settings.emailEnabled){
 listenerList.stream().filter(i-> i instanceof EmailListener).forEach(i->i.sendNotification(notification));
 }
 if(Settings.smsEnabled){
 listenerList.stream().filter(i-> i instanceof SmsListener).forEach(i->i.sendNotification(notification));
 }
 }
}

RecordeFailureListener

public interface RecordFailureListener {
 void sendNotification(Notification notification);
}

EmailListener (FailureType can validated here to assign respective subject line for email)

public class EmailListener implements RecordFailureListener {
 private String emailSubject;//emailistener specific property
 @Override
 public void sendNotification(Notification notification) {
 if(Stream.of("BatchFailure","ServiceFailure").anyMatch(notification.getFailureType()::equalsIgnoreCase)){
 emailSubject= notification.getFailureType();
 }
 else{
 emailSubject="customSubject";
 }
 //code to send email notification
 }
}

SMSListener

public class SmsListener implements RecordFailureListener {
 @Override
 public void sendNotification(Notification notification) {
 //code to sms notification
 }
}

Settings

public class Settings {
 public static boolean emailEnabled;
 public static boolean smsEnabled;
}

Notification model(have added failureType as Notification property)

 public class Notification {
 private int recordId;
 private String recordName;
 private String email;
 private String smsnumber;
 private String failureType;
}
answered Sep 3, 2020 at 19:20

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.