{"slug":"operations/TENANT_ARCHITECTURE","title":"Tenant Routing Architecture","section":"operations","sectionLabel":"Operations","status":"unclassified","type":"document","path":"operations/TENANT_ARCHITECTURE.mdx","bytes":18241,"description":null,"updated":null,"source":"integratewise/Integratewise-docs-wiki","content":"\n## System Overview\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        Browser Request                          │\n│                  saas-corp.localhost:3000/                      │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                      Next.js Middleware                         │\n│  (middleware.ts)                                                │\n│  ────────────────────────────────────────────────────────────   │\n│  1. Extract subdomain: \"saas-corp\"                              │\n│  2. Check if role (founder, sales) → NO                         │\n│  3. Check if reserved (www, app, api) → NO                      │\n│  4. Look up tenant slug \"saas-corp\" → FOUND                     │\n│  5. Inject headers:                                             │\n│     X-Tenant-Slug: saas-corp                                    │\n│     X-Tenant-Id: tenant-1                                       │\n│  6. Route to (tenant) group                                     │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                    Next.js Routing                              │\n│           Route to: app/(tenant)/layout.tsx                     │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│              (tenant)/layout.tsx - Server Component             │\n│  ────────────────────────────────────────────────────────────   │\n│  Wraps children with:                                           │\n│  <TenantProvider>{children}</TenantProvider>                    │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│          TenantProvider - Client Component (Context)            │\n│  (lib/context/tenant-context.tsx)                               │\n│  ────────────────────────────────────────────────────────────   │\n│  1. Read headers via headers() → X-Tenant-Slug: \"saas-corp\"     │\n│  2. Load from DataProvider:                                     │\n│     provider.tenants.getBySlug(\"saas-corp\")                     │\n│  3. Get Tenant object:                                          │\n│     {                                                           │\n│       id: \"tenant-1\",                                           │\n│       name: \"SaaS Corp\",                                        │\n│       slug: \"saas-corp\",                                        │\n│       industry: \"saas\",                                         │\n│       branding: {                                               │\n│         logoUrl: \"🚀\",                                          │\n│         primaryColor: \"#3B82F6\",                                │\n│         backgroundColor: \"#F0F9FF\",                             │\n│         tagline: \"Build, grow, and scale\"                       │\n│       },                                                        │\n│       landing: { ... },                                         │\n│       status: \"active\"                                          │\n│     }                                                           │\n│  4. Provide via React Context                                   │\n│     useTenant() → returns Tenant object                         │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│         (tenant)/page.tsx - Server Component                    │\n│  ────────────────────────────────────────────────────────────   │\n│  Renders TenantLandingPage (client component)                   │\n└────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n┌─────────────────────────────────────────────────────────────────┐\n│     TenantLandingPage - Client Component                        │\n│  (components/tenant/landing-page.tsx)                           │\n│  ────────────────────────────────────────────────────────────   │\n│  const tenant = useTenant()                                     │\n│                                                                 │\n│  Renders:                                                       │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │ Navigation                                              │   │\n│  │  🚀 SaaS Corp | [Sign In]                               │   │\n│  └─────────────────────────────────────────────────────────┘   │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │ Hero Section                                            │   │\n│  │  \"Unified SaaS Operations\"                              │   │\n│  │  \"Connect your entire SaaS stack\"                       │   │\n│  │  [Get Started]                                          │   │\n│  └─────────────────────────────────────────────────────────┘   │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │ Features Grid                                           │   │\n│  │  🔗 One-Click Integrations | ⚡ Intelligent Workflows  │   │\n│  │  👥 Real-Time Collaboration | 🔒 Enterprise Security   │   │\n│  └─────────────────────────────────────────────────────────┘   │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │ SaaS-Specific Section                                  │   │\n│  │  Built for SaaS                                         │   │\n│  │  20+ Pre-built Templates | 60+ Available Integrations  │   │\n│  └─────────────────────────────────────────────────────────┘   │\n│                                                                 │\n│  CSS Variables Applied:                                        │\n│  --primary-color: #3B82F6                                      │\n│  --background-color: #F0F9FF                                   │\n└─────────────────────────────┬────────────────────────────────────┘\n                             │\n                             ▼\n                     ┌──────────────┐\n                     │   Browser    │\n                     │   Renders    │\n                     │ Landing Page │\n                     │   (branded)  │\n                     └──────────────┘\n```\n\n## Component Hierarchy\n\n```\nRoot Layout\n  ↓\n(tenant) Route Group Layout\n  ├─ TenantProvider (Context)\n  │  ├─ Reads: X-Tenant-Slug header\n  │  ├─ Loads: Tenant from DataProvider\n  │  └─ Provides: useTenant() hook\n  │\n  └─ (tenant)/page.tsx (Server Component)\n     └─ TenantLandingPage (Client Component)\n        ├─ Navigation\n        │  ├─ Logo & Name\n        │  └─ Auth Status + CTA\n        ├─ Hero Section\n        │  ├─ Headline (from tenant.landing)\n        │  ├─ Subhead\n        │  └─ CTA Button\n        ├─ Features Grid\n        ├─ Industry Section\n        ├─ CTA Section\n        └─ Footer\n```\n\n## Data Flow Diagram\n\n```\nRequest: saas-corp.localhost:3000\n    │\n    ├─ Middleware extracts subdomain \"saas-corp\"\n    │  ├─ Checks reserved list: NO\n    │  ├─ Checks role list: NO\n    │  └─ Looks up tenant slug\n    │\n    ├─ Tenant Found: tenant-1\n    │  ├─ name: \"SaaS Corp\"\n    │  ├─ industry: \"saas\"\n    │  └─ branding: { primaryColor: \"#3B82F6\", ... }\n    │\n    ├─ Middleware injects headers\n    │  ├─ X-Tenant-Slug: \"saas-corp\"\n    │  └─ X-Tenant-Id: \"tenant-1\"\n    │\n    ├─ TenantProvider reads headers\n    │  ├─ Calls: provider.tenants.getBySlug(\"saas-corp\")\n    │  └─ Gets: Tenant object\n    │\n    ├─ TenantLandingPage calls useTenant()\n    │  ├─ Gets: Tenant object with branding\n    │  └─ Applies: CSS variables\n    │\n    └─ Page renders with tenant colors/logo/copy\n```\n\n## Routing Paths\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│ Subdomain Resolution Logic                                  │\n├─────────────────────────────────────────────────────────────┤\n│                                                             │\n│ subdomain.localhost:3000                                    │\n│     │                                                       │\n│     ├─ Is role? (founder, sales, etc.)                      │\n│     │  └─ YES → /workbench/[role]                          │\n│     │  └─ NO → continue                                    │\n│     │                                                       │\n│     ├─ Is reserved? (www, app, api, admin, docs)            │\n│     │  └─ YES → serve as-is (app → /app)                   │\n│     │  └─ NO → continue                                    │\n│     │                                                       │\n│     ├─ Is tenant slug?                                      │\n│     │  └─ YES → look up in DataProvider                    │\n│     │  │   ├─ FOUND → inject headers, route to (tenant)    │\n│     │  │   └─ NOT FOUND → 404                              │\n│     │  └─ NO → 404                                         │\n│     │                                                       │\n│     └─ 404 \"workspace not found\"                            │\n│                                                             │\n└─────────────────────────────────────────────────────────────┘\n\nExamples:\n\nfounder.localhost:3000\n  ├─ Is \"founder\" a role? YES\n  └─ Route to: /workbench/founder\n\nsaas-corp.localhost:3000\n  ├─ Is \"saas-corp\" a role? NO\n  ├─ Is \"saas-corp\" reserved? NO\n  ├─ Is \"saas-corp\" a tenant slug? YES → found in DataProvider\n  └─ Route to: (tenant)/page.tsx with X-Tenant-Slug header\n\napp.localhost:3000\n  ├─ Is \"app\" a role? NO\n  ├─ Is \"app\" reserved? YES\n  └─ Route to: /app/page.tsx (tenant picker)\n\nunknown.localhost:3000\n  ├─ Is \"unknown\" a role? NO\n  ├─ Is \"unknown\" reserved? NO\n  ├─ Is \"unknown\" a tenant slug? NO\n  └─ 404 \"workspace not found\"\n```\n\n## DataProvider Architecture\n\n```\nDataProvider (in-memory)\n  │\n  ├─ tenants: TenantRepo\n  │  ├─ get(id) → Tenant | null\n  │  ├─ getBySlug(slug) → Tenant | null\n  │  ├─ create(data) → Tenant\n  │  ├─ update(id, partial) → Tenant | null\n  │  ├─ listAll() → Tenant[]\n  │  └─ validateSlug(slug) → boolean\n  │\n  └─ Storage\n     └─ tenants Map\n        ├─ tenant-1: { id, name: \"SaaS Corp\", slug: \"saas-corp\", ... }\n        ├─ tenant-2: { id, name: \"HealthTech Inc\", slug: \"healthtech\", ... }\n        └─ tenant-3: { id, name: \"Fintech Solutions\", slug: \"fintech-solutions\", ... }\n```\n\n## Branding Application\n\n```\nTenant Data\n  │\n  ├─ branding: {\n  │  ├─ logoUrl: \"🚀\"\n  │  ├─ primaryColor: \"#3B82F6\"\n  │  ├─ backgroundColor: \"#F0F9FF\"\n  │  └─ tagline: \"Build, grow, and scale\"\n  │\n  ├─ landing: {\n  │  ├─ headline: \"Unified SaaS Operations\"\n  │  ├─ subhead: \"Connect your entire SaaS stack\"\n  │  ├─ featuredPluginIds: [...]\n  │  └─ featuredAgentIds: [...]\n  │\n  └─ Applied to TenantLandingPage\n     │\n     ├─ CSS Variables\n     │  ├─ --primary-color: #3B82F6\n     │  └─ --background-color: #F0F9FF\n     │\n     ├─ Navigation\n     │  └─ Logo: \"🚀\" + Name: \"SaaS Corp\"\n     │\n     ├─ Hero Section\n     │  └─ Headline: \"Unified SaaS Operations\"\n     │\n     ├─ Buttons\n     │  └─ style={{ background: 'var(--primary-color)' }}\n     │\n     └─ Sections\n        └─ Industry: \"Built for SaaS\"\n```\n\n## Error Handling & Fallbacks\n\n```\nTenant Lookup Failures\n\n1. Unknown Subdomain\n   └─ Middleware → 404 \"workspace not found\"\n\n2. Tenant Exists but Suspended\n   └─ Middleware → ✅ Routes to page\n   └─ Page → Shows \"Workspace Suspended\" message\n\n3. No Branding Config\n   └─ Uses defaults:\n      ├─ primaryColor: #3B82F6\n      ├─ backgroundColor: #F0F9FF\n      └─ logoUrl: (none)\n\n4. TenantProvider Load Error\n   └─ Shows loading skeleton\n   └─ Logs error to console\n   └─ Falls back to defaults\n```\n\n## Performance Optimizations\n\n```\nRequest → Response Timeline\n\nRequest (t=0ms)\n  ↓\nMiddleware Routing (t=1ms)\n  • Extract subdomain: O(1)\n  • Hash lookup in reserved list: O(1)\n  • DataProvider.getBySlug(): O(n) → O(1) with index\n  ↓\nRoute to (tenant) (t=2ms)\n  ↓\nServer Component Render (t=5ms)\n  • TenantProvider mounts\n  • headers() call (no I/O)\n  • DataProvider.getBySlug() (in-memory)\n  ↓\nClient Hydration (t=100ms)\n  • TenantLandingPage mounts\n  • useTenant() hook reads context\n  • CSS variables applied (no re-render)\n  ↓\nPage Interactive (t=300ms)\n\nTotal time to interactive: ~300ms (typical)\n```\n\n## Scaling Considerations\n\n```\nCurrent (In-Memory)\n  ├─ Seed: 3 tenants\n  ├─ Lookup: O(n) → O(1) with index\n  └─ Memory: < 1MB\n\nWith 1000 Tenants\n  ├─ Lookup: O(1) with hash\n  ├─ Memory: ~10MB\n  └─ Cache: 99% hit rate on slug lookups\n\nWith Database (Future)\n  ├─ Query: SELECT * FROM tenants WHERE slug = $1\n  ├─ Cache: Redis TTL=3600s\n  ├─ Hit rate: 99%+\n  └─ Lookup: <10ms (cached), <100ms (uncached)\n```\n\nThis architecture is designed to scale from development to production with minimal changes to the routing logic.\n"}