App Architecture
System Architecture

Architecture Design

Finally, it's time for architecture design! By now, we’ve laid down a solid foundation for the application — the database schema, the RPC calls. With all these in mind, we can proceed to write down the list of components in the system.

Components

  1. Chat Service: Each online user maintains a WebSocket connection with a WebSocket server in the Chat Service. Outgoing and incoming chat messages are exchanged here.

    // WebSocket setup example
    const WebSocket = require('ws');
     
    const ws = new WebSocket('wss://example.com/chat');
     
    ws.on('open', function open() {
      console.log('WebSocket connection established');
    });
     
    ws.on('message', function incoming(data) {
      console.log(`Received message: ${data}`);
    });
     
    ws.on('close', function close() {
      console.log('WebSocket connection closed');
    });
     
    // Send a message
    function sendMessage(message) {
      ws.send(message);
    }
  2. Web Service: It handles all RPC calls except send_message(). Users interact with this service for authentication, join/leave groups, etc. No WebSocket is needed here since all calls are client-initiated and HTTP-based.

    // Example RPC call using HTTP
    const axios = require('axios');
     
    function authenticateUser(username, password) {
      return axios.post('https://example.com/authenticate', { username, password })
        .then(response => response.data)
        .catch(error => console.error('Error authenticating user:', error));
    }
     
    function joinGroup(groupId) {
      return axios.post('https://example.com/joinGroup', { groupId })
        .then(response => response.data)
        .catch(error => console.error('Error joining group:', error));
    }
  3. User Mapping Service: Our chat service is globally distributed. We need to keep track of the server ID of the user's session host.

    // Example of user mapping
    const userSessions = new Map(); // In-memory store for user sessions
     
    function mapUserToServer(userId, serverId) {
      userSessions.set(userId, serverId);
    }
     
    function getUserServer(userId) {
      return userSessions.get(userId);
    }
  4. Translation Service: The messages stored inside the database get automatically translated into the receiver's preferred language for two-way translation.

    // Example translation using Google Cloud Translation API
    const { TranslationServiceClient } = require('@google-cloud/translate').v3beta1;
     
    const translationClient = new TranslationServiceClient();
     
    async function translateMessage(message, targetLanguage) {
      const request = {
        parent: 'projects/YOUR_PROJECT_ID/locations/global',
        contents: [message],
        mimeType: 'text/plain',
        sourceLanguageCode: 'en',
        targetLanguageCode: targetLanguage,
      };
      
      const [response] = await translationClient.translateText(request);
      return response.translations[0].translatedText;
    }
  5. Subscription Management Service: This service allows users to choose between free and paid plans for our application. Based on the plan, users are provided with features corresponding to their plans.

    // Example subscription management
    const stripe = require('stripe')('your-stripe-secret-key');
     
    async function createSubscription(customerId, planId) {
      const subscription = await stripe.subscriptions.create({
        customer: customerId,
        items: [{ plan: planId }],
      });
      return subscription;
    }
     
    async function getSubscriptionDetails(subscriptionId) {
      const subscription = await stripe.subscriptions.retrieve(subscriptionId);
      return subscription;
    }

This architecture design breaks down the application into distinct services, each responsible for specific functionalities, ensuring modularity and scalability.