Çok Kiracılı (Multi-Tenant) SaaS

B2B SaaS yazılımlarında Çok Kiracılı (Multi-Tenant) mimari nasıl kurulur? Paylaşımlı ve izole veritabanı şemaları, Row-Level Security (RLS) ve ölçekleme.

Yozel

12 dakika

Çok Kiracılı (Multi-Tenant) SaaS
İçindekiler (14)

Modern B2B yazılım dünyasında bir ürünü tek bir müşterinin kendi sunucusuna kurmak (On-Premise) veya her yeni müşteri geldiğinde sıfırdan ayrı bir sunucu ve ayrı bir kod kopyası ayağa kaldırmak (Single-Tenant), müşteri sayısı 50'yi geçtiği anda operasyonel ve finansal olarak sürdürülemez hale gelir. Bugün Salesforce, Shopify, Slack, Notion ve HubSpot gibi küresel teknoloji devlerinin binlerce hatta milyonlarca kurumsal müşteriye (kiracıya / tenant) aynı anda, tek bir kod tabanı ve ortak bulut altyapısı üzerinden hizmet verebilmesinin arkasındaki temel mühendislik disiplini Çok Kiracılı Mimari (Multi-Tenant Architecture) yapısıdır. Ancak yüzlerce rakip şirketin en mahrem finansal verilerini, müşteri kayıtlarını ve ticari sırlarını aynı sistem üzerinde barındırırken tek bir satır kod hatasıyla A şirketinin verisini B şirketine göstermemek (Data Leakage), ya da bir müşterinin başlattığı ağır raporlama işleminin diğer tüm müşterilerin sistemini kilitlemesini (Noisy Neighbor) engellemek üst düzey bir mimari tasarım gerektirir. Uygulama ve yazılım mimarileri alanında B2B SaaS geliştiren mühendisler ve teknoloji liderleri için; Silo, Bridge ve Pool veritabanı izolasyon modellerini, PostgreSQL Row-Level Security (RLS) mekanizmasını, kiracı bazlı hız sınırlama (Rate Limiting) stratejilerini ve özel alan adı (Custom Domain) yönlendirme mimarilerini tüm teknik derinliğiyle inceliyoruz.

1. Single-Tenant vs. Multi-Tenant SaaS: Mimari Paradigma Değişimi

Bir apartman binası ile müstakil villalar arasındaki farkı düşünün: Tek Kiracılı (Single-Tenant) mimaride her kurumsal müşteriye kendine ait müstakil bir uygulama sunucusu, ayrı bir önbellek (Redis) ve ayrı bir veritabanı tahsis edilir. İzolasyon fiziksel veya sanal makine seviyesinde olduğu için güvenlik sınırları nettir; ancak 500 müşteriniz olduğunda 500 ayrı veritabanı şemasını güncellemek (migration), 500 ayrı sunucunun CPU/RAM kapasitesini izlemek ve geceleri boş duran yüzlerce sunucunun bulut faturasını ödemek zorunda kalırsınız. Bu durum SaaS iş modelinin en önemli avantajı olan brüt kar marjını (%75-85) eritir.

