Chat Application
A messaging app that treats a one-to-one conversation and a group conversation as the same class, and treats "who's online" as a question separate from "who said what."
Requirements
Functional
- Two users can start a direct chat and exchange text messages.
- A group of users can share a chat room where every member sees every message.
- A user's messages show up in order within a room, to everyone in it.
- Other users can see whether a given user is currently online.
Non-functional
- Adding a group chat feature must not require a separate code path from direct chat - a direct chat is just a room with two members.
- Presence updates (online/offline) must reach members without those members polling for it on every message send.
Design
ChatRoom doesn't distinguish direct from group - it's a set of Users and an ordered
list of Messages either way. A ChatService creates rooms and routes sends; a
PresenceTracker owns online/offline state and notifies interested rooms, so a message
send never has to ask "is this person online" itself.
- 1The sender only knows a room id - never whether the room is direct or group.
- 2The service looks up the room and appends the message; the room enforces membership.
- 3Every room keeps one ordered message list, regardless of member count.
- 4Presence is updated independently of any message being sent.
- 5The tracker calls back into every registered room; each room ignores the update unless the user is actually one of its members.
The only thing that varies between "direct" and "group" is how the room got created - two users versus a roster - never how a message flows through it once it exists.
Class diagram
implementsuses
Code
Design decisions
- No
DirectChatRoom/GroupChatRoomsubclasses. A direct chat is aChatRoomwith exactly two members; nothing about sending, ordering, or reading a message differs by member count, so a subclass hierarchy would exist only to enforce "exactly two," which a factory method (ChatService.createDirectChat) can do just as well without the extra types. - Presence is a separate tracker, not a field the room polls. If
ChatRoomasked each member "are you online" on every send, presence and messaging would be coupled for no reason.PresenceTrackerinstead pushes status changes outward as events, so a room only reacts when something actually changes. Messageis immutable once created. A sent message never mutates - edits or deletes in a real system would be new events referencing the original message's id, not a change to the message object, which keeps message ordering and delivery guarantees simple to reason about.- What's missing for a real system: this models everything in-memory and synchronously; a production version would persist messages before acknowledging the sender, page a room's history instead of loading it whole, and give the client a way to know it's missed messages while offline (a per-user last-read cursor per room).
Common follow-ups
- How would you show "typing…" indicators? The same shape as presence - a separate
short-lived status pushed through the same
PresenceObserver-style callback intoChatRoom, rather than making the room poll for typing state on every render. - How do read receipts fit into this model? A per-(user, room) "last read message id"
cursor, most naturally living alongside
ChatRoom's membership rather than onMessageitself - a read receipt is a per-viewer fact about history, not a fact about the message. - Two users share 100 group chats and one goes offline - does
PresenceTrackernotify all 100 rooms one at a time? Yes -setOfflinecalls every registered observer, so it costs work proportional to every room the tracker knows about per status change. A real system would only notify rooms whose membership actually includes that user, closer to what an observer list keyed by user (instead of a flat list of every room) would give. - Why can a direct chat's
ChatRoomstill technically accept a third member? BecauseChatRoomenforces no maximum - "exactly two" is a rule applied only at creation, byChatService.createDirectChatpassing exactly two users to the constructor. Nothing stopsaddMemberfrom being called on that same room afterward, a real gap this model would need a room-type check or a capped subtype to close.
Check yourself
Question 1 of 4
Why is there no DirectChatRoom/GroupChatRoom class hierarchy?