Bird
Raised Fist0

Design a Messaging System OOP - User, Conversation, Message, Delivery Status

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
๐ŸŽฏ
Design a Messaging System OOP - User, Conversation, Message, Delivery Status
40 min ยท medium
medium

Design a messaging system where multiple users can participate in conversations, send messages, and track delivery status of each message. The system must support one-to-one and group conversations, message ordering, and concurrent message delivery updates.

  • Users can participate in multiple conversations simultaneously.
  • Each conversation can have multiple participants (users).
  • Messages have delivery statuses: SENT, DELIVERED, READ.
  • Delivery status updates can happen concurrently from different devices.
  • Message ordering must be preserved within each conversation.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define User and Conversation Entities
Design the User and Conversation classes with appropriate relationships and state. Consider how to represent participants and their lifecycle.
๐Ÿ’ก Hint
Use composition to model Conversation containing Users; User can exist independently (aggregation). Use Java collections to hold participants.
Conversation holds a private final Set<User> participants - aggregation because Users exist independently; use HashSet for O(1) lookup.
Mid: Recognizes aggregation vs composition; Senior: Explains why User lifecycle is independent and how HashSet ensures no duplicates.
โš ๏ธCommon Wrong Answer
Using composition for User inside Conversation leads to Users being destroyed when Conversation ends, violating independent lifecycle.
Step 2
Design Message Class with Delivery Status Enum
Create the Message class with immutable content and a delivery status field. Use an enum for delivery status to ensure type safety.
๐Ÿ’ก Hint
DeliveryStatus should be an enum with SENT, DELIVERED, READ. Message content should be final to ensure immutability.
Use enum DeliveryStatus { SENT, DELIVERED, READ } - enums provide compile-time safety and exhaustive switch support; Message fields final for immutability.
Mid: Uses enum for delivery status; Senior: Explains how enum prevents runtime bugs from invalid status strings and enables switch-case exhaustiveness.
โš ๏ธCommon Wrong Answer
Using String for delivery status causes runtime bugs due to typos and no compile-time checks.
Step 3
Implement Thread-safe Delivery Status Updates
Ensure delivery status updates on Message are thread-safe to handle concurrent updates from multiple devices.
๐Ÿ’ก Hint
Use volatile for deliveryStatus field and synchronized method to atomically update status only if new status is higher priority.
Declare private volatile DeliveryStatus deliveryStatus; synchronize public boolean updateDeliveryStatus(DeliveryStatus newStatus) to atomically check and set status.
Mid: Uses synchronized method for atomic update; Senior: Explains thread interleaving where without volatile, thread may see stale status causing lost updates.
โš ๏ธCommon Wrong Answer
Not using volatile leads to visibility issues where one thread's update is not seen by others, causing inconsistent delivery status.
Step 4
Model Conversation Message Ordering and Storage
Design how messages are stored and ordered within a Conversation to preserve message sequence.
๐Ÿ’ก Hint
Use a thread-safe List implementation or synchronize access to a List<Message> to maintain insertion order.
Use private final List<Message> messages with synchronized addMessage(Message m) method to ensure thread-safe insertion preserving order.
Mid: Uses synchronized method to protect messages list; Senior: Explains race condition where concurrent adds without synchronization cause lost or reordered messages.
โš ๏ธCommon Wrong Answer
Using ArrayList without synchronization causes race conditions and message order corruption under concurrent writes.
Step 5
Design User-Conversation Association and Message Sending
Implement how a User sends a Message to a Conversation and how the system ensures only participants can send messages.
๐Ÿ’ก Hint
Check participant membership atomically before adding message; use synchronized method in Conversation to enforce this.
public synchronized boolean sendMessage(User sender, String content) checks participants.contains(sender) before creating and adding Message.
Mid: Uses synchronized method to atomically check membership and add message; Senior: Explains thread interleaving where without synchronization, non-participant could send message due to stale participants set.
โš ๏ธCommon Wrong Answer
Not synchronizing membership check and message add allows race where user removed concurrently but still sends message.
๐Ÿ”Œ
API Design Challenge
Design APIs for sending messages, updating delivery status, and retrieving conversation history with concurrency and access control.
  • Use synchronized methods or concurrent collections to handle concurrent message sends and status updates.
  • Ensure only conversation participants can send messages by checking membership atomically.
  • Design APIs to return messages in order and support pagination for large conversations.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • User and Conversation aggregation with independent lifecycle
  • DeliveryStatus enum instead of String
  • volatile deliveryStatus field in Message
  • synchronized updateDeliveryStatus method
  • synchronized addMessage preserving message order
Good to Have - 30%
  • Immutable Message content fields
  • Atomic check in sendMessage to verify participant membership
  • Use of HashSet for participants to prevent duplicates
Bonus - 10%
  • Explain thread interleaving scenarios causing race conditions
  • Use of final keyword to enforce immutability
Using String for delivery status leads to runtime typo bugs and no compile-time checks, causing invalid states and bugs.
Not using volatile on deliveryStatus causes visibility issues and stale reads, leading to inconsistent delivery status across threads.
Not synchronizing message list updates causes lost or reordered messages under concurrency, breaking message order guarantees.
Composition of User inside Conversation causes improper lifecycle coupling, destroying Users when Conversations end.
Checking participant membership and adding message separately without synchronization causes race conditions allowing unauthorized sends.
โœ…
Model Answer
โ–ผ Reveal

