ARCHITECTURE

๐Ÿข Multi-Tenant Architecture

Building platforms where every tenant feels like they have their own system

โฑ๏ธ 6+ Years
๐Ÿ“ฆ 10+ Projects
โœ“ Available for new projects
Experience at: Drop Deliveryโ€ข Flowriteโ€ข ActivePrimeโ€ข Anaqua

๐ŸŽฏ What I Offer

Multi-Tenant Design

Design optimal multi-tenant architecture for your use case.

Deliverables
  • Tenancy model selection
  • Data isolation strategy
  • Schema design
  • Tenant provisioning
  • Scaling strategy

Tenant Isolation

Implement secure data and resource isolation between tenants.

Deliverables
  • Database-level isolation
  • Application-level isolation
  • API isolation
  • Background job isolation
  • Security audit

Enterprise Features

Add enterprise multi-tenant capabilities.

Deliverables
  • White-labeling support
  • Custom domains
  • Tenant-specific configuration
  • Usage metering
  • Admin portal

๐Ÿ”ง Technical Deep Dive

Multi-Tenancy Models

Three main approaches, each with trade-offs:

1. Shared Database, Shared Schema:

1
2
3
4
5
6
7
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    tenant_id INTEGER NOT NULL,  -- Tenant discriminator
    product_name VARCHAR(255),
    ...
);
CREATE INDEX idx_orders_tenant ON orders(tenant_id);
  • โœ… Simple, cost-effective
  • โœ… Easy maintenance
  • โŒ Query discipline required
  • โŒ Noisy neighbor risk

2. Shared Database, Separate Schema:

1
2
3
-- Each tenant gets their own schema
CREATE SCHEMA tenant_123;
CREATE TABLE tenant_123.orders (...);
  • โœ… Better isolation
  • โœ… Per-tenant customization
  • โŒ More complex migrations
  • โŒ Connection pooling challenges

3. Separate Database:

  • โœ… Maximum isolation
  • โœ… Independent scaling
  • โŒ Highest cost
  • โŒ Operational complexity

Tenant Context Implementation

The key to multi-tenancy: automatic tenant scoping

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from contextvars import ContextVar

current_tenant: ContextVar[Tenant] = ContextVar('tenant')

class TenantMiddleware:
    async def __call__(self, request, call_next):
        # Extract tenant from subdomain, header, or token
        tenant = await self.resolve_tenant(request)
        token = current_tenant.set(tenant)
        try:
            return await call_next(request)
        finally:
            current_tenant.reset(token)

class TenantQuerySet(QuerySet):
    def get_queryset(self):
        tenant = current_tenant.get()
        return super().get_queryset().filter(tenant=tenant)

Now every query is automatically scoped to the current tenant.

๐Ÿ“‹ Details & Resources

Multi-Tenant Architecture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Tenant Resolution                         โ”‚
โ”‚     (Subdomain, Header, Token โ†’ Tenant Context)             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
                              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Application Layer                         โ”‚
โ”‚           (All queries scoped to tenant)                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                     โ”‚                     โ”‚
        โ–ผ                     โ–ผ                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Tenant A    โ”‚   โ”‚   Tenant B      โ”‚   โ”‚   Tenant C    โ”‚
โ”‚   (Data)      โ”‚   โ”‚   (Data)        โ”‚   โ”‚   (Data)      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tenant Model Implementation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from django.db import models
from django.conf import settings

class Tenant(models.Model):
    name = models.CharField(max_length=255)
    subdomain = models.CharField(max_length=63, unique=True)
    custom_domain = models.CharField(max_length=255, null=True)
    
    # Billing
    stripe_customer_id = models.CharField(max_length=255)
    plan = models.ForeignKey('Plan', on_delete=models.PROTECT)
    
    # Configuration
    config = models.JSONField(default=dict)
    branding = models.JSONField(default=dict)  # Logo, colors, etc.
    
    # Compliance (e.g., state-specific rules)
    compliance_config = models.JSONField(default=dict)
    
    created_at = models.DateTimeField(auto_now_add=True)

class TenantAwareModel(models.Model):
    """Base class for all tenant-scoped models"""
    tenant = models.ForeignKey(
        Tenant, 
        on_delete=models.CASCADE,
        db_index=True
    )
    
    class Meta:
        abstract = True
    
    def save(self, *args, **kwargs):
        if not self.tenant_id:
            self.tenant = get_current_tenant()
        super().save(*args, **kwargs)

