At Zuddl, we send more than 10 different types of notifications to our users. For instance, when one attendee sends a 1:1 meeting invitation to another and when a meeting invite is accepted, when someone asks a question or an attendee answers one, for votes, etc. All of these notifications are sent instantly as the user takes action.
Single vs multiple notification delivery
At Zuddl, we send two types of service notifications:
- Real-time notifications
- Email notifications
Pusher lets you create web apps with real-time functionality such as sharing live results, messaging, and pushing content to end-users and back. We use one of Pusher’s messaging solutions; Channels, to communicate between our servers, apps, and devices which thereby helps us send real-time notifications to attendees in a single event. Pusher’s ‘Channels’ is also used for real-time charts, real-time user lists, real-time maps, multiplayer gaming, and also to notify many other types of UI updates. Whenever anything happens in our systems, it helps us keep our web pages, apps, and devices updated.
Every notification contains a payload. The notification payload is created at the backend which contains the title, description, and other required information.
Users also receive emails for every notification received on the app such as event reminders, thank you emails, and post-event emails amongst others. For email notifications, we use SendGrid, a cloud-based SMTP provider that allows you to send emails without having to maintain email servers. SendGrid manages all technical requirements, from scaling the infrastructure to ISP(Internet service provider) outreach, reputation monitoring to whitelist services, and real-time analytics.
The first notification system we developed was great but was not scalable. It also had other issues, for example, wrong notifications being sent or notification misfires, delays, and complicated logic on the UI side. In the case of single notification delivery types such as Pusher and email, the existing implementation worked. But when multiple notifications delivery types(Email, Pusher and Firebase Console Messaging) were required at runtime, we had to put on our thinking hats to handle such use cases.
How we used design pattern for notification delivery
To get the right service or delivery at run time we use a factory design pattern. But before I go about explaining the factory design pattern, it is essential to to understand how design patterns are used.
A software design pattern is a basic, repeatable solution to a commonly occurring problem within a given context in software design. It cannot be used off the shelf as a finished piece of code design but needs to be customized and transformed into a source code for a particular use case. It is supposed to be used as a template for how to solve a known problem factoring in several scenarios.
Factory method pattern is one type of design pattern also known as a creational pattern that uses factory methods to deal with the problem. In this way, we can create objects without having to specify the exact class of the object that will be created or the subclasses that are responsible for creating the instance of the class.
To resolve the multiple notifications delivery types that are required at runtime for any event, we created a factory class.
The above class is responsible to fetch a service type based on delivery type.
We have also created a NotifyAdapter class to check common validations like duplicate notification, input request validation, and many others.
Our other services(Email and Pusher) only call the NotifyAdapter class and internally we fetch service type based on DeliveryType using factory pattern.
Factory Method is a widely used, creational design pattern that can be used in many situations where multiple concrete implementations of an interface exist. The pattern removes complex logical code that is hard to maintain and replaces it with a design that is reusable and extensible.