Object-Oriented Design for an Online Chat Server
HardUpdated: Oct 7, 2025
Problem
Explain how you would design a chat server. In particular, provide details about the various backend components, classes, and methods. What would be the hardest problems to solve?
Solution
We certainly need a user class. We need to handle concurrency.
User class:
public class User {
private Long userId;
private String nickname;
public User(Long userId, String nickname) {
this.userId = userId;
## Problem
Design an online chat server. Describe the backend components, classes, and method-level responsibilities required to support multiple users exchanging messages (directly or in sessions), and discuss the hardest problems to solve (concurrency, scaling, durability).
## Solution
### 1. Requirements Analysis
Functional requirements:
- Represent users and messages, with message timestamps and optional metadata.
- Support sessions (chat rooms) where multiple participants can join, send, and receive messages.
- Allow registration/unregistration of listeners (clients) that receive new messages for a session.
- Provide an API or CLI for sending messages and subscribing to message streams.
Non-functional requirements:
- Concurrency: support many concurrent users and sessions with low-latency message delivery.
- Scalability: ability to scale out (multiple servers or a message broker) to handle high fan-out.
- Durability/Retention: optional retention policy for messages (TTL) and capacity to trim old messages.
- Loose coupling: decouple producers and consumers (observer vs broker patterns).
Edge cases and constraints (inferred from the original note):
- Large fan-out (e.g., 1000 listeners) where synchronous observer notifications become expensive.
- Ordering guarantees, at-most-once vs at-least-once delivery (not fully specified in source; left as an implementation decision).
### 2. Use Case Diagram
Actors: User (client), System Administrator (optional), Message Broker / ChatServer.
Use case summary: A User registers/listens to a Session, posts messages, and receives messages published to that Session.
```mermaid
graph TD
subgraph Chat System
UC1(Send Message)
UC2(Receive Message)
UC3(Join Session)
UC4(Leave Session)
end
User --> UC1
User --> UC2
User --> UC3
User --> UC4
Admin --> UC3
3. Class Diagram
Core classes and brief responsibilities (all inferred from file content):
- User: identifier and user metadata (userId, nickname).
- Message: interface/contract for messages (value, timestamp, sender).
- SimpleMessage: a basic Message implementation.
- ChatListener: observer interface that receives new Message events.
- ChatWindow / Client: a listener implementation that holds a local message list for a user.
- ChatSession (or Session): manages registered listeners and publishes incoming messages to them.
- Optionally, MessageBroker / ChatServer: scalable component that handles distribution (e.g., via a queue or pub/sub system).
classDiagram class User { +Long userId +String nickname +getUserId() +getNickname() } class Message { <<interface>> +String getValue() +void setValue(String) +Date getTimestamp() +void setTimestamp(Date) +User getUser() } class SimpleMessage { +String value +User user +Date timestamp } class ChatListener { <<interface>> +void notify(Message) } class ChatWindow { +User user +List~Message~ messageList +void notify(Message) } class ChatSession { +List~ChatListener~ registeredChatListeners +void register(ChatListener) +void incomingMessage(Message) +void publish(Message) } ChatSession "1" -- "0..*" ChatListener : registers ChatWindow ..|> ChatListener SimpleMessage ..> User : sender
4. Activity Diagrams
Activity: Sending a Message
graph TD U[User types message] --> S[Client sends message to ChatSession or Broker] S --> P[Session/Broker validates and enqueues message] P --> N[Publish message to registered listeners] N --> L[Each listener.enqueueOrDeliver(message)]
Activity: Registering a Listener (subscribe)
graph TD U[Client requests subscribe] --> S[ChatSession.register(listener)] S --> A[Add listener to registered list] A --> R[Return success]
5. High-Level Code Implementation
Below are cleaned skeletons adapted from the original Java examples and converted to Python equivalents. These are intentionally minimal: class signatures, attributes, and key method signatures only.
Java
// User.java
public class User {
private Long userId;
private String nickname;
public User(Long userId, String nickname) {
this.userId = userId;
this.nickname = nickname;
}
public Long getUserId() { return userId; }
public String getNickname() { return nickname; }
}
// Message.java
public interface Message {
String getValue();
void setValue(String value);
Date getTimestamp();
void setTimestamp(Date ts);
User getUser();
}
// SimpleMessage.java
public class SimpleMessage implements Message {
private String value;
private User user;
private Date timestamp;
// constructors, getters, setters
}
// ChatListener.java
public interface ChatListener {
void notify(Message newMessage);
}
// ChatWindow.java (client-side listener)
public class ChatWindow implements ChatListener {
private User user;
private List<Message> messageList;
public void notify(Message newMessage) {
if (messageList == null) messageList = new ArrayList<>();
messageList.add(newMessage);
}
}
// ChatSession.java
public class ChatSession {
private List<ChatListener> registeredChatListeners;
public void register(ChatListener listener) {
if (registeredChatListeners == null) registeredChatListeners = new ArrayList<>();
registeredChatListeners.add(listener);
}
public void incomingMessage(Message message) {
publish(message);
}
protected void publish(Message messageToPublish) {
if (registeredChatListeners != null) {
for (ChatListener eachListener : registeredChatListeners) {
eachListener.notify(messageToPublish);
}
}
}
}
Python
from __future__ import annotations
from dataclasses import dataclass, field
from typing import List, Protocol
from datetime import datetime
@dataclass
class User:
user_id: int
nickname: str
class Message(Protocol):
def get_value(self) -> str: ...
def set_value(self, value: str) -> None: ...
def get_timestamp(self) -> datetime: ...
def get_user(self) -> User: ...
@dataclass
class SimpleMessage:
value: str
user: User
timestamp: datetime = field(default_factory=datetime.now)
class ChatListener(Protocol):
def notify(self, message: Message) -> None: ...
class ChatWindow:
def __init__(self, user: User) -> None:
self.user = user
self.message_list: List[Message] = []
def notify(self, message: Message) -> None:
self.message_list.append(message)
class ChatSession:
def __init__(self) -> None:
self.registered_listeners: List[ChatListener] = []
def register(self, listener: ChatListener) -> None:
self.registered_listeners.append(listener)
def incoming_message(self, message: Message) -> None:
self.publish(message)
def publish(self, message: Message) -> None:
for listener in self.registered_listeners:
listener.notify(message)
References
- tian runhe: http://tianrunhe.wordpress.com/2012/03/21/design-an-online-chat-server/
- StackOverflow discussion on suitable design patterns: http://stackoverflow.com/questions/1198016/suitable-design-pattern-for-a-simple-chat-application
- Massive Tech Interview notes: http://massivetechinterview.blogspot.de/2018/02/design-chat.html