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
Service Communication Patterns
The platform follows a straightforward request/response model with asynchronous side-effects:
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.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-queuestored in Redis. The Message Queue Worker picks up the job and processes it independently.Shared Database – All services connect to the same MongoDB instance using the
MongooseClientsingleton. Each service accesses only the collections relevant to its domain, but there is no hard schema-level isolation.
Data Flow
Read Path
- Client sends an authenticated request (JWT in
Authorization: Bearerheader). - Nginx applies rate limiting and forwards to the target service.
- The service middleware validates the JWT.
- The route handler queries MongoDB via the appropriate Model class (which extends
BaseModel). - The response is returned through Nginx to the client.
Write Path
- Client submits a payload validated by Zod schemas (via
express-zod-safe). - The service creates or updates documents in MongoDB using
BaseModelmethods (create,patch). - 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
- The Message Queue Worker process runs independently (no HTTP port).
- It subscribes to the
email-queuein Redis. - For each job, it calls
EmailClient.send_email_now(), which uses the Resend API. - 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:
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 /_pingreturns200 OK(no body).GET /healthreturns200 { "status": "ok" }. - Nginx:
GET /healthon port 80 returns200 "healthy\n". - MongoDB:
mongosh --eval "db.adminCommand('ping')". - Redis:
redis-cli --raw incr ping. - Message Queue Worker: Process detection via
pgrep -f node.