Çok Kiracılı (Multi-Tenant) mimaride ise tüm müşteriler (Tenants) aynı uygulama kümesini (örneğin aynı Kubernetes Deployment'ını) ve çoğu zaman aynı veritabanı kümesini paylaşır; fakat mantıksal olarak birbirlerinden tamamen izole edilmişlerdir. Yeni bir müşteri kredi kartını girip kayıt olduğu anda arka planda yeni bir sunucu kurulmaz; yalnızca tenants tablosuna yeni bir satır eklenir ve müşteri 1 saniye içinde sistemi kullanmaya başlar. Günde 10 kez canlıya yeni kod çıktığınızda, bu güncelleme aynı anda 10.000 kurumsal müşterinin tamamında anında aktif olur. E-ticarette bulut teknolojisi ve SaaS devrimi tam olarak bu kaynak paylaşımı ve anlık ölçeklenebilirlik ekonomisi üzerine inşa edilmiştir.

2. Veritabanı İzolasyon Modelleri: Silo, Bridge ve Pool Mimarisi

Çok kiracılı bir SaaS uygulamasında uygulama katmanı (Application Layer / Stateless API) neredeyse her zaman paylaşımlıdır (Pool). Asıl kritik mimari karar, durum bilgisi tutan (stateful) veritabanı katmanında verilerin nasıl ayrıştırılacağıdır. AWS SaaS Factory ve kurumsal yazılım standartlarında bu ayrım üç temel modelle tanımlanır:

Silo Bridge ve Pool Veritabanı İzolasyon Modelleri Karşılaştırması
B2B SaaS mimarilerinde kiracı verileri; tam izole Silo (Database-per-Tenant), şema bazlı Bridge (Schema-per-Tenant) veya paylaşımlı tablo Pool (Row-Level) modelleriyle yönetilir.

2.1. Silo Modeli: Kiracı Başına Ayrı Veritabanı (Database-per-Tenant)

Bu modelde uygulama sunucuları ortaktır ancak her kiracının kendine ait ayrı bir veritabanı (veya aynı RDS kümesi içinde ayrı bir mantıksal veritabanı: db_tenant_a, db_tenant_b) bulunur. Uygulama katmanında çalışan bir Tenant Router (Kiracı Yönlendirici) ara yazılımı (middleware), gelen HTTP isteğindeki JWT token veya alt alan adına bakarak hangi kiracının istek attığını anlar ve bağlantı havuzundan (Connection Pool) o kiracının veritabanı bağlantısını seçer.

  • Avantajları: En yüksek veri izolasyonu ve güvenlik seviyesini sunar. Bir kiracının veritabanını yedekten geri dönmek (Point-in-Time Restore) diğer kiracıları hiç etkilemez. Bankacılık, savunma sanayii ve sağlık (HIPAA / KVKK) gibi "Verim başka şirketlerle aynı tabloda duramaz" şartı koşan kurumsal (Enterprise) müşterileri ikna etmek çok kolaydır. Ayrıca her müşterinin verisi kendi KMS anahtarıyla (Bring Your Own Key - BYOK) şifrelenebilir.
  • Dezavantajları: Maliyet yüksektir. Kiracı sayısı 2.000'e ulaştığında bir tabloya yeni bir sütun eklemek (ALTER TABLE) için 2.000 veritabanında sırayla şema göçü (Schema Migration) çalıştırmak gerekir; bu sırada bazılarının başarısız olması ciddi operasyonel baş ağrısı yaratır. Ayrıca veritabanı bağlantı havuzu (Connection Pooling) yönetimi zorlaşır.

2.2. Bridge (Köprü) Modeli: Kiracı Başına Ayrı Şema (Schema-per-Tenant)

Tek bir fiziksel PostgreSQL veritabanı kümesi kullanılır ancak her kiracı için bu veritabanı içinde ayrı bir Namespace / Schema açılır (Örn: tenant_101.orders, tenant_102.orders). İstek geldiğinde uygulama bağlantı üzerinde SET search_path TO tenant_101; komutunu çalıştırır ve tüm SQL sorguları otomatik olarak o kiracının şemasında çalışır.

  • Avantajları: Silo modeline göre donanım ve lisans maliyetini ciddi oranda düşürürken, tablolar düzeyinde mantıksal ayrımı korur. Tek bir kiracının verisini dışa aktarmak (pg_dump --schema=tenant_101) veya silmek çok kolaydır.
  • Dezavantajları: PostgreSQL tek bir veritabanı içinde 10.000'den fazla şema ve yüz binlerce tablo (pg_class katalog şişmesi) oluştuğunda ciddi performans kaybı yaşar. Ayrıca binlerce şemaya aynı anda Alembic, Flyway veya Prisma ile migration uygulamak hala yavaştır.

2.3. Pool (Havuz) Modeli: Paylaşımlı Tablo ve `tenant_id` (Shared Everything)

Yüksek ölçekli modern SaaS platformlarının (1.000'den 1.000.000'a kadar kiracısı olan sistemlerin) varsayılan standardı Pool (Havuz) modelidir. Tek bir veritabanı ve tek bir şema vardır. Tüm kiracıların verileri aynı tablolarda (users, invoices, products) yan yana durur ve her tabloda zorunlu bir tenant_id (veya organization_id) sütunu bulunur.

  • Avantajları: En düşük altyapı maliyeti ve en yüksek kaynak verimliliği. Şema değişikliği (Migration) tek bir tabloda saniyeler içinde tamamlanır. 100.000 kiracıya kadar bağlantı havuzu (PgBouncer) darboğazı yaşamadan ölçeklenir.
  • Dezavantajları: Yazılımcı hatasına karşı en hassas modeldir. Eğer bir geliştirici SELECT * FROM invoices WHERE status = 'PAID' sorgusunu yazarken sonuna AND tenant_id = ? eklemeyi unutursa, tüm müşterilerin faturaları birbirine karışır! İşte bu felaketi veritabanı motoru seviyesinde imkansız kılmak için PostgreSQL Row-Level Security (RLS) devreye girer.
Karşılaştırma Kriteri Silo Modeli (Ayrı DB) Bridge Modeli (Ayrı Şema) Pool Modeli (Paylaşımlı Tablo + RLS)
Veri İzolasyon Seviyesi Maksimum (Fiziksel / Mantıksal DB ayrımı) Yüksek (Tablo ve indeks ayrımı) Orta / Yüksek (Satır bazlı tenant_id + RLS)
Sunucu ve Altyapı Maliyeti Çok Yüksek (Kiracı başına sabit kaynak) Orta Çok Düşük (Maksimum kaynak paylaşımı)
Schema Migration (Tablo Güncelleme) Çok Zor (Binlerce DB'de ayrı ayrı çalışır) Zor (Binlerce şemada döngüyle çalışır) Çok Kolay (Tek seferde anında uygulanır)
"Noisy Neighbor" (Gürültülü Komşu) Riski Sıfır / Çok Düşük Yüksek (Aynı CPU ve Disk I/O paylaşılır) Çok Yüksek (Aktif kota ve indeksleme şarttır)
Tek Kiracı Yedekten Dönme (Restore) Çok Kolay (Native DB Snapshot) Kolay (pg_dump şema bazlı) Zor (tenant_id filtreli özel ETL betiği gerekir)
İdeal Müşteri Segmenti Bankalar, Holdingler, Yüksek Ücretli Enterprise Orta Ölçekli B2B (50 - 500 Kurumsal Müşteri) PLG (Product-Led Growth), KOBİ ve Yüksek Hacimli SaaS
Hibrit (Tiered) SaaS Mimarisi: En başarılı B2B SaaS şirketleri tek bir modele bağlı kalmaz; fiyatlandırma paketine göre hibrit model uygular. Örneğin aylık 49$ ödeyen binlerce Standart/Pro paketi müşterisi Pool (Paylaşımlı) veritabanında barındırılırken, yıllık 50.000$ ödeyen Enterprise paket müşterisi tek tıkla Silo (İzole Veritabanı) katmanına taşınır. Uygulama kodu aynı kalır; yalnızca Tenant Router ilgili müşterinin bağlantı dizgesini (connection string) izole RDS sunucusuna yönlendirir.

3. PostgreSQL Row-Level Security (RLS) ile Sıfır Güven (Zero-Trust) Veri İzolasyonu

Pool modelinde veri sızıntısını önlemek için yalnızca uygulama katmanındaki ORM filtrelerine (Prisma middleware, Hibernate Filter veya Laravel Global Scope) güvenmek yeterli değildir. Bir geliştirici günün birinde performans için ham bir SQL (Raw Query) yazdığında veya yeni bir raporlama mikroservisi eklediğinde WHERE tenant_id = ... koşulunu unutabilir.

PostgreSQL Row-Level Security RLS ve Noisy Neighbor Koruma Mimarisi
PostgreSQL Row-Level Security (RLS), geliştirici SQL sorgusunda WHERE koşulunu unutsa bile veritabanı çekirdeği seviyesinde başka kiracının verisinin okunmasını engeller.

PostgreSQL Row-Level Security (RLS), izolasyonu doğrudan veritabanı motorunun çekirdeğine indirir. Sistem şu şekilde çalışır:

  1. Tablo üzerinde RLS aktif edilir: ALTER TABLE orders ENABLE ROW LEVEL SECURITY; ve tablo sahibi için bile zorunlu kılınır: ALTER TABLE orders FORCE ROW LEVEL SECURITY;.
  2. Veritabanında katı bir güvenlik politikası tanımlanır: CREATE POLICY tenant_isolation_policy ON orders USING (tenant_id = current_setting('app.current_tenant_id')::uuid);.
  3. Uygulama katmanına bir HTTP isteği geldiğinde, veritabanı işlemi (Transaction) başlar başlamaz ilk komut olarak oturum değişkeni atanır: SET LOCAL app.current_tenant_id = 'a8098c1a-f86e-11da-bd1a-00112444be1e';.
  4. Bu andan itibaren yazılımcı kod içinde SELECT * FROM orders; (hiçbir WHERE koşulu olmadan!) yazsa bile, PostgreSQL yalnızca o oturumdaki tenant_id'ye ait satırları döner. Hatta yazılımcı yanlışlıkla oturum değişkenini atamadan sorgu çalıştırırsa, PostgreSQL tüm tabloyu dönmek yerine 0 satır (boş küme) veya hata döner (Fail-Closed).

Veritabanı indekslemesinde (Indexing) ise Pool modelinin altın kuralı Composite Index (Bileşik İndeks) kullanmaktır. Tüm indekslerin ilk sütunu mutlaka tenant_id olmalıdır (Örn: CREATE INDEX idx_orders_tenant_created ON orders (tenant_id, created_at DESC);). Bu sayede B-Tree indeks ağacında her kiracının verisi fiziksel olarak birbirine yakın sayfalarda (data pages) kümelenir ve sorgular milyonlarca satır arasında sadece ilgili kiracının alt ağacını tarar.

4. "Noisy Neighbor" (Gürültülü Komşu) Problemi ve Çözüm Stratejileri

Çok kiracılı sistemlerde bir müşterinin (örneğin Black Friday kampanyası yapan büyük bir e-ticaret kiracısının veya yanlış yapılandırılmış bir entegrasyon botunun) saniyede 10.000 API isteği göndererek CPU, bellek, veritabanı bağlantı havuzu ve kuyruk işçilerini (Queue Workers) %100 tüketmesi ve diğer 999 masum müşterinin sisteminin yavaşlamasına veya çökmesine yol açması durumuna "Noisy Neighbor" (Gürültülü Komşu) problemi denir.

Dayanıklı (Resilient) bir Multi-Tenant SaaS mimarisinde Gürültülü Komşu etkisini sıfırlamak için dört katmanlı savunma uygulanır:

  • 1. Kiracı Bazlı Hız Sınırlama (Tenant-Aware Rate Limiting): API Gateway (Kong, Envoy, AWS API Gateway) veya Redis tabanlı Token Bucket / Sliding Window algoritmalarında hız sınırı IP adresine göre değil, doğrudan tenant_id ve abonelik paketine göre uygulanır. Örneğin Başlangıç paketi dakikada 300 istek, Kurumsal paket dakikada 5.000 istek atabilir. Limiti aşan kiracıya HTTP 429 Too Many Requests ve Retry-After başlığı dönülürken diğer kiracılar 10ms yanıt almaya devam eder.
  • 2. Adil Kuyruk Yönetimi (Fair Queuing & Bulkhead Pattern): Arka plan işlerinde (fatura PDF üretimi, toplu e-posta gönderimi, stok senkronizasyonu) tek bir FIFO kuyruğu (RabbitMQ / BullMQ / Sidekiq) kullanılırsa, A kiracısının kuyruğa bıraktığı 500.000 iş, B kiracısının tek 1 adet şifre sıfırlama e-postasının saatlerce beklemesine neden olur! Bunu önlemek için Sharded Queues (Bölünmüş Kuyruklar) veya Round-Robin Fair Scheduling kullanılır; işçiler (workers) her kiracıdan sırayla eşit sayıda iş çeker veya ağır toplu işlemler (batch jobs) ayrı düşük öncelikli (low-priority) işçi havuzlarına (Bulkhead) izole edilir.
  • 3. Sorgu Zaman Aşımı ve Kaynak Kotaları (Statement Timeout): Veritabanı seviyesinde her oturuma SET LOCAL statement_timeout = '5000ms'; sınırı konur. Bir kiracının başlattığı optimize edilmemiş ağır bir filtreleme sorgusu 5 saniyeyi aşarsa veritabanı CPU'sunu kilitlemeden otomatik iptal edilir.
  • 4. Yatay Veritabanı Bölümleme (Tenant Sharding - Citus / Vitess): Kiracı sayısı yüz binleri aştığında tek bir PostgreSQL sunucusu yetmez. Citus gibi dağıtık PostgreSQL eklentileriyle tablolar tenant_id sütununa göre (Hash Sharding) 10 veya 50 farklı fiziksel veritabanı düğümüne (Shard) dağıtılır. Her kiracının tüm ilişkisel tabloları aynı fiziksel düğümde (Co-location) tutulduğu için JOIN sorguları milisaniyeler içinde çalışmaya devam eder.

5. Kiracı Çözümleme (Tenant Resolution), Özel Alan Adları ve White-Label Mimari

Geleneksel yazılım geliştirmenin yanı sıra kodsuz uygulama geliştirme platformları ile B2B bayi portali kurulumu ve Web3 tabanlı merkezsiz SaaS arayüzlerinde kurumsal müşteriler uygulamayı kendi markalarıyla (White-Label) ve kendi alan adlarıyla (Custom Domain) kullanmak isterler.

Bir isteğin hangi kiracıya ait olduğunu belirleyen Tenant Resolution katmanı genellikle üç yöntemi birlikte destekler:

  1. Alt Alan Adı (Subdomain Routing): musteri-a.platformunuz.com şeklinde gelen istekte Host başlığı okunarak kiracı belirlenir. Joker karakterli SSL sertifikası (Wildcard SSL: *.platformunuz.com) ile kolayca yönetilir.
  2. Özel Alan Adı (Custom Domain & Reverse Proxy): Müşteri kendi alan adını (örneğin portal.sirketim.com.tr) CNAME kaydı ile sizin kenar (Edge) sunucunuza yönlendirir. Burada Cloudflare for SaaS (Custom Hostnames) veya Caddy Server On-Demand TLS kullanılarak müşterinin alan adı için saniyeler içinde otomatik Let's Encrypt SSL sertifikası üretilir ve Edge Worker katmanında alan adı X-Tenant-ID başlığına çevrilerek arka uca iletilir.
  3. İstek Başlığı veya JWT Claim (Header / Token Routing): Mobil uygulamalar ve tek sayfalık uygulamalarda (SPA / Next.js) kullanıcı birden fazla organizasyona (Workspace) üye olabileceği için aktif kiracı kimliği X-Tenant-ID başlığında taşınır ve sunucu tarafında kullanıcının o kiracıya gerçekten yetkisi olup olmadığı RBAC (Role-Based Access Control) tablosundan doğrulanır. Ayrıca yapay zeka ve tersine mühendislik saldırılarına karşı istemciden gelen tenant_id parametresine asla doğrudan güvenilmez; her zaman imzalı JWT oturum bilgisiyle çapraz doğrulama yapılır (IDOR zafiyeti koruması).

6. Sıkça Sorulan Sorular (SSS)

1. Yeni bir B2B SaaS girişimine başlıyoruz, ilk gün hangi veritabanı modelini seçmeliyiz?

Kesinlikle Pool Modeli (Paylaşımlı veritabanı, her tabloda tenant_id sütunu ve PostgreSQL RLS) ile başlamalısınız. İlk günden her müşteriye ayrı veritabanı (Silo) açmak, CI/CD süreçlerinizi, migration adımlarınızı ve bulut faturanızı gereksiz yere karmaşıklaştırır. Ancak kod yazarken her tabloya ilk günden tenant_id eklemek ileride sistemi çok kiracılıya dönüştürme (refactoring) kabusunu önler ve tek bir veritabanıyla ilk 5.000 kurumsal müşterinize rahatlıkla hizmet vermenizi sağlar.

2. Bir kullanıcı aynı e-posta adresiyle birden fazla şirketin (Tenant) hesabına üye olabilir mi?

Evet, modern B2B SaaS mimarilerinde (Slack, GitHub, Jira, Linear gibi) users (Kimlik/Identity) tablosu ile tenants (Organizasyon/Workspace) tablosu birbirine doğrudan 1'e-N (One-to-Many) değil, araya konulan bir tenant_memberships (Üyelikler ve Roller) tablosu üzerinden Çoka-Çok (Many-to-Many) ilişkiyle bağlanmalıdır. Böylece bir mali müşavir veya danışman tek bir e-posta ve şifreyle giriş yapıp üst menüden (Organization Switcher) hizmet verdiği 10 farklı şirketin paneli arasında tek tıkla geçiş yapabilir.

3. Redis önbellekleme (Caching) ve Elasticsearch arama indekslerinde kiracı izolasyonu nasıl sağlanır?

İzolasyon yalnızca SQL veritabanıyla sınırlı kalmamalıdır! Redis'te önbellek anahtarları (Cache Keys) oluşturulurken en başa mutlaka tenant_id öneki (Namespace Prefix) eklenmelidir: Örneğin dashboard_stats yerine tenant:8492:dashboard_stats kullanılmalıdır. Aksi takdirde A kiracısı için hesaplanan finansal özet tablosu önbellekten B kiracısına gösterilebilir! Benzer şekilde Elasticsearch veya Algolia'da her belgeye tenant_id filtresi zorunlu olarak gömülmeli, S3 dosya depolamada ise dosyalar s3://bucket-adi/tenants/{tenant_id}/faturalar/... dizin yapısıyla ayrıştırılmalıdır.

4. Kurumsal bir müşteri "Verilerimi diğer müşterilerden tamamen silip bana özel sunucuya taşıyın" derse ne yapılır?

Kod tabanınızda tüm tablo ilişkileri zaten tenant_id üzerinden kurgulandığı için bu geçiş (Tenant Migration / Silo Promotion) oldukça temizdir. Müşteri için izole bir RDS örneği ayağa kaldırılır, AWS DMS (Database Migration Service) veya mantıksal replikasyon ile yalnızca WHERE tenant_id = 'X' koşulunu sağlayan satırlar yeni veritabanına aktarılır ve kontrol düzlemindeki (Control Plane) tenants tablosunda bu müşterinin db_connection_url alanı yeni veritabanına yönlendirilir.

5. Çok kiracılı SaaS sistemlerinde müşteri bazlı maliyet (Tenant Cost Attribution / Unit Economics) nasıl hesaplanır?

Paylaşımlı (Pool) altyapıda hangi müşterinin bulut faturanızın ne kadarını tükettiğini bilmek karlılık için şarttır. Bunun için uygulama katmanındaki OpenTelemetry izlerine ve loglarına tenant_id etiketi eklenir. Bir müşterinin ay boyunca yaptığı toplam API çağrısı sayısı, tükettiği toplam veritabanı sorgu süresi (CPU ms), S3'te kapladığı gigabayt alanı ve giden ağ trafiği (Egress) toplanıp toplam bulut faturasına oranlanarak her kiracının gerçek birim maliyeti (Cost-per-Tenant) dolar bazında net olarak raporlanır.

Paylaş

Yozel

Codex haberleri en az iki bağımsız kaynakla doğrulanır; düzeltmeler yazının altında tarih ve saatle belirtilir. Yayın ilkelerimiz