94. Design Order Notification System

Design Order Notification System
Design and implement a real-time order notification system for a modern e-commerce platform. The system should notify different stakeholders such as customers, sellers, and delivery partners about important events in an order's lifecycle. The design should be extensible so that additional notification channels and event types can be supported later.

Supported Stakeholder Types

Stakeholder types are CUSTOMER, SELLER, and DELIVERY_PARTNER. A stakeholder may subscribe to one or more order events and may configure separate channel preferences for each subscribed event type.

Supported Order Events

The system must support event types such as ORDER_PLACED, ORDER_SHIPPED, ORDER_DELIVERED, and more. Different stakeholders may care about different events. For example, a customer may subscribe to all three events above, a seller may subscribe only to ORDER_PLACED, and a delivery partner may subscribe only to ORDER_SHIPPED. Notifications must be generated only for stakeholders linked to the specific order for which the event is published.

Notification Channels

The system must support multiple channels such as EMAIL, SMS, and APP_PUSH. Channel preferences are maintained separately for each (stakeholderId, stakeholderType, eventType) subscription. If a stakeholder subscribes with an empty channel list, all supported channels must be enabled by default.

Functional Requirements

A stakeholder must be able to subscribe to an event type, unsubscribe from an event type, add channels to an existing subscription, and remove channels from an existing subscription. After unsubscribing from an event type, the stakeholder must not receive notifications for that event type for any order. When an order event is published, the system must generate one notification per active channel for each linked stakeholder currently subscribed to that event type. Actual notification delivery may be asynchronous, but the method should return the generated notification log lines for that call.

Bonus Requirement

The system should support replaying notifications for a given orderId, eventType, and stakeholder. Replay must use the stakeholder's latest active channel preferences for that event type, not necessarily the channel set that was active when the event was originally published.

Output Format

Whenever a method returns a string representation of an object, fields must be separated using space-hyphen-space, that is - .

  • Notification log line format:
    NOTIFICATION - orderId=<orderId> - eventType=<eventType> - stakeholderType=<stakeholderType> - stakeholderId=<stakeholderId> - channel=<channel> - message="<message>"
  • Replay summary line format:
    REPLAY - orderId=<orderId> - eventType=<eventType> - stakeholderType=<stakeholderType> - stakeholderId=<stakeholderId>

Constructor

OrderNotificationSystem(List<String> eventTypes, List<String> notificationChannels)

  • Both parameter lists must be non-empty.
  • eventTypes must contain distinct, non-null, non-blank supported event names.
  • notificationChannels must contain distinct, non-null, non-blank supported channel names.
  • The order of notificationChannels defines the channel order used in returned notification log lines.

Exact Method Signatures

boolean subscribeUnsubscribeToEvent(String stakeholderId, String stakeholderType, String eventType, List<String> channels, boolean subscribe)

  • If subscribe=true, creates a new subscription or replaces the full active channel set for that stakeholder and event type.
  • If subscribe=true and channels is empty, all supported channels are enabled by default.
  • If subscribe=false, removes the entire subscription for that stakeholder and event type. In this case, channels is ignored.
  • Duplicate channels in input are ignored.
  • If any required input is invalid, the method returns false and makes no state change.
  • For valid input, the method is idempotent and returns true even if the final state is unchanged.

boolean addRemoveChannels(String stakeholderId, String stakeholderType, String eventType, List<String> channels, boolean add)

  • Updates the channel set of an existing subscription.
  • If add=true, the given channels are added to the current channel set.
  • If add=false, the given channels are removed from the current channel set.
  • Duplicate channels in the input list are ignored.
  • If the subscription does not exist, or if channels is null or empty, or if any input is invalid, the method returns false and makes no state change.
  • If removing channels makes the active channel set empty, the subscription is removed completely.
  • For valid input, the method is idempotent and returns true even if the final state is unchanged.

List<String> publishOrderEvent(String orderId, String eventType, String customerId, String sellerId, String deliveryPartnerId, String message)

  • Publishes an event for an order and returns the generated notification log lines.
  • Not duplicate event must be published for the same orderId, eventType. Return an empty list in that case.
  • Only stakeholders linked to that order and currently subscribed to the event type receive notifications.
  • One log line is generated per active channel.
  • The same message is used in every generated notification log line for that publish call.
  • Returned notification log lines must be ordered by stakeholder type in this order: CUSTOMER, SELLER, DELIVERY_PARTNER. Within one stakeholder, channels must follow constructor channel order.
  • If input is invalid, the method returns an empty list and stores no publish history.
  • If no linked stakeholder is subscribed, the method returns an empty list.

List<String> replayNotifications(String orderId, String eventType, String stakeholderId, String stakeholderType)

  • Replays notifications for a previously published (orderId, eventType) for the specified stakeholder.
  • Replay uses the stakeholder's latest active channel set for that event type.
  • Replay uses the original message from the stored publish record.
  • If input is invalid, or if there is no matching historical publish record, or if the stakeholder currently has no active subscription for that event type, the method returns an empty list.
  • Otherwise, the method returns one replay summary line followed by replayed notification log lines in constructor channel order.

