LogIn
I don't have account.

Kotak Mahindra Bank SDE-1 Round 3 Interview Experience – LLD Round (Notification Service Design)

Kunal Dixit
427 Views

One of the most interesting rounds in my Kotak interview process was the Low-Level Design round. Unlike traditional DSA interviews, this round focused on designing a real-world system and evaluating how I structure classes, APIs, business logic and extensibility.

The interviewer asked me to design and implement a Notification Service that could send notifications through multiple channels such as Email, WhatsApp and SMS. At first glance the problem looked simple, but as the discussion progressed it became clear that the interviewer was less interested in sending notifications and more interested in understanding whether I could design a maintainable, scalable and extensible system.

The biggest challenge was not the notification delivery itself. The challenge was designing a flexible template framework that could support multiple notification types, different communication channels, dynamic placeholders, validation and future extensibility without requiring major code changes.

Problem Statement

The system should support two APIs:

1. Template Registration

Allows businesses to register notification templates.

Example:

OTP Template


Required Inputs:

emailAddress
mobileNumber
name
otp

// Email Template:

Subject: OTP Information

Hi {name},

This is your OTP {otp}

// SMS Template:

OTP Information {otp} for login.

Promotion Template


Required Inputs:

emailAddress
mobileNumber
couponCode

// Email Template:

Subject: Promotion

Hi,

Your coupon code is {couponCode}

// SMS Template:

Coupon code: {couponCode}

2. Template Invocation


Example Request:

{
  "templateName": "OTP",
  "input": {
    "emailAddress": "user@gmail.com",
    "mobileNumber": "9999999999",
    "name": "Raj",
    "otp": "123456"
  }
}

Expected Output:


// Email:

Subject: OTP Information

Hi Raj,

This is your OTP 123456

// SMS:

OTP Information 123456 for login.
Requirements Clarification

Before jumping into design, I clarified a few requirements:

  • Functional Requirements
  • Register templates dynamically
  • Support multiple notification channels
  • Replace placeholders dynamically
  • Validate mandatory fields
  • Send notifications through all configured channels
  • Store templates in memory
  • Non-Functional Requirements
  • Extensible for future channels
  • Easy to maintain
  • Low coupling
  • High cohesion
  • Thread-safe design
  • Easy testing

These discussions are usually where interviewers start evaluating design maturity.

High-Level Architecture

I proposed dividing responsibilities into separate components.


                Client
                   |
                   v
        NotificationService
                   |
     ----------------------------
     |            |             |
     v            v             v
TemplateRepo  TemplateEngine  SenderFactory
                                   |
                   --------------------------------
                   |              |              |
                   v              v              v
             EmailSender     SmsSender   WhatsappSender

Instead of building one giant NotificationService class, every component owns a single responsibility. This follows the Single Responsibility Principle and keeps the design maintainable as the system grows.

Core Domain Models

Notification Channel


public enum Channel {
    EMAIL,
    SMS,
    WHATSAPP
}

This provides type safety and avoids string comparisons throughout the codebase.

Template

A template represents a notification definition.


public class Template {
    private String templateName;
    private Set<String> requiredFields;
    private Map<Channel, ChannelTemplate> channelTemplates;
}

Example:


OTP Template
├── Email Template
├── SMS Template
└── WhatsApp Template

The advantage is that adding a new channel does not require modifying existing templates.

Channel Template


public class ChannelTemplate {
    private String subject;
    private String body;
}

For SMS and WhatsApp, subject can remain null. This model keeps the design generic.

Template Repository

Since the interviewer explicitly mentioned using local data structures, I proposed an in-memory repository.


public class TemplateRepository {
    private final Map<String, Template> templates =
            new ConcurrentHashMap<>();

}

Methods:


void save(Template template);
Template get(String templateName);

Using ConcurrentHashMap makes the repository thread-safe for concurrent registrations and invocations.

Template Engine

One of the most important pieces of the design is the template rendering engine. Its responsibility is replacing placeholders.

Input:


Hi {name}

Your OTP is {otp}

Data:


{
  "name":"Raj",
  "otp":"123456"
}

Output:


Hi Raj

Your OTP is 123456

Implementation:


public class TemplateEngine {

    public String render(
            String template,
            Map<String, String> values) {
        String result = template;
        for (Map.Entry<String, String> entry : values.entrySet()) {
            result = result.replace(
                    "{" + entry.getKey() + "}",
                    entry.getValue());
        }
        return result;
    }
}