The design models Users and Conversations with aggregation to respect independent lifecycles. Messages are immutable except for a volatile deliveryStatus field updated atomically via synchronized methods. DeliveryStatus is an enum to ensure type safety. Conversations maintain message order using a synchronized List. Sending messages requires atomic membership checks and message addition to prevent unauthorized access and race conditions.

  • Use HashSet for participants in Conversation to ensure O(1) lookup and prevent duplicates, modeling aggregation since Users exist independently.
  • Implement DeliveryStatus as a Java enum with SENT, DELIVERED, READ to provide compile-time safety and exhaustive switch-case support.
  • Declare deliveryStatus field in Message as volatile to ensure visibility of updates across threads.
  • Synchronize updateDeliveryStatus method in Message to atomically update delivery status only if new status has higher priority.
  • Synchronize addMessage method in Conversation to preserve message insertion order and prevent race conditions during concurrent writes.
  • Synchronize sendMessage method in Conversation to atomically check participant membership and add message, preventing unauthorized sends.
  • User and Conversation have aggregation relationship; User lifecycle independent of Conversation.
  • DeliveryStatus implemented as enum for type safety and exhaustive switch.
  • Message class immutable except deliveryStatus which is volatile for visibility.
  • Synchronized updateDeliveryStatus method ensures atomic delivery status updates.
  • Conversation stores messages in List<Message> with synchronized addMessage to preserve order under concurrency.
  • sendMessage method synchronized to atomically check participant membership and add message, enforcing access control.

Practice

(1/5)
1. In designing the Order class, should the OrderState be an abstract class or an interface? Consider that each state has different behavior but shares no common state variables. Which is the best choice and why?
easy
A. Abstract class, because it allows sharing common state variables and default method implementations.
B. Interface, because states only define behavior contracts without shared state, enabling multiple inheritance.
C. Abstract class, because it enforces a fixed inheritance hierarchy and prevents multiple implementations.
D. Interface, because it allows storing state variables in the interface for reuse.

Solution

  1. Step 1: Analyze state sharing needs

    States have no shared variables, only behavior differences, so no need for shared state in a superclass.
  2. Step 2: Evaluate interface vs abstract class

    Interface suits behavior contracts without state and allows flexibility; abstract class is unnecessary and restrictive here.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Interface fits stateless behavior contracts best [check]
Hint: Interface for behavior-only contracts without shared state [check]
Common Mistakes:
2. In a system using the Prototype pattern, should the Vehicle base type be an abstract class or an interface? Why?
easy
A. Abstract class, because it can hold common state and provide default cloning behavior
B. Interface, because it allows multiple inheritance and enforces method signatures without state
C. Interface, because cloning requires no shared implementation or state
D. Abstract class, because interfaces cannot declare clone() methods

Solution

  1. Step 1: Consider cloning and shared state

    Cloning often requires copying internal state; an abstract class can hold this state and provide a default clone() implementation.
  2. Step 2: Evaluate interface limitations

    Interfaces define behavior but cannot hold state or provide default cloning logic (in Java before default methods). Thus, abstract class fits better.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class holds state + cloning logic -> correct OOP design [check]
Hint: Clone needs shared state -> abstract class better than interface [check]
Common Mistakes:
3. In the Amazon Locker Service, if the assignLocker() method is not synchronized, which exact sequence of thread interleaving causes two packages to be assigned the same locker?
medium
A. Thread 1 assigns locker and updates status; Thread 2 waits until Thread 1 finishes, then assigns a different locker.
B. Thread 1 and Thread 2 both call assignLocker() simultaneously and both fail to find any available lockers.
C. Thread 1 checks locker availability -> true, context switch; Thread 2 checks availability -> true; both assign the same locker before status update.
D. Thread 1 releases a locker; Thread 2 immediately assigns it without checking availability.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads locker as available, then context switches before marking it occupied.
  2. Step 2: Thread 2 also reads locker as available and proceeds to assign it, causing double-booking.

    Both threads assign the same locker before either updates the status.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Check-then-act gap -> double assignment [check]
Hint: Check availability -> context switch -> both assign same locker [check]
Common Mistakes:
4. In a parking lot system, if parkVehicle() is not synchronized, which exact sequence of Thread 1 and Thread 2 causes double-booking of the same spot?
medium
A. Thread 1 parks vehicle, Thread 2 waits until Thread 1 finishes, then parks another vehicle.
B. Thread 1 and Thread 2 both crash due to simultaneous access.
C. Thread 1 checks spot availability -> true, context switch; Thread 2 checks spot availability -> true; both park simultaneously.
D. Thread 1 parks vehicle, Thread 2 reads spot as occupied and waits.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available but before marking it occupied, context switches to Thread 2 which also reads it as available.
  2. Step 2: Both threads proceed to park, causing double-booking

    Because the state update is not atomic, both threads park vehicles in the same spot simultaneously.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act gap causes double-booking without synchronization [check]
Common Mistakes:
5. The Airline Reservation System needs to support ElectricCar vehicles requiring ChargingSpot parking. What is the minimum change to the current Java design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally.
B. Modify Vehicle base class to include charging capability and add ChargingSpot as a new subclass of Spot.
C. Create a new Vehicle hierarchy for electric cars and a separate ParkingLot for charging spots.
D. Add a boolean isElectric field to Vehicle and conditionally handle charging spots in existing Spot classes.

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot must not modify existing classes to respect OCP and avoid class explosion.
  2. Step 2: Minimal change approach

    Adding new enum values and creating new subclasses for ElectricCar and ChargingSpot, plus updating compatibility externally, extends functionality without changing existing code.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New enums + subclasses + external compatibility update -> no existing class change [check]
Hint: New enums + subclasses + external map -> no existing class edits [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard