Files
orion/docs/architecture/diagrams/multitenant-diagrams.md

25 KiB

Multi-Domain Architecture Diagram

Current vs New Architecture

BEFORE (Current Setup)

┌─────────────────────────────────────────────────────────────────┐
│                     Your FastAPI Application                     │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │           Vendor Context Middleware                        │ │
│  │                                                            │ │
│  │  Check Host header:                                        │ │
│  │  • vendor1.platform.com  →  Query Vendor.subdomain         │ │
│  │  • /vendor/vendor1/      →  Query Vendor.subdomain         │ │
│  └────────────────────────────────────────────────────────────┘ │
│                             ↓                                    │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │              Database: vendors table                       │ │
│  │  ┌──────────────────────────────────────────────────────┐  │ │
│  │  │ id │ subdomain │ name        │ is_active           │  │ │
│  │  ├────┼───────────┼─────────────┼─────────────────────┤  │ │
│  │  │ 1  │ vendor1   │ Shop Alpha  │ true                │  │ │
│  │  │ 2  │ vendor2   │ Shop Beta   │ true                │  │ │
│  │  └──────────────────────────────────────────────────────┘  │ │
│  └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Customers access via:
  → vendor1.platform.com     (production)
  → /vendor/vendor1/         (development)

AFTER (With Custom Domains)

