İşe En Baştan Doğru Başlamak: Neden Bu İkili?
Açık konuşayım. SaaS işi dışarıdan bakınca “bir giriş formu, bir ödeme ekranı, bir de panel” gibi görünüyor — sanki iki saatte bitecekmiş gibi (eh, fena değil). Ama işin aslı öyle değil; kirli veri, dağınık tenant yapısı, yarım yamalak yetkilendirme ve sonradan “hadi şunu da ekleyelim” mantığıyla büyütülmüş abonelik sistemi derken, bir sabah kalkıyorsunuz ve elinizdeki şey artık kontrol edilemez bir canavara dönmüş oluyor. Ben 2023 yazında İstanbul’da küçük bir ekip için benzer bir yapı tasarlarken bunu bizzat yaşadım; ilk sürüm hızlı çıktı. Veri ayrımı kısmı gevşek olunca ikinci ayda gerçekten acı çektik — o acıyı size anlatmayayım.
Kendi deneyimimden konuşuyorum, Bu yüzden Next.js + PostgreSQL ikilisi bana hep mantıklı geliyor. Next.js tarafında sunucu bileşenleri, route handler’lar — en azından ben öyle düşünüyorum — ve middleware ile işleri toparlamak nispeten kolay. PostgreSQL tarafında işe RLS, JSONB ve dayaniklı indeksleme seçenekleri var. Yani hem hız hem düzen var; biraz mutfak tezgâhı gibi düşünün — her şey elinizin altında ama ortalık dağılmıyor.
Bunu biraz açayım.
Ha bu arada, geçen ay Ankara’daki bir fintech demosunda yine aynı soruya döndüm: “Neden ayrı veritabanları değil?” Cevap pratikte çoğu ekip için aynı kaldı — maliyet, operasyon yükü. Bakım karmaşası. Küçük startup için ortak veritabanı çok daha rahat akıyor; enterprise seviyede işe hibrit modeller devreye girebiliyor. Yani tek doğru yok, ama yanlış seçim gerçekten çok var.
Multi-Tenancy Kararı: En Kritik Viraj
Bir SaaS platformunda en önemli karar bence multi-tenancy stratejisi (buna dikkat edin). Sadece teknik mimariyi değil, fatura kesimini, güvenliği. Hatta müşteri desteğini de etkiliyor bu karar — yani küçümsemeyin. Ben genelde shared database + tenant_id yaklaşımını seviyorum; ilk aşamada işler daha sade ilerliyor çünkü.
Bakın, PostgreSQL’in row-level security özelliği burada fena değil, hatta baya işe yarıyor. Uygulama katmanında “aman şu kullanıcı başka tenant’ın verisini görmesin” diye sürekli kontrol koymak yerine bunu veritabanına gömüyorsunuz, nokta. Hata yüzeyi daralıyor. Bir nevi kapıyı salona değil apartmanın girişine kilitlemek gibi — mantıklı, değil mi?
Şimdi gelelim işin can alıcı noktasına. Bu konuyla ilgili Python Performans Darboğazı: Tahmin Etme, Ölç yazımıza da göz atmanızı tavsiye ederim.
Tabii kusursuz değil. RLS kuralları ilk kurulumda biraz — en azından ben öyle düşünüyorum — can sıkabiliyor; özellikle ekipte SQL bilgisi zayıfsa yanlış policy yüzünden veri ya görünmez oluyor ya da tam tersi, açık kalıyor — her ikisi de kötü. Geçen yıl İzmir’de görüştüğüm bir ekipte tam da bu oldu: test ortamında çalışan sorgular prod’da sessizce duvara tosladı. Sebep? Basit bir policy hatasıydı. Basit ama acı verici.
Ortak Veritabanı mı, Ayrı Veritabanı mı?
| Yaklaşım | Artıları | Eksi Yanları |
|---|---|---|
| Ortak veritabanı + tenant_id | Düşük maliyet, kolay operasyon, hızlı başlangıç | Büyük ölçekte dikkatli indeksleme ister |
| Ayrı veritabanı / schema | Daha sert izolasyon, bazı kurumsal senaryolarda rahatlık | Maliyet artar, bakım zorlaşır |
Bir şey dikkatimi çekti: Küçük bir startup için ortak veritabanı çoğu zaman doğru hamle oluyor. Neden? Çünkü ürün-pazar uyumu ararken kimse gece üçte on ayrı database cluster yönetmek istemez — bunu deneyenler bilir. Enterprise tarafta işe regülasyon baskısı varsa ya da müşteri başına özel izolasyon şartsa farklı model düşünülür; ama o noktada iş zaten “basit SaaS” olmaktan çıkmış demektir zaten.
PostgreSQL’de iyi tasarlanmış bir multi-tenant yapı size yalnızca performans vermiyor; aynı zamanda güvenlik ve operasyonel sakinlik de sağlıyor.
Next.js Tarafında Mimariyi Temiz Tutmak
Next.js’i SaaS için güçlü yapan şeylerden biri sunucu taraflı akışın doğal hissettirmesi. App Router ile sayfa bazlı yapı kuruluyor, ama asıl güzel taraf şu: middleware sayesinde tenant bilgisini subdomain’den ya da özel domain’den çekebiliyorsunuz, bunu header olarak geçiriyorsunuz. Server component içinde kullanıyorsunuz. Client state’e bağımlılık azalıyor — bu küçük bir fark gibi görünse de ileride çok şeyi değiştiriyor. Bu konuyla ilgili FERPA Uyumlu RAG: Kurumsal Sistemler Nerede Çuvallıyor? yazımıza da göz atmanızı tavsiye ederim.
Bunu ilk kez test ettiğimde şaşırdım açıkçası. Eski projelerde tenant bağlamını prop drilling ile taşırken kodun içi tam bir çorba ölüyordu, özellikle proje büyüyünce. Burada işe bağlam neredeyse görünmez hâle geliyor — temiz, düzenli, debug etmesi çok daha kolay. Bu konuyla ilgili Avrupa’nın Veri Güvensizliği: ABD ve Çin Neden Aynı Kaderi Paylaşıyor? yazımıza da göz atmanızı tavsiye ederim.
// Basit bir tenant çözümleme fikri
export function middleware(request) {
const host = request.headers.get("host") || "";
const tenant = host.split(".")[0];
const headers = new Headers(request.headers);
headers.set("x-tenant", tenant);
return NextResponse.next({
request: { headers }
});
}
Lafı gevelemeden söyleyeyim: bu desen küçük ekiplerde çok iyi çalışıyor. Frontend ve backend sınırı bulanıklaşmadan ilerliyorsunuz. Ama dikkat edin — middleware içine fazla iş yüklerseniz performans hissi bozulur. Kısacası kapıda kısa kontrol tamam, ama uzun mülakat yapmayın.
Veri Modeli: Şişirmeden Büyütmek Lazım
SaaS veri modeli tasarlarken en büyük tuzak “ileride lazım ölür” diye her şeyi tabloya doldurmak oluyor. Oysa iyi şema tasarımı geleceği tahmin etmekten çok bugün hangi sorguları çalıştıracağını bilmekle ilgili. Ben buna kendi kafamda “önce kullanım senaryosu, sonra kolon” diyorum — basit ama işe yarıyor. LangChain Ajanlarını Üretimde İzlemek: Gerçek Zamanlı Rehber yazımızda da bu konuya değinmiştik. ₹30 LPA Bir Geliştiricinin Günü: Maaştan Fazlası Ne Var? yazımızda da bu konuya değinmiştik.
Genelde merkezde organizations tablosu ölür; üyelikler memberships üzerinden yönetilir; ürün alanına göre invoices, projects veya tickets gibi tablolar organization_id referansı taşır. Bu kadar net olmak çoğu zaman yeterli olurdu… eğer herkes kafasına göre ekstra alan eklemeseydi tabi. Neyse.
Bu fark küçük görünüyor ama altı ay sonra raporlama yaparken hayat kurtarıyor.
Peki ne yapmalı? Kritik alanları normal kolonlarda tutun, değişken metadata’yı JSONB’ye atın, arama gerekiyorsa full-text search ekleyin. En baştan pagination düşünün — sonsuz listeyi sonradan düzeltmek gerçek bir işkence. Bir de connection pooling meselesi var; PgBouncer gibi çözümleri erkenden koymazsanız trafik artınca hoş olmayan sürprizler yaşayabilirsiniz. Yaşayanlar bilir.
- Kritik ilişkiler: organization_id mutlaka ana eksen olsun.
- Erişim modeli: owner/admin/member gibi rollerle başlayın. — bunu es geçmeyin
- Sorgu alışkanlığı: filtrelenmeyen büyük liste bırakmayın.
- Büyüme önlemi: indeksleri geç değil erken düşünün.
Kimlik Doğrulama ve Yetki Kontrolü Neden Dağılmaz?
Ne yalan söyleyeyim, SaaS tarafında authentication ile authorization birbirine karıştırılıyor sık sık. Halbuki bunlar farklı şeyler. Kim olduğunuzu auth (söylemesi ayıp) söyler; ne yapabileceğinizi işe authorization belirler. Benim pratikte gördüğüm en sağlam düzenlerden biri, session yönetimi için hazır bir sistem kullanıp rol kontrolünü uygulama içinde net kurallarla yapmak oldu.
Klasik üçlü çoğu üründe yetiyor: owner, admin ve member. Tabii her üründe yetki matrisi değişir; muhasebe odaklı bir araçta read-only finance rolü gerekebilir, destek ekiplerine sadece ticket erişimi açılır, vs. Ama temel mantık aynı kalır — yetkiyi rastgele dağıtmazsınız. Dağıtırsanız başınız ağrır.
Birkaç sene önce Berlin’de çalışan bir arkadaşım bana şunu demişti: “Yetkiyi sonradan eklemek ev tadilatı gibi.” Haklıydı. Hele bir de server action’larda kontrol unutulursa sorun doğrudan üretime sızar. Bu yüzden RBAC’i API katmanına gömmek daha güvenli; client tarafındaki gizleme sadece kozmetik kalmalı — asla güvenlik mekanizması olarak görmeyin bunu.
Kaba ama İş Gösteren Güvenlik Akışı
- Kullanıcı giriş yapar ve oturum açılır — ama dür, oturum bilgisini alırsınız ve tenant bağlamını ayrıca doğrularsınız. Bu iki adım birbirinden farklı.
- Tüm isteklerde x-tenant değeri kontrol edilir.
- Kritik işlemler server side izin denetiminden geçer.
- Tamamen güvenilecek tek yer client değildir. Hiçbir zaman.
Sipariş Değil Abonelik Dünyası: Stripe Mantığı
Sıkça Sorulan Sorular
Next.js ile PostgreSQL’i SaaS’te nasıl birlikte düşünmeliyim?
Next.js tarafında kimlik doğrulama, route handler’lar ve (gerekiyorsa) middleware ile istekleri doğru şekilde yönlendirmek işin merkezinde. PostgreSQL tarafında işe tenant ayrımını ve veri güvenliğini RLS gibi mekanizmalarla sağlamayı hedeflersiniz. Benim pratikte gördüğüm en iyi yaklaşım, “güvenliği uygulama katmanına yıkmadan” veritabanına da bir sorumluluk vermek.
Multi-tenancy için shared database + tenant_id gerçekten yeterli mi?
Birçok startup için evet, başlangıçta shared database + tenant_id en mantıklısı oluyor; maliyet ve operasyon yükü ciddi azalıyor. Ancak iş büyüdükçe performans, indeksleme ve RLS policy’lerinin kalitesi kritik hâle geliyor. İsterseniz hibrit modele geçiş planını baştan düşünmek de iyi ölür; ben ikinci ayda “sonradan düzeltirim” yaklaşımının ne kadar pahalıya patlayabildiğini gördüm.
PostgreSQL RLS kullanırken en sık yapılan hata ne?
En yaygını policy mantığını yanlış kurmak ya da eksik test yapmak. Sonuç olarak bazı sorgular ya beklenenden fazla veriyi döndürür ya da tam tersine görünmez ölür. Ekipte SQL’e aşinalık düşükse, policy’leri bir “standart” gibi dokümante etmek ve otomatik testle doğrulamak gerçekten fark yaratıyor.
Ayrı veritabanı mı, ortak veritabanı mı seçmeliyim?
Tek bir doğru yok; karar genelde ölçek beklentisi, maliyet ve operasyon kabiliyetiyle şekilleniyor. Ortak veritabanı daha hızlı başlatır, ayrı veritabanı işe izolasyonu artırır ama operasyon yükünü yükseltir. Ben genelde “ilk etapta ortak + iyi tenant ayrımı” ile başlıyor, ardından ölçüme göre ayrıştırmayı kademeli düşünüyorum.
Tenant ayrımını sadece uygulama katmanında yapmak neden riskli?
Uygulama katmanında kontrol yapmak mümkün ama her sorguda doğru filtreyi koymayı unutmamak gerekir; unuttuğunuz anda güvenlik açığı oluşur. RLS ile bu kontrolü veritabanına taşıdığınızda hata yüzeyi azalır ve güvenlik daha tutarlı hâle gelir. Kısacası, kapıyı salonun içine değil binanın girişine kilitlemek gibi düşünün.
Kaynaklar ve İleri Okuma
Azure PostgreSQL ve Row-Level Security (RLS)
Microsoft Azure Architecture: Multi-tenant uygulama tasarımı
Next.js Route Handlers (App Router)
Vercel/Next.js GitHub (güncel dokümantasyon ve örnekler)
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



