Architecture Overview

This page describes the high-level architecture of the eTeamups Platform, including how clients reach backend services, how services communicate with data stores, and how external integrations fit into the picture.

High-Level System Diagram

graph TB subgraph Clients HUB["Zeswa Hub<br/>(zeswa.com, port 4000)"] ADMIN["Admin Portal<br/>(admin.zeswa.com, port 3000)"] end subgraph "Nginx Reverse Proxy (port 443 / 80)" NGINX["Nginx<br/>TLS Termination<br/>Rate Limiting<br/>Path-based Routing"] end subgraph "Application Services" AUTH["Auth Service<br/>:9000"] PROFILE["Profile Service<br/>:9100"] ORG["Organisation Service<br/>:9107"] MEDIA["Media Service<br/>:9102"] MQ["Message Queue Worker"] end subgraph "Data Stores" MONGO[("MongoDB 7<br/>16 Collections")] REDIS[("Redis 7<br/>Queue Backend")] end subgraph "External Services" RESEND["Resend<br/>(Email API)"] SIGNOZ["SigNoz<br/>(Observability)"] end HUB -->|HTTPS| NGINX ADMIN -->|HTTPS| NGINX NGINX -->|/auth| AUTH NGINX -->|/profile| PROFILE NGINX -->|/organisation| ORG NGINX -->|/media| MEDIA AUTH --> MONGO PROFILE --> MONGO ORG --> MONGO MEDIA --> MONGO MQ --> MONGO AUTH -->|enqueue email| REDIS MQ -->|dequeue jobs| REDIS MQ -->|send email| RESEND AUTH -.->|OTLP traces + logs| SIGNOZ PROFILE -.->|OTLP traces + logs| SIGNOZ ORG -.->|OTLP traces + logs| SIGNOZ MEDIA -.->|OTLP traces + logs| SIGNOZ MQ -.->|OTLP traces + logs| SIGNOZ

Service Communication Patterns

The platform follows a straightforward request/response model with asynchronous side-effects:

  1. Synchronous HTTP – Every client request enters through Nginx, which strips the service prefix (e.g., /auth) and proxies to the correct upstream over HTTP/1.1 on the internal Docker bridge network.

  2. Asynchronous Job Queue – When a service needs to perform a non-blocking operation (currently limited to email delivery), it pushes a job onto the BullMQ email-queue stored in Redis. The Message Queue Worker picks up the job and processes it independently.

  3. Shared Database – All services connect to the same MongoDB instance using the MongooseClient singleton. Each service accesses only the collections relevant to its domain, but there is no hard schema-level isolation.

sequenceDiagram participant Client participant Nginx participant AuthService as Auth Service participant Redis participant Worker as MQ Worker participant Resend participant MongoDB Client->>Nginx: POST /auth/login Nginx->>AuthService: POST /login (stripped prefix) AuthService->>MongoDB: Look up account MongoDB-->>AuthService: Account record AuthService->>MongoDB: Create OTP record AuthService->>Redis: Enqueue email job AuthService-->>Nginx: 200 { accessToken, refreshToken } Nginx-->>Client: 200 JSON Redis-->>Worker: Dequeue email job Worker->>Resend: POST email (OTP) Resend-->>Worker: Email sent

Data Flow

Read Path

  1. Client sends an authenticated request (JWT in Authorization: Bearer header).
  2. Nginx applies rate limiting and forwards to the target service.
  3. The service middleware validates the JWT.
  4. The route handler queries MongoDB via the appropriate Model class (which extends BaseModel).
  5. The response is returned through Nginx to the client.

Write Path

  1. Client submits a payload validated by Zod schemas (via express-zod-safe).
  2. The service creates or updates documents in MongoDB using BaseModel methods (create, patch).
  3. If the write triggers a side-effect (e.g., sending an OTP email), the service pushes a job to the Redis-backed BullMQ queue.

Async Processing Path

  1. The Message Queue Worker process runs independently (no HTTP port).
  2. It subscribes to the email-queue in Redis.
  3. For each job, it calls EmailClient.send_email_now(), which uses the Resend API.
  4. On success the job is marked complete; on failure BullMQ handles retries.

Network Architecture

All containers run on a single Docker bridge network named eteamups-network. Only Nginx and the two frontend applications expose ports to the host:

graph LR subgraph "Host Machine" subgraph "eteamups-network (Docker bridge)" NGINX["Nginx<br/>80, 443 exposed"] AUTH["Auth<br/>9000 internal"] PROFILE["Profile<br/>9100 internal"] ORG["Organisation<br/>9107 internal"] MEDIA["Media<br/>9102 internal"] MQ["MQ Worker<br/>no port"] MONGO["MongoDB<br/>27017 exposed"] REDIS["Redis<br/>6379 exposed"] ADMINP["Admin Portal<br/>3000 exposed"] HUBP["Zeswa Hub<br/>4000 exposed"] end end NGINX --- AUTH NGINX --- PROFILE NGINX --- ORG NGINX --- MEDIA NGINX --- ADMINP NGINX --- HUBP AUTH --- MONGO AUTH --- REDIS PROFILE --- MONGO ORG --- MONGO MEDIA --- MONGO MQ --- REDIS MQ --- MONGO

Port Allocation

Container Internal Port Exposed to Host Purpose
nginx 80, 443 Yes Reverse proxy, TLS termination
auth-service 9000 No (expose only) Auth API
profile-service 9100 No (expose only) Profile API
organisation-service 9107 No (expose only) Organisation API
media-service 9102 No (expose only) Media API
message-queue-service No BullMQ worker process
mongodb 27017 Yes Database
redis 6379 Yes Queue and cache
admin-portal 3000 Yes Admin frontend
zeswa-hub 4000 Yes Public frontend

DNS / Hostname Routing

Nginx listens on three server names and routes accordingly:

Hostname Destination
zeswa.com / www.zeswa.com Zeswa Hub (port 4000)
admin.zeswa.com Admin Portal (port 3000)
api.zeswa.com API Gateway – path-based routing to backend services

Container Images

Production container images are built per service and pushed to the GitHub Container Registry (GHCR):

Image Service
ghcr.io/creativeaura/eteamups-platform-auth:latest Auth Service
ghcr.io/creativeaura/eteamups-platform-profile:latest Profile Service
ghcr.io/creativeaura/eteamups-platform-organisation:latest Organisation Service
ghcr.io/creativeaura/eteamups-platform-media:latest Media Service
ghcr.io/creativeaura/eteamups-platform-message-queue:latest Message Queue Worker
ghcr.io/creativeaura/eteamups-platform-nginx:latest Nginx Reverse Proxy
ghcr.io/creativeaura/eteamsup-admin-portal:latest Admin Portal
ghcr.io/creativeaura/zeswa-hub:latest Zeswa Hub

Health Checks

Every service exposes health-check endpoints used by Docker for container orchestration:

  • Express services: GET /_ping returns 200 OK (no body). GET /health returns 200 { "status": "ok" }.
  • Nginx: GET /health on port 80 returns 200 "healthy\n".
  • MongoDB: mongosh --eval "db.adminCommand('ping')".
  • Redis: redis-cli --raw incr ping.
  • Message Queue Worker: Process detection via pgrep -f node.