┌─────────────────────────────────────────────────────────────────┐
│                     Your FastAPI Application                     │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │           Enhanced Vendor Context Middleware                │ │
│  │                                                            │ │
│  │  Priority 1: Check if custom domain                        │ │
│  │  • customdomain1.com  →  Query VendorDomain.domain         │ │
│  │                                                            │ │
│  │  Priority 2: Check if subdomain                            │ │
│  │  • vendor1.platform.com  →  Query Vendor.subdomain         │ │
│  │                                                            │ │
│  │  Priority 3: Check if path-based                           │ │
│  │  • /vendor/vendor1/  →  Query Vendor.subdomain             │ │
│  └────────────────────────────────────────────────────────────┘ │
│                             ↓                                    │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │              Database: vendors table                       │ │
│  │  ┌──────────────────────────────────────────────────────┐  │ │
│  │  │ id │ subdomain │ name        │ is_active           │  │ │
│  │  ├────┼───────────┼─────────────┼─────────────────────┤  │ │
│  │  │ 1  │ vendor1   │ Shop Alpha  │ true                │  │ │
│  │  │ 2  │ vendor2   │ Shop Beta   │ true                │  │ │
│  │  └──────────────────────────────────────────────────────┘  │ │
│  └────────────────────────────────────────────────────────────┘ │
│                             ↓                                    │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │            NEW TABLE: vendor_domains                       │ │
│  │  ┌──────────────────────────────────────────────────────┐  │ │
│  │  │ id │ vendor_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 - Vendor 1)
  → shop.alpha.com           (custom domain - Vendor 1)
  → customdomain2.com        (custom domain - Vendor 2)
  → vendor1.platform.com     (subdomain - still works!)
  → /vendor/vendor1/         (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                        │
│                                                         │
│  ┌───────────────────────────────────────────────────┐  │
│  │  Vendor Context Middleware                        │  │
│  │                                                   │  │
│  │  host = "customdomain1.com"                       │  │
│  │                                                   │  │
│  │  Step 1: Is it a custom domain?                   │  │
│  │  not host.endswith("platform.com")  → YES         │  │
│  │                                                   │  │
│  │  Step 2: Query vendor_domains table               │  │
│  │  SELECT * FROM vendor_domains                     │  │
│  │  WHERE domain = 'customdomain1.com'               │  │
│  │  AND is_active = true                             │  │
│  │  AND is_verified = true                           │  │
│  │                                                   │  │
│  │  Result: vendor_id = 1                            │  │
│  │                                                   │  │
│  │  Step 3: Load Vendor 1                            │  │
│  │  SELECT * FROM vendors WHERE id = 1               │  │
│  │                                                   │  │
│  │  Step 4: Set request state                        │  │
│  │  request.state.vendor = Vendor(id=1, ...)         │  │
│  └───────────────────────────────────────────────────┘  │
│                          ↓                              │
│  ┌───────────────────────────────────────────────────┐  │
│  │  Route Handler                                    │  │
│  │                                                   │  │
│  │  @router.get("/")                                 │  │
│  │  def shop_home(request):                          │  │
│  │      vendor = request.state.vendor  # Vendor 1    │  │
│  │                                                   │  │
│  │      # All queries auto-scoped to Vendor 1        │  │
│  │      products = get_products(vendor.id)           │  │
│  │                                                   │  │
│  │      return render("shop.html", {                 │  │
│  │          "vendor": vendor,                        │  │
│  │          "products": products                     │  │
│  │      })                                           │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
           │
           │ HTML Response
           ↓
┌──────────────────────┐
│  Customer Browser    │
│                      │
│  Sees:               │
│  Vendor 1's shop     │
│  at customdomain1.com│
└──────────────────────┘

Scenario 2: Customer visits vendor1.platform.com (subdomain)

Customer → DNS → Server → Nginx → FastAPI

FastAPI Middleware:
  host = "vendor1.platform.com"
  
  Step 1: Custom domain? NO (ends with .platform.com)
  Step 2: Subdomain? YES
          Extract "vendor1"
          Query: SELECT * FROM vendors 
                 WHERE subdomain = 'vendor1'
          Result: Vendor 1
  
  request.state.vendor = Vendor 1
  
Route → Render Vendor 1's shop

Scenario 3: Development - localhost:8000/vendor/vendor1/

Customer → localhost:8000/vendor/vendor1/

FastAPI Middleware:
  host = "localhost:8000"
  path = "/vendor/vendor1/"
  
  Step 1: Custom domain? NO (localhost)
  Step 2: Subdomain? NO (localhost has no subdomain)
  Step 3: Path-based? YES
          Extract "vendor1" from path
          Query: SELECT * FROM vendors 
                 WHERE subdomain = 'vendor1'
          Result: Vendor 1
  
  request.state.vendor = Vendor 1
  request.state.clean_path = "/" (strip /vendor/vendor1)
  
Route → Render Vendor 1's shop

Database Relationships

┌─────────────────────────────────────────┐
│            vendors                      │
├─────────────────────────────────────────┤
│ id (PK)                                 │
│ subdomain (UNIQUE)                      │
│ name                                    │
│ is_active                               │
│ ...                                     │
└─────────────────┬───────────────────────┘
                  │
                  │ One-to-Many
                  │
        ┌─────────┴──────────┐
        │                    │
        ↓                    ↓
┌───────────────────┐  ┌─────────────────────┐
│  vendor_domains   │  │    products         │
├───────────────────┤  ├─────────────────────┤
│ id (PK)           │  │ id (PK)             │
│ vendor_id (FK)    │  │ vendor_id (FK)      │
│ domain (UNIQUE)   │  │ name                │
│ is_primary        │  │ price               │
│ is_active         │  │ ...                 │
│ is_verified       │  └─────────────────────┘
│ verification_token│
│ ...               │
└───────────────────┘

Example Data:

vendors:
  id=1, subdomain='vendor1', name='Shop Alpha'
  id=2, subdomain='vendor2', name='Shop Beta'

vendor_domains:
  id=1, vendor_id=1, domain='customdomain1.com', is_verified=true
  id=2, vendor_id=1, domain='shop.alpha.com', is_verified=true
  id=3, vendor_id=2, domain='customdomain2.com', is_verified=true

products:
  id=1, vendor_id=1, name='Product A'  ← Belongs to Vendor 1
  id=2, vendor_id=1, name='Product B'  ← Belongs to Vendor 1
  id=3, vendor_id=2, name='Product C'  ← Belongs to Vendor 2

Middleware Decision Tree

                    [HTTP Request Received]
                            │
                            ↓
                    ┌───────────────┐
                    │ Extract Host  │
                    │ from headers  │
                    └───────┬───────┘
                            │
                            ↓
              ┌─────────────────────────┐
              │ Is admin request?       │
              │ (admin.* or /admin)     │
              └────┬────────────────┬───┘
                   │ YES            │ NO
                   ↓                │
         [Skip vendor detection]    │
         Admin routing              │
                                    ↓
                   ┌────────────────────────────┐
                   │ Does host end with         │
                   │ .platform.com or localhost?│
                   └────┬───────────────────┬───┘
                        │ NO               │ YES
                        │                  │
                        ↓                  ↓
         ┌──────────────────────┐  ┌──────────────────────┐
         │ CUSTOM DOMAIN        │  │ Check for subdomain  │
         │                      │  │ or path prefix       │
         │ Query:               │  │                      │
         │ vendor_domains table │  │ Query:               │
         │ WHERE domain = host  │  │ vendors table        │
         │                      │  │ WHERE subdomain = X  │
         └──────────┬───────────┘  └──────────┬───────────┘
                    │                         │
                    │                         │
                    └─────────┬───────────────┘
                              │
                              ↓
                    ┌─────────────────┐
                    │ Vendor found?   │
                    └────┬────────┬───┘
                         │ YES    │ NO
                         ↓        ↓
              [Set request.state.vendor]  [404 or homepage]
                         │
                         ↓
              [Continue to route handler]

Full System Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         Internet                                    │
└────────────────────────────┬────────────────────────────────────────┘
                             │
         ┌───────────────────┼───────────────────┐
         │                   │                   │
         ↓                   ↓                   ↓
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ customdomain1.  │ │ vendor1.        │ │ 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. Vendor Context ← Detects vendor from domain          │ │
│  │  3. Auth                                                 │ │
│  └──────────────────────────────────────────────────────────┘ │
│                             ↓                                  │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │  Route Handlers                                          │ │
│  │  - Shop pages (vendor-scoped)                            │ │
│  │  - Admin pages                                           │ │
│  │  - API endpoints                                         │ │
│  └──────────────────────────────────────────────────────────┘ │
│                             ↓                                  │
│  ┌──────────────────────────────────────────────────────────┐ │
│  │  Database Queries                                        │ │
│  │  All queries filtered by:                                │ │
│  │  WHERE vendor_id = request.state.vendor.id               │ │
│  └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
                             │
                             ↓
              ┌──────────────────────────────┐
              │   PostgreSQL Database        │
              │                              │
              │   Tables:                    │
              │   - vendors                  │
              │   - vendor_domains ← NEW!    │
              │   - products                 │
              │   - customers                │
              │   - orders                   │
              └──────────────────────────────┘

DNS Configuration Examples

Vendor 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: _wizamart-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 vendor_domains table
  5. Finds vendor_id = 1
  6. Shows Vendor 1's shop