Object-Oriented Design for Online Chat Server
HardUpdated: Jan 1, 2026
1. Problem Statement
Design an online chat server that supports users exchanging messages directly or via sessions (chat rooms). Keep the design minimal and focus on clean class responsibilities and simple message distribution.
Core goals:
- Represent users and messages with essential metadata (sender, timestamp)
- Support sessions where participants join, send, and receive messages
- Allow clients to subscribe/unsubscribe to message streams
- Keep delivery loosely coupled (observer or simple broker)
2. System Requirements
Functional Requirements:
- Users and messages with timestamps and optional metadata
- Sessions (chat rooms) supporting join/leave and message broadcast
- Registration/unregistration of listeners (clients) that receive messages
- Send/receive APIs for publishing and consuming messages
Non-Functional Requirements:
- Concurrency: many users/sessions; low-latency delivery
- Scalability: handle high fan-out; pluggable broker if needed
- Durability/Retention: optional message TTL and trimming
- Loose coupling between producers and consumers
Constraints/Edge Cases:
- Large fan-out where synchronous notifications are expensive
- Message ordering and delivery semantics (at-most/at-least-once) left as implementation detail
3. Use Case Diagram
Actors: User (client), Admin (optional), Chat System
graph TB subgraph "Chat System" UC1["Send Message"] UC2["Receive Message"] UC3["Join Session"] UC4["Leave Session"] end User([User]) Admin([Admin]) User --> UC1 User --> UC2 User --> UC3 User --> UC4 Admin --> UC3 style User fill:#4CAF50,color:#fff style Admin fill:#FF9800,color:#fff
4. Class Diagram
Core Classes:
- User: identifier and nickname
- Message: interface defining value, timestamp, sender
- SimpleMessage: concrete message
- ChatListener: observer interface for message notifications
- ChatWindow: client-side listener storing received messages
- ChatSession: manages listeners; publishes incoming messages
- (Optional) ChatServer/Broker: distribution layer (not implemented here)
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
5. Activity Diagrams
Send Message
graph TB U[User types message] --> S[Client sends to ChatSession] S --> V[Validate/enqueue] V --> P[Publish to registered listeners] P --> L[Listeners deliver/store]
Subscribe Listener
graph TB U[Client requests subscribe] --> R[ChatSession register listener] R --> A[Add to registered list] A --> OK[Return success]
6. Java Implementation
import java.util.*;
import java.util.Date;
// 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;
public SimpleMessage(String value, User user) {
this.value = value;
this.user = user;
this.timestamp = new Date();
}
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public Date getTimestamp() { return timestamp; }
public void setTimestamp(Date ts) { this.timestamp = ts; }
public User getUser() { return user; }
}
// 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 = new ArrayList<>();
public ChatWindow(User user) { this.user = user; }
public void notify(Message newMessage) {
messageList.add(newMessage);
}
}
// ChatSession.java
public class ChatSession {
private List<ChatListener> registeredChatListeners = new ArrayList<>();
public void register(ChatListener listener) {
registeredChatListeners.add(listener);
}
public void incomingMessage(Message message) {
publish(message);
}
protected void publish(Message messageToPublish) {
for (ChatListener eachListener : registeredChatListeners) {
eachListener.notify(messageToPublish);
}
}
}
7. Python Implementation
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)