This component is completely reusable and independent from notification delivery.

Notification Sender Strategy

This is where good LLD design begins to shine. Instead of writing:


if(channel.equals("EMAIL")) {
   ...
}
else if(channel.equals("SMS")) {
   ...
}
else if(channel.equals("WHATSAPP")) {
   ...
}

I introduced the Strategy Pattern.

Common Interface


public interface NotificationSender {
    void send(NotificationRequest request);
}

Email Sender


public class EmailSender
        implements NotificationSender {
    @Override
    public void send(NotificationRequest request) {
        System.out.println(
                "Sending Email to "
                        + request.getEmailAddress());
    }
}

SMS Sender


public class SmsSender
        implements NotificationSender {
    @Override
    public void send(NotificationRequest request) {
        System.out.println(
                "Sending SMS to "
                        + request.getMobileNumber());
    }
}

WhatsApp Sender


public class WhatsappSender
        implements NotificationSender {
    @Override
    public void send(NotificationRequest request) {
        System.out.println(
                "Sending WhatsApp to "
                        + request.getMobileNumber());
    }
}

Each channel owns its own delivery logic.

Sender Factory

To avoid large switch statements:


public class SenderFactory {
    private final Map<Channel,
            NotificationSender> senders;
    public NotificationSender getSender(
            Channel channel) {
        return senders.get(channel);
    }
}

This follows the Open-Closed Principle. Tomorrow if business asks for:

  • Push Notification
  • Slack
  • Telegram
  • Teams

we simply add:


class PushSender
implements NotificationSender

without changing existing code.

API 1 – Register Template

POST /templates

Request:


{
  "templateName":"OTP",
  ...
}

Service Layer:


public void registerTemplate(
        Template template) {
    repository.save(template);
}

Complexity:

  • Time : O(1)
  • Space: O(1)

API 2 – Invoke Template

POST /notifications/send

Flow:

  1. Fetch Template
  2. Validate Inputs
  3. Render Template
  4. Send Notification

Implementation:


public void invoke(
        String templateName,
        Map<String,String> input) {
    Template template =
            repository.get(templateName);
    validate(template, input);
    for(var channelEntry :
            template.getChannelTemplates()
                    .entrySet()) {

        Channel channel =
                channelEntry.getKey();

        ChannelTemplate ct =
                channelEntry.getValue();

        String body =
                engine.render(
                        ct.getBody(),
                        input);
        NotificationSender sender =
                factory.getSender(channel);
        sender.send(
                buildRequest(body, input));
    }
}

This orchestration layer remains extremely clean because responsibilities are delegated to specialized components.

Production-Level Enhancements

Toward the end of the interview, I discussed how this design could evolve in a real production environment.

Asynchronous Processing

Instead of sending notifications synchronously:


API
Send Email
Send SMS
Return Response

Use:


API
Kafka
Notification Workers
Email/SMS Providers

This improves reliability and latency.

Retry Mechanism

Failures are inevitable. For example:

  • Email Provider Down
  • Network Timeout
  • WhatsApp API Failure

Introduce:

  • Retry Queue
  • Exponential Backoff
  • Dead Letter Queue

for failed notifications.

Provider Abstraction

Today:

  • Email → SES
  • SMS → Twilio
  • WhatsApp → Meta

Tomorrow:

  • Email → SendGrid
  • SMS → Gupshup
  • WhatsApp → Infobip

Business logic should remain unchanged.

Audit Logs

Every notification should be tracked.

NotificationAudit

Fields:

  • NotificationId
  • TemplateName
  • Channel
  • Status
  • RetryCount
  • CreatedAt

This helps debugging and compliance.

Key Learnings

This round taught me that interviewers are usually not evaluating whether you can write Java classes. They are evaluating whether you can model a real business problem using good software engineering principles. The strongest aspects of this solution are:

  • Single Responsibility Principle
  • Open Closed Principle
  • Strategy Pattern
  • Factory Pattern
  • Template Engine Separation
  • Thread-safe Repository
  • Extensible Architecture
  • Production Readiness Considerations

Looking back, the interviewer was primarily interested in the service layer implementation, template rendering flow and how new notification channels could be added without changing existing code. Candidates who focus only on class diagrams often miss these discussions, while candidates who explain design decisions, trade-offs and future extensibility generally perform much better in Low-Level Design interviews.

Recommended Interview Experience

Responses (0)

Write a response

CommentHide Comments

No Comments yet.