Multi-Tenancy Approaches
1. Database per Tenant
Each tenant gets their own database - highest isolation but most expensive.
2. Schema per Tenant
Shared database, separate schemas - good isolation with shared resources.
3. Shared Database, Shared Schema
All tenants in one table with tenant_id - most cost-effective, requires careful query building.
Recommended Architecture
┌─────────────────────────────────────────────┐
│ Load Balancer │
└─────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ API GW │ │ API GW │ │ API GW │
└─────────┘ └─────────┘ └─────────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ Service │ │ Service │ │ Service │
└─────────┘ └─────────┘ └─────────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ Database│ │ Database│ │ Database│
└─────────┘ └─────────┘ └─────────┘
Key Components
API Gateway
Service Layer
Data Layer
Implementing Tenant Isolation
// Middleware to extract tenant ID
export function withTenant(handler: NextApiHandler) {
return async (req: NextRequest) => {
const tenantId = req.headers.get('x-tenant-id');
if (!tenantId) {
return new Response('Tenant required', { status: 401 });
}
// Set tenant context
setTenantId(tenantId);
return handler(req);
};
}
// Query with tenant isolation
const getUser = async (userId: string) => {
const tenantId = getTenantId();
return db.query(
'SELECT * FROM users WHERE id = $1 AND tenant_id = $2',
[userId, tenantId]
);
};
Scaling Considerations
Conclusion
SaaS architecture requires balancing cost, isolation, and scalability. Start simple and evolve as you understand your tenants' needs.
┌─────────────────────────────────────────────┐
│ Load Balancer │
└─────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ API GW │ │ API GW │ │ API GW │
└─────────┘ └─────────┘ └─────────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ Service │ │ Service │ │ Service │
└─────────┘ └─────────┘ └─────────┘
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│ Database│ │ Database│ │ Database│
└─────────┘ └─────────┘ └─────────┘
// Middleware to extract tenant ID
export function withTenant(handler: NextApiHandler) {
return async (req: NextRequest) => {
const tenantId = req.headers.get('x-tenant-id');
if (!tenantId) {
return new Response('Tenant required', { status: 401 });
}
// Set tenant context
setTenantId(tenantId);
return handler(req);
};
}
// Query with tenant isolation
const getUser = async (userId: string) => {
const tenantId = getTenantId();
return db.query(
'SELECT * FROM users WHERE id = $1 AND tenant_id = $2',
[userId, tenantId]
);
};