class Order(TenantAwareModel):
    """Example: Orders are tenant-scoped"""
    product = models.CharField(max_length=255)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    
    objects = TenantManager()  # Auto-filters by tenant

Tenant Isolation Patterns

LayerIsolation MethodImplementation
DatabaseRow-level filteringtenant_id column + auto-scope
ApplicationContext middlewareThread-local/context var
APIToken/subdomainTenant resolution middleware
Background JobsJob metadataTenant ID in job payload
CacheKey prefixingtenant:{id}:key pattern
FilesPath prefixings3://bucket/{tenant_id}/

Enterprise Features

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class TenantConfiguration:
    """Per-tenant customization"""
    
    def get_branding(self, tenant: Tenant) -> Branding:
        return Branding(
            logo_url=tenant.branding.get('logo'),
            primary_color=tenant.branding.get('primary_color', '#000'),
            custom_css=tenant.branding.get('custom_css')
        )
    
    def get_compliance_rules(self, tenant: Tenant) -> ComplianceRules:
        # e.g., State-specific rules for cannabis delivery
        return ComplianceRules(
            max_order_weight=tenant.compliance_config.get('max_weight'),
            delivery_hours=tenant.compliance_config.get('hours'),
            age_verification=tenant.compliance_config.get('age_check', True)
        )
    
    def get_sso_config(self, tenant: Tenant) -> Optional[SSOConfig]:
        if not tenant.config.get('sso_enabled'):
            return None
        return SSOConfig(
            provider=tenant.config['sso_provider'],
            metadata_url=tenant.config['sso_metadata_url']
        )

Frequently Asked Questions

What is multi-tenant architecture?

Multi-tenant architecture allows multiple customers (tenants) to share the same application instance while keeping their data isolated. It’s essential for SaaS applications, enabling efficient resource utilization and simplified operations.

How much does multi-tenant implementation cost?

Multi-tenant development typically costs $120-170 per hour. Adding multi-tenancy to an existing app starts around $20,000-40,000, while building a new multi-tenant SaaS platform ranges from $75,000-200,000+.

What are the different multi-tenant models?

Three approaches: 1) Shared database with tenant ID column (simplest, lowest isolation), 2) Shared database with separate schemas per tenant (moderate isolation), 3) Separate database per tenant (highest isolation, most complex). I help choose based on your security and scale requirements.

How do you handle tenant data isolation?

I implement: row-level security policies (PostgreSQL RLS), middleware that automatically filters queries, tenant context in all operations, and testing to prevent data leakage. Data isolation is critical, I use defense-in-depth.

Can you add multi-tenancy to an existing application?

Yes, though it requires careful planning. I’ve retrofitted multi-tenancy onto single-tenant apps: adding tenant models, migrating data, implementing filtering, updating tests, and ensuring no cross-tenant data access. It’s a significant undertaking.


Experience:

Case Studies:

Related Technologies: SaaS Development, PostgreSQL, API Security

๐Ÿ’ผ Real-World Results

Multi-State Delivery Platform

Drop Delivery
Challenge

Build platform serving 65 dispensary clients across 6 states with different regulations, branding, and configurations.

Solution

Multi-tenant architecture with shared database, tenant-specific configuration for compliance rules, white-label support for branding, centralized management.

Result

65 clients, 6 states, $30M+ orders, all on shared platform with complete isolation.

Enterprise AI Platform

Anaqua
Challenge

Serve multiple enterprise clients with strict data isolation requirements.

Solution

Tenant-isolated AI systems with separate vector stores per client, tenant-scoped audit logging, enterprise SSO per tenant.

Result

Fortune 500 clients confident in data isolation.

Multi-CRM Integration Platform

ActivePrime
Challenge

Platform serving enterprise clients across multiple CRM platforms.

Solution

Multi-tenant SaaS with per-tenant CRM connections, isolated data processing, tenant-specific configuration.

Result

Serving enterprise clients with billions of records, complete isolation.

โšก Why Work With Me

  • โœ“ Built platform serving 65+ B2B clients at Drop Delivery
  • โœ“ Enterprise multi-tenancy with strict isolation at Anaqua
  • โœ“ White-labeling and customization experience
  • โœ“ Compliance and regulatory multi-tenancy
  • โœ“ Full implementation, design to production

Build Your Multi-Tenant Platform

Within 24 hours