İş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 ise RLS, JSONB ve dayanıklı 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 ise 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 ise 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 oluyordu, özellikle proje büyüyünce. Burada ise bağlam neredeyse görünmez hale 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 olur” 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 olur; ü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 ise 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 dur, 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ığı
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



