Multi-Tenancy
Fullfinity supports multi-tenant deployments where each tenant has its own isolated database.
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────────────┐│ Single Codebase │├─────────────────────────────────────────────────────────┤│ tenant1.example.com tenant2.example.com tenant3.com ││ │ │ │ ││ ▼ ▼ ▼ ││ ┌─────────┐ ┌─────────┐ ┌─────────┐ ││ │ DB: t1 │ │ DB: t2 │ │ DB: t3 │ ││ │ Modules:│ │ Modules:│ │ Modules:│ ││ │ - core │ │ - core │ │ - core │ ││ │ - crm │ │ - crm │ │ - crm │ ││ │ - inv │ │ │ │ - inv │ ││ └─────────┘ └─────────┘ └─────────┘ │└─────────────────────────────────────────────────────────┘Benefits
Section titled “Benefits”- Data Isolation - Each tenant’s data is in a separate database
- Independent Modules - Tenants can install different modules
- Customization - Views can be customized per tenant
- Security - Complete data separation
- Scalability - Databases can be on different servers
How It Works
Section titled “How It Works”1. Tenant Routing
Section titled “1. Tenant Routing”Tenants are identified by subdomain or host header:
# Request to tenant1.example.comtenant = get_tenant_from_host(request.headers.get("host"))# tenant = "tenant1"2. Database Connection
Section titled “2. Database Connection”Each request uses the tenant’s database pool:
# Get connection pool for tenantpool = await db_manager.get_pool(tenant_db_name)3. Connection Pooling
Section titled “3. Connection Pooling”Pools are maintained per database:
# Pools are cached and reusedif tenant_db not in connection_pools: connection_pools[tenant_db] = await create_pool(tenant_db)Configuration
Section titled “Configuration”Enable Multi-Tenancy
Section titled “Enable Multi-Tenancy”multi_tenant: enabled: true host_routing: true default_database: mainTenant Database Setup
Section titled “Tenant Database Setup”Each tenant needs a database:
-- Create tenant databaseCREATE DATABASE tenant_acme;# Initialize/upgrade the tenant's schema (the CLI is `fullfinity-server`)fullfinity-server -c config.yaml -db tenant_acme -u allModule Installation Per Tenant
Section titled “Module Installation Per Tenant”Each tenant can install different modules:
# Install module for specific tenantawait install_module(tenant_db="tenant_acme", module_name="crm")The module’s:
- Models are created in the tenant’s database
- Views are stored in the tenant’s
ui_viewtable - Security rules are added to the tenant’s
model_accesstable
View Customization Per Tenant
Section titled “View Customization Per Tenant”Views stored in the database can be customized:
# Tenant-specific view modificationview = await UiView.filter(identifier="contact_form_view").first()view.arch[0]["content"].append(custom_field)await view.save()Data Migration
Section titled “Data Migration”Export Tenant Data
Section titled “Export Tenant Data”pg_dump -h localhost -U postgres tenant_acme > tenant_acme_backup.sqlImport to New Tenant
Section titled “Import to New Tenant”createdb tenant_newpsql -h localhost -U postgres tenant_new < tenant_acme_backup.sqlConsiderations
Section titled “Considerations”Performance
Section titled “Performance”- Connection pools are per-database
- Cache keys include database name
- Queries are isolated per tenant
Security
Section titled “Security”- No cross-tenant data access
- Tenant context verified on each request
- Database credentials can differ per tenant
Maintenance
Section titled “Maintenance”- Migrations run per database
- Module updates apply to all tenants
- Database backups are independent
Common Patterns
Section titled “Common Patterns”Shared Master Data
Section titled “Shared Master Data”For data shared across tenants (e.g., countries, currencies):
- Store in a shared database
- Replicate to tenant databases on sync
- Or use read-only access to shared database
Tenant Provisioning
Section titled “Tenant Provisioning”async def create_tenant(name: str, admin_email: str): # 1. Create database await create_database(f"tenant_{name}")
# 2. Run schema setup await run_setup(f"tenant_{name}")
# 3. Install core module await install_module(f"tenant_{name}", "core")
# 4. Create admin user await create_user(f"tenant_{name}", admin_email)Next Steps
Section titled “Next Steps”- Module System - How modules work with multi-tenancy