Multi-Tenant: Satu Sistem, Banyak Pelanggan
Saya sudah membangun beberapa SaaS platform di Indonesia, dan satu hal yang selalu menjadi diskusi panjang adalah: bagaimana cara isolate data antar tenant tanpa membuat sistem terlalu kompleks?
Multi-tenant architecture itu seperti apartemen: banyak penghuni dalam satu gedung, tapi masing-masing punya privasi dan ruang sendiri. Tantangannya adalah memastikan tetangga tidak bisa masuk ke apartemen Anda, sambil tetap menjaga efisiensi operasional.
Tiga Strategi Isolation
1. Shared Database, Shared Schema (Pool Model)
Semua tenant berbagi tabel yang sama, dibedakan dengan tenant_id column. Ini paling efisien secara resource, tapi paling rumit untuk di-isolate. Cocok untuk SaaS dengan banyak tenant kecil.
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
customer_name VARCHAR(255),
total DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::uuid);
2. Shared Database, Separate Schemas — Setiap tenant punya schema sendiri dalam database yang sama. Lebih baik isolation-nya, tapi lebih sulit untuk manage migrations.
3. Separate Database per Tenant — Full isolation, tapi paling mahal dan sulit di-maintain. Cocok untuk enterprise clients yang butuh compliance ketat.
Tenant Context di Go
package middleware
import "context"
type contextKey string
const TenantKey contextKey = "tenant"
func TenantMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tenantID := extractTenantFromHeader(r)
tenant, err := tenantRepo.FindByID(r.Context(), tenantID)
if err != nil {
http.Error(w, "Tenant not found", 404)
return
}
ctx := context.WithValue(r.Context(), TenantKey, tenant)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
Billing Integration
Dari pengalaman saya, billing SaaS itu kompleks. Saya biasanya pakai pattern ini:
- Track usage per tenant — API calls, storage, active users
- Calculate bill periodically — cron job harian/mingguan
- Integrate with payment gateway — Midtrans untuk Indonesia
- Handle plan limits — soft limits (warning) dan hard limits (block)
Pelajaran dari Production
Kesalahan terbesar yang saya lihat di SaaS multi-tenant: mengabaikan tenant isolation sejak awal. Kalau Anda mulai dengan shared-everything tanpa tenant_id di setiap query, migration ke proper isolation nanti akan sangat menyakitkan.
Selalu mulai dengan asumsi: "setiap query harus tahu tenant-nya." Ini discipline yang harus dimiliki seluruh tim.
Scaling Multi-Tenant Systems
Dari pengalaman saya, scaling multi-tenant SaaS itu unik. Kamu tidak bisa sekadar vertical scaling — kamu perlu tenant-aware load balancing. Beberapa strategi: tenant sharding (distribusi tenant ke beberapa database cluster berdasarkan ukuran atau region), caching per tenant (Redis dengan tenant prefix untuk cache isolation), dan asymmetric scaling (resource allocation berbeda berdasarkan plan tenant).
Yang juga penting: noise neighbor mitigation. Satu tenant yang heavy usage tidak boleh mempengaruhi performance tenant lain. Di production, saya menerapkan resource quotas per tenant dan monitoring untuk mendeteksi anomalous behavior.