BLOG · mimari· çok kiracılılık
Tek yazılımı birden fazla üniversiteye kendi markasıyla sunmak: çok kiracılı mimari
Efe Müderrisoğlu · 1 Ağustos 2026 · 13 dakika
Bir kampüs yazılımını ikinci üniversiteye satarken ilk sorulan şeylerden biri markadır. Kurum, öğrencisinin karşısına kendi adıyla ve kendi rengiyle çıkmak ister. Bunu sağlamanın kolay ama yanlış yolu her kuruma ayrı bir kurulum vermek, zor ama doğru yolu tek kurulumun kiracıyı çalışma anında çözmesidir.
Her kuruma ayrı kurulum yapmak neden pahalı?
Maliyet sunucu faturası değil, çarpan etkisidir. Kurulum sayısı arttıkça yapılan her iş o sayıyla çarpılır ve ekibin zamanı üründen bakıma kayar.
Somut olarak on kurulumda şunlar on kez yapılır: veritabanı göçü, bağımlılık güncellemesi, güvenlik yaması, sertifika yenileme, hata düzeltmesinin dağıtımı, yedek doğrulaması. Bunların hiçbiri zor değildir; sorun, birinin atlanmasıyla kurulumların birbirinden ayrışmaya başlamasıdır. Altı ay sonra elinizde tek bir ürün değil, birbirine benzeyen ama aynı olmayan on farklı sürüm olur. O noktadan sonra “şu özellik hangi kurumda var” sorusuna cevap veremezsiniz.
| Kurum başına kurulum | Tek kurulum, çok kiracılı | |
|---|---|---|
| Göç işlemi | Kurum sayısı kadar | Bir kez |
| Sürüm ayrışması | Zamanla kaçınılmaz | Yapısal olarak imkânsız |
| Yeni kurum ekleme | Altyapı kurulumu | Bir kayıt satırı |
| Veri izolasyonu | Fiziksel, doğal | Kodla sağlanır, disiplin ister |
| Kuruma özel şema değişikliği | Mümkün | İstenmez |
| Kaynak kullanımı | Her kurum kendi boşluğunu öder | Paylaşılır |
Tek kurulumun bedeli tek yerdedir: izolasyon artık altyapının değil, sizin sorumluluğunuzdadır. Bu bedel ödenmeye değer, ama ödendiğinin farkında olmak gerekir.
Kiracı çalışma anında nasıl çözümlenir?
En pratik yol hostname’dir. Uygulama tek build olarak dağıtılır, gelen isteğin konak adındaki ilk etiket kiracının kısa koduna eşlenir ve o kiracının kaydı yüklenir.
mef.cardeacore.com → subdomain "mef" → kiracı kaydı
cardeauni.cardeacore.com → subdomain "cardeauni" → kiracı kaydı
cardeacore.com → apex, kiracı yok → tanıtım sayfaları Bizde kiracı tablosu bu eşlemeyi taşır: kısa kod, görünen ad, girişte kabul edilen e-posta alan adı, kiracıyı çözen subdomain etiketi, saat dilimi, arayüz dili ve marka nesnesi.
tenants
slug "mef"
name "MEF Üniversitesi"
email_domain "mef.edu.tr"
subdomain "mef"
timezone "Europe/Istanbul"
locale "tr"
branding { logo, favicon, app_name, colors: { accent, accent_ink } } Giriş ekranı, sayfa açılırken kiracı listesini çeker, konak adının ilk etiketiyle eşleşeni bulur ve markayı ona göre kurar. Eşleşme yoksa uygulama kendi varsayılan görünümüne düşer. Bu geri düşüş önemlidir: markası tanımlanmamış yeni bir kiracı, kırık bir ekran yerine sade bir ekran görür.
Subdomain’in yol tabanlı ayrıma göre avantajı, tarayıcı tarafındaki her şeyin
doğal olarak ayrışmasıdır. Çerez kapsamı, yerel depolama, servis çalışanı, hatta
kullanıcının sekme geçmişi. campus.com/mef ile campus.com/bogazici aynı
kaynağı paylaşır ve bir kiracıda önbelleğe alınan veri diğerinde görünebilir.
Marka hangi katmanlarda değişmeli?
Kurumun beklediği “kendi markası”, tek bir renk değişikliğinden ibaret değildir. Değişmesi gereken katmanları baştan listelemek, sonradan her biri için ayrı ayrı kod dolaşmaktan ucuzdur.
| Katman | Kaynak | Kiracıya göre değişir mi |
|---|---|---|
| Vurgu rengi ve kontrast rengi | branding.colors | Evet |
| Logo ve favicon | branding.logo, branding.favicon | Evet |
| Uygulama adı ve giriş sloganı | branding.app_name | Evet |
| Giden e-postanın gönderen adı | kiracı ayarı | Evet |
| E-posta şablonunun üst bandı | marka nesnesi | Evet |
| Bildirim başlığı | uygulama adı | Evet |
| Mobil uygulama ikonu ve mağaza adı | derleme zamanı | Duruma göre |
| Yasal metinler ve veri sorumlusu | kiracı ayarı | Evet |
| Ekran yerleşimi ve akış | kod | Hayır |
Son satır en önemlisidir. Marka kişiselleştirmesi görünümde durmalı, akışa sızmamalıdır. “Bu üniversitede onay iki aşamalı olsun” gibi bir istek marka değil, yapılandırma meselesidir ve veri modelinde bir ayar olarak karşılanmalıdır. Kiracıya özel kod dalları yazmaya başladığınız gün, ayrı kurulumların bütün maliyetini tek kurulumun içine taşımış olursunuz.
CSS değişkeniyle tema
Renk kişiselleştirmesinin en ucuz uygulaması, kiracının renklerini kök öğeye CSS değişkeni olarak yazmaktır. Bileşenler değişkeni okur, kiracıyı bilmez.
// Kiracı accent'i yoksa null döner → uygulamanın varsayılan moru kalır.
export function tenantAccentVars(tenant) {
const accent = tenant?.branding?.colors?.accent;
if (!accent) return null;
return {
'--color-accent-val': accent,
'--color-accent-dark-val': darken(accent, 0.18),
'--color-accent-soft-val': alpha(accent, 0.14),
'--color-accent-rgb-val': toRgbTriplet(accent),
'--color-on-accent-val': tenant.branding.colors.accent_ink ?? '#FFFFFF'
};
} Burada iki ayrıntı işe yarıyor. Birincisi, tek bir vurgu renginden türetilen koyu, yumuşak ve RGB üçlü varyantları; bir kurum size yalnız kurumsal kırmızısını verir, gerisini siz üretirsiniz. İkincisi, çıkışta veya markasız kiracıya geçişte bu değişkenlerin hepsinin temizlenmesi. Yarım temizlik, bir önceki kiracının renginin ekranın bir köşesinde kalmasına yol açar ve bu hata her zaman demoda ortaya çıkar.
Veri izolasyonu ve kiracı sızıntısı riski
Tek veritabanında en büyük risk, bir kiracının verisinin diğerine görünmesidir.
Bu genelde kötü niyetle değil, unutulmuş bir where koşuluyla olur.
Riski azaltan üç kural var.
Birincisi, kiracı kimliğinin tek kaynağı oturumdur. İstemciden gelen kiracı bilgisi yalnız görünüm amaçlıdır; yetkilendirme kararına asla girmez. Bir istekte hem oturumdaki kiracı hem parametredeki kiracı varsa, oturumdaki kazanır ve uyuşmazlık hata olarak döner.
İkincisi, kiracı filtresi varsayılan olmalıdır. Sorguyu yazan geliştiricinin filtreyi eklemeyi hatırlamasına dayanan bir sistem, er ya da geç bir kez unutulur. Veri erişim katmanında kiracı kapsamını otomatik uygulayan bir yardımcı, ondan sapmayı bilinçli bir tercih hâline getirir.
Üçüncüsü, test tarafında ikinci kiracının varlığıdır. Her yeni uç için testte ikinci bir kiracı ve ona ait bir kayıt oluşturup, birinci kiracının jetonuyla o kaydın görünmediğini doğrulayan bir senaryo yazın. Bu, yazması iki dakika süren ve en pahalı hatayı yakalayan testtir.
Dosya depolaması da unutulmamalı. Yükleme yollarına kiracı kimliğini koymak ve imzalı bağlantılarda kiracıyı doğrulamak, veritabanı tarafındaki disiplinin dosya tarafındaki karşılığıdır.
Kiracıya göre modül açıp kapamak
Her kurum her modülü almaz. Bir üniversite yalnız kulüp ve etkinlik tarafını kullanırken, diğeri öğrenme yönetimini de açar. Bunun uygulaması, kiracı kaydında etkin modül listesi tutmak ve arayüzde menüyü o listeden üretmektir.
Yerel geliştirmede aynı mekanizmanın ortam değişkeni karşılığı işe yarar; henüz kimseye açılmamış bir modülü kendi makinenizde açık, dağıtımda kapalı tutmanızı sağlar.
ENABLED_MODULES=lms,ois Burada bir tuzak var: modül kapalıyken de arka uçtaki uçların erişilebilir kalması. Menüde görünmemek erişimi engellemez. Modül denetimi, arayüzde değil, istek yetkilendirmesinde uygulanmalıdır.
Yerel geliştirmede kiracıyı taklit etmek
localhost üzerinde subdomain çalışmaz, dolayısıyla kiracı çözümlemesi de
çalışmaz. İki pratik çözüm var.
Birincisi, joker DNS sunan bir yerel alan adı kullanmak: mef.lvh.me gibi
adresler 127.0.0.1‘e çözülür ve subdomain mantığı üretimdeki gibi işler. Bu,
çerez kapsamı ve yönlendirme davranışını da gerçekçi tutar.
İkincisi, sorgu parametresiyle geçici bir geçersiz kılma: ?tenant=mef.
Kurulumu sıfırdır ve hızlı denemeler için yeterlidir. Şartı, bu yolun yalnız
geliştirme ortamında açık olması ve yetkilendirmeye değil, yalnız görünüme etki
etmesidir.
Göç yönetimi
Tek veritabanı, göç tarafını basitleştirir: şema bir kez ilerler, bütün kiracılar aynı anda yeni şemayı görür. Karşılığında iki disiplin gerekir.
Şema kiracıya özel olamaz. Bir kuruma özel kolon eklemek, o kolonun diğer bütün kiracılarda boş durması demektir ve bu istisnalar birikerek şemayı okunamaz hâle getirir. Kuruma özel alanlar, tıpkı marka nesnesi gibi, esnek bir JSON kolonunda toplanmalıdır.
Göç, dağıtımın parçası olmalıdır. Kod canlıya çıkıp göç çalışmadığında, yeni kolonu okuyan her istek hata döner ve bu hata bütün kiracıları aynı anda vurur. Bizde bunun acısı bir kez çekildi: birleştirme sonrası göç komutu unutuldu, uygulama yeni kolonu sorguladı ve tek kurulum olduğu için etki anında herkese yayıldı. Tek kurulumun tek göçü avantajdır, ama tek arıza noktası olduğunu da kabul etmek gerekir.
Bir de göç geçmişinin düz kalması meselesi var. Paralel çalışan dallarda birbirinden habersiz göç dosyaları üretildiğinde geçmiş çatallanır ve “en son sürüme yükselt” komutu hangi ucu takip edeceğini bilemez. Yeni göç yazmadan önce mevcut uçları kontrol etmek, sonradan birleştirme göçü yazmaktan ucuzdur.
E-posta ve bildirim katmanı neden ayrı ele alınmalı?
Arayüzün markası doğru olsa bile, kurumun öğrencisine giden e-posta hâlâ sizin ürününüzün adıyla düşüyorsa marka çalışması yarım kalmıştır. Öğrenci sistemi günde bir kez açar, e-postayı her gün görür.
Değişmesi gereken alanlar sanıldığından fazladır: gönderen adı, gönderen adresi ya da en azından yanıt adresi, e-posta şablonunun üst bandı ve logosu, altbilgideki kurum bilgisi, bağlantıların işaret ettiği alan adı. Bunlardan sonuncusu en çok atlanandır; kurumun kendi alan adında görünen bir e-postanın içindeki bağlantı sizin genel alan adınıza gidiyorsa, kullanıcı bunu tutarsızlık olarak okur.
Gönderen adresinin kurumun alan adında olması, teknik olarak alan adı kimlik doğrulama kayıtlarının kurum tarafından açılmasını gerektirir. Bu iş kurumun bilgi işlem birimindedir ve süreci uzatır; kurulum planında bir madde olarak yer almalı, sonradan hatırlanmamalı.
Anlık bildirimlerde de aynı mantık geçerlidir. Bildirim başlığı uygulamanın kiracıya özel adını taşımalıdır, çünkü telefon ekranında görünen tek metin odur.
Kiracıya özel davranış nerede durmalı?
Marka kişiselleştirmesi ile davranış özelleştirmesi arasındaki sınır, çok kiracılı bir üründe en kritik mimari karardır. Görünüm verideyse ürün ölçeklenir, davranış koddaysa ürün ölçeklenmez.
Pratik ölçüt şudur: bir istek geldiğinde kendinize sorun, bunu karşılamak için if tenant == "x" yazmam gerekiyor mu? Cevap evetse istek yanlış katmandadır.
Aynı ihtiyacı bütün kiracılara açık bir ayar olarak ifade edebiliyorsanız,
yapılandırma olarak karşılayın. Edemiyorsanız, gerçekten gerekli olup olmadığını
tekrar sorgulayın.
| İstek | Doğru katman |
|---|---|
| Kurumsal renk ve logo | Marka verisi |
| Onay zincirinin kademe sayısı | Kiracı yapılandırması |
| Etkinlik türü listesi | Kiracı verisi |
| Belirli bir modülün kapalı olması | Kiracı yapılandırması |
| Bir kuruma özel ekran akışı | Genelde reddedilmeli |
| Bir kuruma özel veri alanı | Esnek JSON alanı |
Reddedilen isteğin yerine ne konulacağı da önemli. Çoğu zaman kurumun istediği özel akış, aslında var olan akışın bir ayarla karşılanabilecek bir varyantıdır; sorulması gereken, isteğin arkasındaki ihtiyaçtır.
Mobil tarafı
Mobil, çok kiracılılığın en pahalı katmanıdır çünkü mağaza kimliği derleme zamanında sabitlenir. Uygulamanın içeriğini kiracıya göre değiştirmek kolaydır; mağazada kurumun kendi adıyla görünmesi ise ayrı bir uygulama kimliği, ayrı ikon, ayrı mağaza kaydı ve kurum sayısı kadar çoğalan bir sürüm süreci demektir.
Karar, kurumun beklentisiyle ekibin kapasitesi arasında verilir. Tek uygulamada kiracıya göre tema, çoğu kurum için yeterlidir. Kendi adıyla mağazada olmak isteyen kurum içinse ortak kod tabanından çoklu hedef üretmek gerekir, ve bunun sürüm yükü baştan planlanmalıdır.
CardeaCore bu işi nasıl yapıyor
CardeaCore tek build olarak dağıtılır. Kiracı, konak adının ilk etiketinden çözülür; kiracı kaydı kısa kodu, görünen adı, giriş için kabul edilen e-posta alan adını, saat dilimini, arayüz dilini ve marka nesnesini taşır. Marka nesnesindeki renkler CSS değişkenlerine yazılır, bileşenler kiracıyı bilmeden doğru renkle boyanır; marka tanımlı değilse uygulama kendi varsayılan moruna düşer.
Modüller kiracı bazında açılır, geliştirme ortamında ortam değişkeniyle taklit edilir. Yerel geliştirmede kiracı ya joker DNS’li bir yerel alan adıyla ya da sorgu parametresiyle taklit edilir.
Veri tarafında kiracı filtresi bir hatırlanacak adım değil, sorgunun varsayılanı olarak duruyor. Test tarafında da her yeni uç için ikinci kiracı senaryosu yazılıyor. Bu iki alışkanlık, çok kiracılı bir kampüs platformunda kod kalitesinden çok daha belirleyici.
SIK SORULAN SORULAR
Sık sorulan sorular
Çok kiracılı mimari ne demek?
Tek bir uygulama kurulumunun birden fazla kurumu, her birinin verisi ve görünümü ayrı kalacak biçimde barındırmasıdır. Kurumlar aynı kodu ve aynı veritabanını paylaşır, ayrımı kiracı kimliği sağlar. Alternatifi, her kuruma ayrı sunucu ve ayrı kurulum vermektir.
Her üniversiteye ayrı kurulum yapmak neden pahalı?
Maliyet sunucu kirası değil, çarpan etkisidir. On kurulumda her güvenlik yaması, her göç ve her hata düzeltmesi on kez uygulanır ve sürümler zamanla birbirinden ayrışır. Üç dört kurulumdan sonra ekibin işi ürün geliştirmek değil, kurulumları senkron tutmak olur.
Kiracıyı nasıl çözümlemeli, subdomain mi yoksa yol mu?
Subdomain pratikte en temizidir çünkü çerez kapsamı, marka algısı ve giriş akışı doğal olarak ayrışır. Kurum kendi alan adını istediğinde aynı çözümleme özel alan adı eşlemesine genişletilir. Yol tabanlı ayrım tarayıcı depolamasını paylaştırdığı için sızıntı riskini artırır.
Marka değişimi için ayrı build almak gerekir mi?
Gerekmez ve önerilmez. Renk, logo, isim gibi değerler veriden okunup CSS değişkenlerine yazıldığında tek build bütün kurumlara hizmet eder. Kurum başına build, dağıtım sayısını kurum sayısıyla çarpar.
Kiracı sızıntısı riskini nasıl azaltırım?
Kiracı filtresini isteğe bağlı bir where koşulu olmaktan çıkarıp veri erişim katmanının varsayılanı hâline getirin. Kimliğin tek kaynağı oturum jetonu olsun, istemciden gelen kiracı bilgisine asla güvenilmesin. Testlerde her uç için ikinci bir kiracının verisini görmediğini doğrulayan bir senaryo bulunsun.
Mobil uygulamada marka nasıl değişir?
İki yol var. Aynı uygulamanın içeriğini kiracıya göre değiştirmek en ucuzudur ama mağaza kimliği tek kalır. Kurum kendi adıyla mağazada görünmek istiyorsa ayrı bir uygulama kimliği, ayrı ikon ve ayrı mağaza kaydı gerekir; kod paylaşılır, sürüm süreci kurum sayısı kadar çoğalır.