Let's say that I have a chatroom application that manages rooms, users, and messages. I'm building this out as an opportunity to practice some service/manager/web separation and teach myself good application design.
With the following classes:
Model:
public class Room {
private final String id = UUID.randomUUID().toString();
private boolean open = true;
private List<Message> messages = new ArrayList<Message>();
public void addMessage(Message m) {
synchronized(messages) {
messages.add(m);
}
}
public void close() {
this.open = false;
}
public List<Message> getMessages() {
synchronized(messages) {
return new ArrayList<>(messages);
}
}
}
Manager:
public interface RoomManager {
public void add(Room r);
public Room get(String id);
}
Service:
public class RoomService {
public void postMessage(Message m, Room r) {
if (!r.isOpen()) {
throw new IllegalArgumentException("Room " + r + " is closed");
}
r.addMessage(m);
}
}
The Message
class is unexciting - a String
contents and maybe a timestamp.
What I'm struggling with is how to layout this design and hopefully answer a few niggling questions that are plaguing me.
Assuming that this above framework will be used in a web service with multiple incoming requests:
A web request to post a message to a room will only have the ID for the room. Should the lookup (translation from
String roomId
->Room room
) happen at the web layer? Or should the service method be altered to accept the id of the Room instead of the Room itself?There's a race condition in the
postMessage()
method wherein the status of the room might change between checking it, and actually posting the message. Is the service level the right place for this synchronization to take place?I ideally want the service to not have to know what kind of manager it's dealing with - whether it's a database, an in-memory solution, etc. Where should thread safety be determined, at the model, manager, or service level? If I add
@Transactional
to the service level, it won't help when dealing with an in-memory manager. But adding some kind of object-based synchronization to the service might unnecessarily prevent multiple database queries (that are otherwise unrelated - ie. posting two messages to two separate rooms) from running in parallel. Obviously if we're dealing with a database query, thread safety at the model level is a moot point, since unless we're dealing with a local cache or in-memory model manager, I'm likely to get two separate instances to the same object.
These seem like fairly fundamental questions but I'm having a hard time finding books, tutorials, talks, etc. that answer these kinds of higher-level design questions. I understand concurrency and the various tools (re-entrant locks, synchronized blocks, etc.) but putting them into practice in a sane, manageable way seems to be a DIY endeavor.
Hoping someone can help answer the above questions, or provide some resources that might help me answer them myself.