# Multi-Domain Architecture Diagram ## Current vs New Architecture ### BEFORE (Current Setup) ``` ┌─────────────────────────────────────────────────────────────────┐ │ Your FastAPI Application │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Store Context Middleware │ │ │ │ │ │ │ │ Check Host header: │ │ │ │ • store1.platform.com → Query Store.subdomain │ │ │ │ • /store/store1/ → Query Store.subdomain │ │ │ └────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Database: stores table │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ │ │ id │ subdomain │ name │ is_active │ │ │ │ │ ├────┼───────────┼─────────────┼─────────────────────┤ │ │ │ │ │ 1 │ store1 │ Shop Alpha │ true │ │ │ │ │ │ 2 │ store2 │ Shop Beta │ true │ │ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ Customers access via: → store1.platform.com (production) → /store/store1/ (development) ``` ### AFTER (With Custom Domains) ``` ┌─────────────────────────────────────────────────────────────────┐ │ Your FastAPI Application │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Enhanced Store Context Middleware │ │ │ │ │ │ │ │ Priority 1: Check if custom domain │ │ │ │ • customdomain1.com → Query StoreDomain.domain │ │ │ │ │ │ │ │ Priority 2: Check if subdomain │ │ │ │ • store1.platform.com → Query Store.subdomain │ │ │ │ │ │ │ │ Priority 3: Check if path-based │ │ │ │ • /store/store1/ → Query Store.subdomain │ │ │ └────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Database: stores table │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ │ │ id │ subdomain │ name │ is_active │ │ │ │ │ ├────┼───────────┼─────────────┼─────────────────────┤ │ │ │ │ │ 1 │ store1 │ Shop Alpha │ true │ │ │ │ │ │ 2 │ store2 │ Shop Beta │ true │ │ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ NEW TABLE: store_domains │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ │ │ id │ store_id │ domain │ is_verified │ │ │ │ │ ├────┼───────────┼───────────────────┼───────────────┤ │ │ │ │ │ 1 │ 1 │ customdomain1.com │ true │ │ │ │ │ │ 2 │ 1 │ shop.alpha.com │ true │ │ │ │ │ │ 3 │ 2 │ customdomain2.com │ true │ │ │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ Customers can now access via: → customdomain1.com (custom domain - Store 1) → shop.alpha.com (custom domain - Store 1) → customdomain2.com (custom domain - Store 2) → store1.platform.com (subdomain - still works!) → /store/store1/ (path-based - still works!) ``` ## Request Flow Diagram ### Scenario 1: Customer visits customdomain1.com ``` ┌──────────────────────┐ │ Customer Browser │ │ │ │ Visit: │ │ customdomain1.com │ └──────────┬───────────┘ │ │ HTTP Request │ Host: customdomain1.com ↓ ┌──────────────────────┐ │ DNS Resolution │ │ │ │ customdomain1.com │ │ ↓ │ │ 123.45.67.89 │ (Your server IP) └──────────┬───────────┘ │ │ Routes to server ↓ ┌──────────────────────┐ │ Nginx/Web Server │ │ │ │ Receives request │ │ server_name _; │ (Accept ALL domains) │ │ │ Proxy to FastAPI │ │ with Host header │ └──────────┬───────────┘ │ │ proxy_set_header Host $host ↓ ┌─────────────────────────────────────────────────────────┐ │ FastAPI Application │ │ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Store Context Middleware │ │ │ │ │ │ │ │ host = "customdomain1.com" │ │ │ │ │ │ │ │ Step 1: Is it a custom domain? │ │ │ │ not host.endswith("platform.com") → YES │ │ │ │ │ │ │ │ Step 2: Query store_domains table │ │ │ │ SELECT * FROM store_domains │ │ │ │ WHERE domain = 'customdomain1.com' │ │ │ │ AND is_active = true │ │ │ │ AND is_verified = true │ │ │ │ │ │ │ │ Result: store_id = 1 │ │ │ │ │ │ │ │ Step 3: Load Store 1 │ │ │ │ SELECT * FROM stores WHERE id = 1 │ │ │ │ │ │ │ │ Step 4: Set request state │ │ │ │ request.state.store = Store(id=1, ...) │ │ │ └───────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Route Handler │ │ │ │ │ │ │ │ @router.get("/") │ │ │ │ def shop_home(request): │ │ │ │ store = request.state.store # Store 1 │ │ │ │ │ │ │ │ # All queries auto-scoped to Store 1 │ │ │ │ products = get_products(store.id) │ │ │ │ │ │ │ │ return render("shop.html", { │ │ │ │ "store": store, │ │ │ │ "products": products │ │ │ │ }) │ │ │ └───────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ │ HTML Response ↓ ┌──────────────────────┐ │ Customer Browser │ │ │ │ Sees: │ │ Store 1's shop │ │ at customdomain1.com│ └──────────────────────┘ ``` ### Scenario 2: Customer visits store1.platform.com (subdomain) ``` Customer → DNS → Server → Nginx → FastAPI FastAPI Middleware: host = "store1.platform.com" Step 1: Custom domain? NO (ends with .platform.com) Step 2: Subdomain? YES Extract "store1" Query: SELECT * FROM stores WHERE subdomain = 'store1' Result: Store 1 request.state.store = Store 1 Route → Render Store 1's shop ``` ### Scenario 3: Development - localhost:8000/store/store1/ ``` Customer → localhost:8000/store/store1/ FastAPI Middleware: host = "localhost:8000" path = "/store/store1/" Step 1: Custom domain? NO (localhost) Step 2: Subdomain? NO (localhost has no subdomain) Step 3: Path-based? YES Extract "store1" from path Query: SELECT * FROM stores WHERE subdomain = 'store1' Result: Store 1 request.state.store = Store 1 request.state.clean_path = "/" (strip /store/store1) Route → Render Store 1's shop ``` ## Database Relationships ``` ┌─────────────────────────────────────────┐ │ stores │ ├─────────────────────────────────────────┤ │ id (PK) │ │ subdomain (UNIQUE) │ │ name │ │ is_active │ │ ... │ └─────────────────┬───────────────────────┘ │ │ One-to-Many │ ┌─────────┴──────────┐ │ │ ↓ ↓ ┌───────────────────┐ ┌─────────────────────┐ │ store_domains │ │ products │ ├───────────────────┤ ├─────────────────────┤ │ id (PK) │ │ id (PK) │ │ store_id (FK) │ │ store_id (FK) │ │ domain (UNIQUE) │ │ name │ │ is_primary │ │ price │ │ is_active │ │ ... │ │ is_verified │ └─────────────────────┘ │ verification_token│ │ ... │ └───────────────────┘ Example Data: stores: id=1, subdomain='store1', name='Shop Alpha' id=2, subdomain='store2', name='Shop Beta' store_domains: id=1, store_id=1, domain='customdomain1.com', is_verified=true id=2, store_id=1, domain='shop.alpha.com', is_verified=true id=3, store_id=2, domain='customdomain2.com', is_verified=true products: id=1, store_id=1, name='Product A' ← Belongs to Store 1 id=2, store_id=1, name='Product B' ← Belongs to Store 1 id=3, store_id=2, name='Product C' ← Belongs to Store 2 ``` ## Middleware Decision Tree ``` [HTTP Request Received] │ ↓ ┌───────────────┐ │ Extract Host │ │ from headers │ └───────┬───────┘ │ ↓ ┌─────────────────────────┐ │ Is admin request? │ │ (admin.* or /admin) │ └────┬────────────────┬───┘ │ YES │ NO ↓ │ [Skip store detection] │ Admin routing │ ↓ ┌────────────────────────────┐ │ Does host end with │ │ .platform.com or localhost?│ └────┬───────────────────┬───┘ │ NO │ YES │ │ ↓ ↓ ┌──────────────────────┐ ┌──────────────────────┐ │ CUSTOM DOMAIN │ │ Check for subdomain │ │ │ │ or path prefix │ │ Query: │ │ │ │ store_domains table │ │ Query: │ │ WHERE domain = host │ │ stores table │ │ │ │ WHERE subdomain = X │ └──────────┬───────────┘ └──────────┬───────────┘ │ │ │ │ └─────────┬───────────────┘ │ ↓ ┌─────────────────┐ │ Store found? │ └────┬────────┬───┘ │ YES │ NO ↓ ↓ [Set request.state.store] [404 or homepage] │ ↓ [Continue to route handler] ``` ## Full System Architecture ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Internet │ └────────────────────────────┬────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ↓ ↓ ↓ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ customdomain1. │ │ store1. │ │ admin. │ │ com │ │ platform.com │ │ platform.com │ │ │ │ │ │ │ │ DNS → Server IP │ │ DNS → Server IP │ │ DNS → Server IP │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ └───────────────────┼───────────────────┘ │ ↓ ┌──────────────────────────────┐ │ Cloudflare / Load Balancer │ │ (Optional) │ │ - SSL Termination │ │ - DDoS Protection │ │ - CDN │ └──────────────┬───────────────┘ │ ↓ ┌──────────────────────────────┐ │ Nginx / Web Server │ │ │ │ server_name _; │ ← Accept ALL domains │ proxy_pass FastAPI; │ │ proxy_set_header Host; │ ← Pass domain info └──────────────┬───────────────┘ │ ↓ ┌────────────────────────────────────────────────────────────────┐ │ FastAPI Application │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Middleware Stack │ │ │ │ 1. CORS │ │ │ │ 2. Store Context ← Detects store from domain │ │ │ │ 3. Auth │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Route Handlers │ │ │ │ - Shop pages (store-scoped) │ │ │ │ - Admin pages │ │ │ │ - API endpoints │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Database Queries │ │ │ │ All queries filtered by: │ │ │ │ WHERE store_id = request.state.store.id │ │ │ └──────────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────────┘ │ ↓ ┌──────────────────────────────┐ │ PostgreSQL Database │ │ │ │ Tables: │ │ - stores │ │ - store_domains ← NEW! │ │ - products │ │ - customers │ │ - orders │ └──────────────────────────────┘ ``` ## DNS Configuration Examples ### Store 1 wants to use customdomain1.com **At Domain Registrar (GoDaddy/Namecheap/etc):** ``` Type: A Name: @ Value: 123.45.67.89 (your server IP) TTL: 3600 Type: A Name: www Value: 123.45.67.89 TTL: 3600 Type: TXT Name: _orion-verify Value: abc123xyz (verification token from your platform) TTL: 3600 ``` **After DNS propagates (5-15 mins):** 1. Customer visits customdomain1.com 2. DNS resolves to your server 3. Nginx accepts request 4. FastAPI middleware queries store_domains table 5. Finds store_id = 1 6. Shows Store 1's shop