# Multi-Application Architecture & Naming Conventions **Audience**: DevOps Engineers, Infrastructure Team, Developers **Status**: Architecture Reference Document **Last Updated**: November 2025 --- ## Overview This document defines the **multi-application architecture** for Maple Open Technologies production infrastructure. The infrastructure is designed to support **multiple independent applications** (MaplePress, MapleFile, mapleopentech) sharing common infrastructure (Cassandra, Redis, Meilisearch) while maintaining clear boundaries and isolation. --- ## Architecture Principles ### 1. Shared Infrastructure, Isolated Applications ``` ┌─────────────────────────────────────────────────────────────────┐ │ SHARED INFRASTRUCTURE │ │ (Used by ALL apps: MaplePress, MapleFile, mapleopentech) │ ├─────────────────────────────────────────────────────────────────┤ │ Infrastructure Workers (1-5): │ │ - Manager Node (worker-1): Redis │ │ - Cassandra Cluster (workers 2,3,4) │ │ - Meilisearch (worker 5) │ │ │ │ Networks: │ │ - maple-private-prod (databases, cache, search) │ │ - maple-public-prod (reverse proxies + backends) │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ APPLICATION: MAPLEPRESS │ ├─────────────────────────────────────────────────────────────────┤ │ Worker 6 - MaplePress Backend + Proxy: │ │ Stack: maplepress │ │ │ │ Service: maplepress_backend │ │ Hostname: maplepress-backend │ │ Port: 8000 │ │ Networks: maple-private-prod + maple-public-prod │ │ Connects to: Cassandra, Redis, Meilisearch, Spaces │ │ │ │ Service: maplepress_backend-caddy │ │ Hostname: caddy │ │ Domain: getmaplepress.ca (API) │ │ Proxies to: maplepress-backend:8000 │ │ │ │ Worker 7 - MaplePress Frontend: │ │ Stack: maplepress-frontend │ │ Service: maplepress-frontend_caddy │ │ Hostname: frontend-caddy │ │ Domain: getmaplepress.com (Web UI) │ │ Serves: /var/www/maplepress-frontend/ │ │ Calls: https://getmaplepress.ca (backend API) │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ APPLICATION: MAPLEFILE (Future) │ ├─────────────────────────────────────────────────────────────────┤ │ Worker 8 - MapleFile Backend + Proxy: │ │ Stack: maplefile │ │ │ │ Service: maplefile_backend │ │ Hostname: maplefile-backend │ │ Port: 8000 │ │ Networks: maple-private-prod + maple-public-prod │ │ Connects to: Cassandra, Redis, Meilisearch, Spaces │ │ │ │ Service: maplefile_backend-caddy │ │ Hostname: maplefile-backend-caddy │ │ Domain: maplefile.ca (API) │ │ Proxies to: maplefile-backend:8000 │ │ │ │ Worker 9 - MapleFile Frontend: │ │ Stack: maplefile-frontend │ │ Service: maplefile-frontend_caddy │ │ Hostname: maplefile-frontend-caddy │ │ Domain: maplefile.com (Web UI) │ │ Serves: /var/www/maplefile-frontend/ │ │ Calls: https://maplefile.ca (backend API) │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ APPLICATION: mapleopentech (Future) │ ├─────────────────────────────────────────────────────────────────┤ │ Worker 10 - mapleopentech Backend + Proxy: │ │ Stack: mapleopentech │ │ │ │ Service: mapleopentech_backend │ │ Hostname: mapleopentech-backend │ │ Port: 8000 │ │ Networks: maple-private-prod + maple-public-prod │ │ Connects to: Cassandra, Redis, Meilisearch, Spaces │ │ │ │ Service: mapleopentech_backend-caddy │ │ Hostname: mapleopentech-backend-caddy │ │ Domain: api.mapleopentech.io (API) │ │ Proxies to: mapleopentech-backend:8000 │ │ │ │ Worker 11 - mapleopentech Frontend: │ │ Stack: mapleopentech-frontend │ │ Service: mapleopentech-frontend_caddy │ │ Hostname: mapleopentech-frontend-caddy │ │ Domain: mapleopentech.io (Web UI) │ │ Serves: /var/www/mapleopentech-frontend/ │ │ Calls: https://api.mapleopentech.io (backend API) │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Naming Conventions ### Pattern: **Option C - Hybrid Stacks** **Strategy**: - Backend + Backend Caddy in **one stack** (deployed together) - Frontend Caddy in **separate stack** (independent deployment) **Why this pattern?** - Backend and its reverse proxy are tightly coupled → deploy together - Frontend is independent → deploy separately - Avoids redundant naming like `maplepress-backend_backend` - Clean service names: `maplepress_backend`, `maplepress_backend-caddy`, `maplepress-frontend_caddy` ### Stack Names | Application | Stack Name | Services in Stack | Purpose | |-------------|-----------------------|---------------------------------|--------------------------------------| | MaplePress | `maplepress` | `backend`, `backend-caddy` | Backend API + reverse proxy | | MaplePress | `maplepress-frontend` | `caddy` | Frontend static files | | MapleFile | `maplefile` | `backend`, `backend-caddy` | Backend API + reverse proxy | | MapleFile | `maplefile-frontend` | `caddy` | Frontend static files | | mapleopentech | `mapleopentech` | `backend`, `backend-caddy` | Backend API + reverse proxy | | mapleopentech | `mapleopentech-frontend` | `caddy` | Frontend static files | ### Service Names (Docker Auto-Generated) Docker Swarm automatically creates service names from: `{stack-name}_{service-name}` | Stack Name | Service in YAML | Full Service Name | Purpose | |-----------------------|------------------|-----------------------------|----------------------------------| | `maplepress` | `backend` | `maplepress_backend` | Go backend API | | `maplepress` | `backend-caddy` | `maplepress_backend-caddy` | Backend reverse proxy | | `maplepress-frontend` | `caddy` | `maplepress-frontend_caddy` | Frontend static file server | | `maplefile` | `backend` | `maplefile_backend` | Go backend API | | `maplefile` | `backend-caddy` | `maplefile_backend-caddy` | Backend reverse proxy | | `maplefile-frontend` | `caddy` | `maplefile-frontend_caddy` | Frontend static file server | **View services:** ```bash docker service ls # Output: # maplepress_backend 1/1 # maplepress_backend-caddy 1/1 # maplepress-frontend_caddy 1/1 # maplefile_backend 1/1 # maplefile_backend-caddy 1/1 # maplefile-frontend_caddy 1/1 ``` ### Hostnames (DNS Resolution Within Networks) Hostnames are defined in the stack YAML (`hostname: ...`) and used for container-to-container communication. | Application | Component | Hostname | Used By | |-------------|-----------------|-------------------------------|----------------------------------| | MaplePress | Backend | `maplepress-backend` | Caddy proxy, other services | | MaplePress | Backend Caddy | `caddy` | Internal reference (rarely used) | | MaplePress | Frontend Caddy | `frontend-caddy` | Internal reference (rarely used) | | MapleFile | Backend | `maplefile-backend` | Caddy proxy, other services | | MapleFile | Backend Caddy | `caddy` | Internal reference (rarely used) | | MapleFile | Frontend Caddy | `frontend-caddy` | Internal reference (rarely used) | **Example - Caddyfile for MaplePress backend:** ```caddy getmaplepress.ca www.getmaplepress.ca { reverse_proxy maplepress-backend:8000 # Uses hostname, not service name } ``` **Example - Caddyfile for MapleFile backend:** ```caddy maplefile.ca www.maplefile.ca { reverse_proxy maplefile-backend:8000 } ``` ### Docker Configs (Auto-Generated with Stack Prefix) | Stack Name | Config in YAML | Full Config Name | |-------------------------|----------------|-------------------------------------| | `maplepress` | `caddyfile` | `maplepress_caddyfile` | | `maplepress-frontend` | `caddyfile` | `maplepress-frontend_caddyfile` | | `maplefile` | `caddyfile` | `maplefile_caddyfile` | | `maplefile-frontend` | `caddyfile` | `maplefile-frontend_caddyfile` | **View configs:** ```bash docker config ls # Output: # maplepress_caddyfile # maplepress-frontend_caddyfile # maplefile_caddyfile # maplefile-frontend_caddyfile ``` ### File Paths | Application | Component | Path | |-------------|-----------|---------------------------------------| | MaplePress | Frontend | `/var/www/maplepress-frontend/` | | MaplePress | Backend | `/var/www/monorepo/cloud/mapleopentech-backend/` | | MapleFile | Frontend | `/var/www/maplefile-frontend/` | | MapleFile | Backend | `/var/www/monorepo/cloud/mapleopentech-backend/` | --- ## Resource Allocation ### Workers 1-5: Shared Infrastructure (ALL Apps) | Worker | Role | Services | Shared By | |--------|-----------------------|---------------------------------------|----------------| | 1 | Manager + Redis | Swarm manager, Redis cache | All apps | | 2 | Cassandra Node 1 | cassandra-1 | All apps | | 3 | Cassandra Node 2 | cassandra-2 | All apps | | 4 | Cassandra Node 3 | cassandra-3 | All apps | | 5 | Meilisearch | meilisearch (full-text search) | All apps | ### Workers 6-7: MaplePress Application | Worker | Role | Services | |--------|----------------------------|---------------------------------------------| | 6 | MaplePress Backend + Proxy | maplepress_backend, maplepress_backend-caddy | | 7 | MaplePress Frontend | maplepress-frontend_caddy | ### Workers 8-9: MapleFile Application (Future) | Worker | Role | Services | |--------|---------------------------|--------------------------------------------| | 8 | MapleFile Backend + Proxy | maplefile_backend, maplefile_backend-caddy | | 9 | MapleFile Frontend | maplefile-frontend_caddy | ### Workers 10-11: mapleopentech Application (Future) | Worker | Role | Services | |--------|----------------------------|---------------------------------------------| | 10 | mapleopentech Backend + Proxy | mapleopentech_backend, mapleopentech_backend-caddy | | 11 | mapleopentech Frontend | mapleopentech-frontend_caddy | --- ## Network Topology ### maple-private-prod (Shared by ALL Apps) **Purpose**: Private backend services - databases, cache, search **Services**: - Cassandra cluster (3 nodes) - Redis - Meilisearch - **All backend services** (maplepress-backend, maplefile-backend, mapleopentech-backend) **Security**: No ingress ports, no internet access, internal-only ### maple-public-prod (Per-App Reverse Proxies + Backends) **Purpose**: Internet-facing services - reverse proxies and backends **Services**: - **All backend services** (join both networks) - **All Caddy reverse proxies** (backend + frontend) **Security**: Ports 80/443 exposed on workers running Caddy --- ## Deployment Commands ### MaplePress ```bash # Backend + Backend Caddy (deployed together in one stack) docker stack deploy -c maplepress-stack.yml maplepress # Frontend (deployed separately) docker stack deploy -c maplepress-frontend-stack.yml maplepress-frontend ``` ### MapleFile (Future) ```bash # Backend + Backend Caddy (deployed together in one stack) docker stack deploy -c maplefile-stack.yml maplefile # Frontend (deployed separately) docker stack deploy -c maplefile-frontend-stack.yml maplefile-frontend ``` ### mapleopentech (Future) ```bash # Backend + Backend Caddy (deployed together in one stack) docker stack deploy -c mapleopentech-stack.yml mapleopentech # Frontend (deployed separately) docker stack deploy -c mapleopentech-frontend-stack.yml mapleopentech-frontend ``` --- ## Verification Commands ### List All Stacks ```bash docker stack ls # Expected output: # NAME SERVICES # cassandra 3 # maplepress 2 (backend + backend-caddy) # maplepress-frontend 1 (frontend caddy) # maplefile 2 (future) # maplefile-frontend 1 (future) # meilisearch 1 # redis 1 ``` ### List All Services ```bash docker service ls | sort # Expected output (partial): # cassandra_cassandra-1 1/1 # cassandra_cassandra-2 1/1 # cassandra_cassandra-3 1/1 # maplepress_backend 1/1 # maplepress_backend-caddy 1/1 # maplepress-frontend_caddy 1/1 # meilisearch_meilisearch 1/1 # redis_redis 1/1 ``` ### List All Configs ```bash docker config ls # Expected output: # maplepress_caddyfile # maplepress-frontend_caddyfile ``` --- ## Adding a New Application To add a new application (e.g., "MaplePortal"): ### 1. Update .env.template ```bash # Add new section # ============================================================================== # MAPLEPORTAL APPLICATION # ============================================================================== # Backend Configuration MAPLEPORTAL_BACKEND_DOMAIN=api.mapleportal.io MAPLEPORTAL_SPACES_BUCKET=mapleportal-prod MAPLEPORTAL_JWT_SECRET=CHANGEME MAPLEPORTAL_IP_ENCRYPTION_KEY=CHANGEME # Frontend Configuration MAPLEPORTAL_FRONTEND_DOMAIN=mapleportal.io MAPLEPORTAL_FRONTEND_API_URL=https://api.mapleportal.io ``` ### 2. Create New Workers ```bash # Worker 12 - Backend + Backend Caddy # Worker 13 - Frontend Caddy ``` ### 3. Follow Naming Convention - Stack names: `mapleportal` (backend + backend-caddy), `mapleportal-frontend` - Service names: `mapleportal_backend`, `mapleportal_backend-caddy`, `mapleportal-frontend_caddy` - Hostnames: `mapleportal-backend`, `mapleportal-backend-caddy`, `mapleportal-frontend-caddy` - Domains: `api.mapleportal.io` (backend), `mapleportal.io` (frontend) - Paths: `/var/www/mapleportal-frontend/` ### 4. Deploy Services ```bash # Backend + backend-caddy in one stack docker stack deploy -c mapleportal-stack.yml mapleportal # Frontend in separate stack docker stack deploy -c mapleportal-frontend-stack.yml mapleportal-frontend ``` --- ## Benefits of This Architecture ### 1. Clear Separation - Each app has dedicated workers and services - No naming conflicts between apps - Easy to identify which services belong to which app ### 2. Shared Infrastructure Efficiency - Single Cassandra cluster serves all apps - Single Redis instance (or sharded by app) - Single Meilisearch instance with app-prefixed indexes - Cost savings: 5 workers for infrastructure vs 15+ if each app had its own ### 3. Independent Scaling - Scale MaplePress without affecting MapleFile - Deploy new apps without touching existing ones - Remove apps without impacting infrastructure ### 4. Operational Clarity ```bash # View only MaplePress services docker service ls | grep maplepress # View only MapleFile services docker service ls | grep maplefile # Restart MaplePress backend docker service update --force maplepress_backend # Remove MapleFile entirely (if needed) docker stack rm maplefile docker stack rm maplefile-frontend ``` ### 5. Developer Friendly - Developers instantly know which app they're working with - No ambiguous "backend" or "frontend" names - Service discovery is intuitive: `maplepress-backend:8000` --- ## Migration Checklist (For Existing Deployments) If you deployed with old naming (`caddy`, `maplepress`, `frontend-caddy`), migrate like this: ### Step 1: Update Configuration Files Locally ```bash cd ~/monorepo/cloud/infrastructure/production # Update all YAML files to use new naming # - maplepress → maplepress-backend # - caddy → maplepress-backend-caddy # - frontend-caddy → maplepress-frontend-caddy # Update Caddyfiles to use new hostnames # - backend:8000 → maplepress-backend:8000 ``` ### Step 2: Remove Old Stacks ```bash # On manager node docker stack rm maplepress docker stack rm caddy docker stack rm frontend-caddy # Wait for cleanup sleep 10 # Remove old configs docker config rm caddy_caddyfile docker config rm frontend-caddy_caddyfile ``` ### Step 3: Deploy New Stacks ```bash # Deploy with new names (Option C naming) docker stack deploy -c maplepress-stack.yml maplepress docker stack deploy -c maplepress-frontend-stack.yml maplepress-frontend ``` ### Step 4: Verify ```bash docker service ls # Should show: # maplepress_backend # maplepress_backend-caddy # maplepress-frontend_caddy docker config ls # Should show: # maplepress_caddyfile # maplepress-frontend_caddyfile ``` --- **Last Updated**: November 2025 **Maintained By**: Infrastructure Team **Status**: Production Standard - Follow for All New Applications