Sobes.tech
Senior

It is necessary to design a backend architecture for a messenger (Slack, Teams, Mattermost). Requirements and constraints: only 1x1 chats, no group chats only text messages, no attachments service accessible via the internet up to 10,000 users in the system messages should be delivered quickly, within a few seconds message order should be preserved, messages should not be duplicated ability to scroll back in chat history, i.e., messages need to be stored clients – mobile devices, no need to design them authorization does not need to be designed

sobes.tech AI

Answer from AI

For designing a backend of a messenger with 1x1 chats and text messages, considering the requirements, the following architecture can be proposed:

  1. API server in Go — accepts requests from clients, handles sending and receiving messages.

  2. Message storage — a relational database (e.g., PostgreSQL) with a messages table:

CREATE TABLE messages (
  id SERIAL PRIMARY KEY,
  sender_id INT NOT NULL,
  receiver_id INT NOT NULL,
  content TEXT NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
  • Indexes on (sender_id, receiver_id, created_at) for quick chat history retrieval.
  1. Message delivery — use WebSocket or long polling to ensure fast real-time message delivery.

  2. Order and deduplication — messages are numbered by creation time, the client receives them in created_at order. The server guarantees id uniqueness.

  3. Scaling — with 10,000 users, the load is moderate, and a single API server with a load balancer can be used.

  4. Caching — Redis can be added to cache the latest messages to speed up loading.

  5. Security — although authorization does not need to be designed, basic protection against DDoS and data validation should be considered.

Example of handling message sending in Go (simplified):

func SendMessage(w http.ResponseWriter, r *http.Request) {
    var msg Message
    if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
        http.Error(w, "Invalid input", http.StatusBadRequest)
        return
    }
    msg.CreatedAt = time.Now()
    // Save to DB
    err := db.QueryRow(`INSERT INTO messages (sender_id, receiver_id, content, created_at) VALUES ($1, $2, $3, $4) RETURNING id`,
        msg.SenderID, msg.ReceiverID, msg.Content, msg.CreatedAt).Scan(&msg.ID)
    if err != nil {
        http.Error(w, "DB error", http.StatusInternalServerError)
        return
    }
    // Send to receiver via WebSocket
    sendToReceiver(msg)
    w.WriteHeader(http.StatusOK)
}

This approach ensures fast message exchange, history storage, and a simplified architecture suitable for the specified requirements.