Constraints

  • orderId, eventType, stakeholderId, stakeholderType, and message must be non-null and non-blank wherever applicable.
  • stakeholderType must be one of CUSTOMER, SELLER, or DELIVERY_PARTNER.
  • eventType and channel names must belong to the sets configured in the constructor.
  • orderId and stakeholderId are unique identifiers in the system wherever applicable.
  • customerId and sellerId must be non-null and non-blank when publishing an order event.
  • deliveryPartnerId may be null or empty if no delivery partner is linked to that order yet.
  • For each subscription, channels behave like a set.
  • Actual notification delivery may be asynchronous, but the returned log lines represent the notifications generated by the method call.

Example 1

OrderNotificationSystem system = new OrderNotificationSystem(List.of("ORDER_PLACED", "ORDER_SHIPPED", "ORDER_DELIVERED"), List.of("EMAIL", "SMS", "APP_PUSH"))
system.subscribeUnsubscribeToEvent(stakeholderId="CUSTOMER-1", stakeholderType="CUSTOMER", eventType="ORDER_PLACED", channels=List.of("SMS"), subscribe=true)
returns true

system.subscribeUnsubscribeToEvent(stakeholderId="SELLER-1", stakeholderType="SELLER", eventType="ORDER_PLACED", channels=List.of("EMAIL"), subscribe=true)
returns true

system.subscribeUnsubscribeToEvent(stakeholderId="DELIVERY-1", stakeholderType="DELIVERY_PARTNER", eventType="ORDER_SHIPPED", channels=List.of(), subscribe=true)
returns true

system.publishOrderEvent(orderId="ORDER-001", eventType="ORDER_PLACED", customerId="CUSTOMER-1", sellerId="SELLER-1", deliveryPartnerId="DELIVERY-1", message="Order ORDER-001 has been placed")
returns List.of("NOTIFICATION - orderId=ORDER-001 - eventType=ORDER_PLACED - stakeholderType=CUSTOMER - stakeholderId=CUSTOMER-1 - channel=SMS - message="Order ORDER-001 has been placed"", "NOTIFICATION - orderId=ORDER-001 - eventType=ORDER_PLACED - stakeholderType=SELLER - stakeholderId=SELLER-1 - channel=EMAIL - message="Order ORDER-001 has been placed"")


Explanation
The customer and seller are both linked to the order and subscribed to ORDER_PLACED, so they receive notifications on their active channels. The delivery partner is linked to the order but subscribed only to ORDER_SHIPPED, so no notification is generated for that stakeholder in this publish call.

Example 2

OrderNotificationSystem system = new OrderNotificationSystem(List.of("ORDER_PLACED", "ORDER_SHIPPED", "ORDER_DELIVERED"), List.of("EMAIL", "SMS", "APP_PUSH"))
system.subscribeUnsubscribeToEvent(stakeholderId="CUSTOMER-1", stakeholderType="CUSTOMER", eventType="ORDER_DELIVERED", channels=List.of("EMAIL", "SMS"), subscribe=true)
returns true

system.publishOrderEvent(orderId="ORDER-002", eventType="ORDER_DELIVERED", customerId="CUSTOMER-1", sellerId="SELLER-1", deliveryPartnerId="DELIVERY-1", message="Order ORDER-002 has been delivered")
returns List.of("NOTIFICATION - orderId=ORDER-002 - eventType=ORDER_DELIVERED - stakeholderType=CUSTOMER - stakeholderId=CUSTOMER-1 - channel=EMAIL - message="Order ORDER-002 has been delivered"", "NOTIFICATION - orderId=ORDER-002 - eventType=ORDER_DELIVERED - stakeholderType=CUSTOMER - stakeholderId=CUSTOMER-1 - channel=SMS - message="Order ORDER-002 has been delivered"")

system.addRemoveChannels(stakeholderId="CUSTOMER-1", stakeholderType="CUSTOMER", eventType="ORDER_DELIVERED", channels=List.of("EMAIL"), add=false)
returns true

system.replayNotifications(orderId="ORDER-002", eventType="ORDER_DELIVERED", stakeholderId="CUSTOMER-1", stakeholderType="CUSTOMER")
returns List.of("REPLAY - orderId=ORDER-002 - eventType=ORDER_DELIVERED - stakeholderType=CUSTOMER - stakeholderId=CUSTOMER-1", "NOTIFICATION - orderId=ORDER-002 - eventType=ORDER_DELIVERED - stakeholderType=CUSTOMER - stakeholderId=CUSTOMER-1 - channel=SMS - message="Order ORDER-002 has been delivered"")


Explanation
The original publish generates one notification per active channel, so both EMAIL and SMS are used. Later, EMAIL is removed from the active subscription. Replay uses the latest active channel set, so only SMS is used during replay.





Please use Laptop/Desktop or any other large screen to add/edit code.