High-Performance Queries with Entity Framework Core 10
N+1 problem'ini çözün, AsNoTracking kullanın ve raw SQL ile veri çekerek yüksek performanslı sorgulamalar yazın.
03.6a990c3e numaralı fikir
SaaS uygulamalarında en önemli mimari kavramlardan biri tenant yönetimidir. Doğru tasarlanmış bir tenant yapısı, tek bir uygulama üzerinden birçok müşteriye güvenli ve ölçeklenebilir şekilde hizmet vermeyi mümkün kılar. Bu yazıda tenant kavramını, subdomain ve custom domain kullanımını, veri izolasyonunu ve ASP.NET Core üzerinde uygulanabilecek temel yaklaşımları ele alıyoruz. Ayrıca gerçek hayatta karşılaşılabilecek senaryolar üzerinden multi-tenant mimarinin neden önemli olduğunu inceliyoruz.
SaaS (Software as a Service) uygulamaları gün geçtikçe daha fazla tercih ediliyor. Özellikle aynı uygulamanın onlarca hatta binlerce farklı müşteriye hizmet verdiği sistemlerde en kritik kavramlardan biri tenant yapısıdır. Aslında SaaS mimarisinin temelini tenant yönetimi oluşturur. Çünkü kullanıcı doğrulama, veri izolasyonu, lisanslama, abonelik yönetimi ve yetkilendirme gibi birçok konu doğrudan tenant mantığı üzerine kuruludur. Bu nedenle multi-tenant bir sistem geliştirmeden önce tenant kavramını doğru anlamak oldukça önemlidir.
En basit tanımıyla tenant, sistemi kullanan bağımsız müşteri veya firma anlamına gelir. Örneğin bir CRM uygulaması geliştirdiğinizi düşünelim.
afirmasi.blabla.com → Tenant Abfirmasi.blabla.com → Tenant BHer iki firma aynı uygulamayı kullanmasına rağmen;
Yani uygulama fiziksel olarak tek olsa bile her müşteri kendisini ayrı bir sisteme bağlanıyormuş gibi hisseder. Kısacası;
Tenant = Firma = Company = Müşteri
Birçok projede TenantId, CompanyId veya OrganizationId gibi isimlerle de karşımıza çıkabilir. Önemli olan isim değil, temsil ettiği mantıktır.
Tek bir müşteri için geliştirilen uygulamalarda tenant kavramına ihtiyaç duyulmayabilir. Ancak SaaS dünyasında her müşteri için ayrı bir uygulama yayınlamak ciddi operasyonel maliyet oluşturur. Multi-tenant mimaride ise;
Bu yaklaşım hem geliştirme hem de operasyon maliyetlerini önemli ölçüde azaltır. Örneğin yeni bir özellik geliştirdiğinizde her müşteriye ayrı ayrı kurulum yapmak yerine uygulamayı bir kez güncellemeniz yeterlidir.
Bir isteğin hangi tenant'a ait olduğunu anlayabilmek için önce tenant'ın belirlenmesi gerekir. En yaygın iki yöntem vardır.
En sık kullanılan yöntemlerden biridir. Örnek olarak;
afirmasi.blabla.com
bfirmasi.blabla.com
İstek uygulamaya ulaştığında host bilgisi okunur.
afirmasi.blabla.com
Buradan afirmasi bilgisi alınır ve Tenant tablosunda sorgulanır.
Elde edilen TenantId, uygulamanın geri kalanında kullanılmaya başlanır.
Bu yöntem özellikle çok sayıda müşterisi olan SaaS uygulamalarında oldukça pratiktir.
Bazı müşteriler kendi alan adlarını kullanmak ister. Örneğin;
www.afirmasi.com
Bu durumda müşteri DNS tarafında bir CNAME kaydı oluşturur ve alan adını SaaS uygulamasına yönlendirir. .NET tarafında ise host bilgisi okunarak ilgili tenant bulunur.
www.afirmasi.com
Kullanıcı açısından deneyim tamamen kendi markası altında devam eder. Özellikle kurumsal müşteriler için bu yöntem oldukça yaygındır.
Genellikle merkezi bir Tenant tablosu oluşturulur.
CREATE TABLE Tenants (
TenantId INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(100) NOT NULL,
Subdomain NVARCHAR(100) NULL,
CustomDomain NVARCHAR(255) NULL,
IsActive BIT DEFAULT 1,
CreatedAt DATETIME DEFAULT GETDATE()
);
Alanların amacı şöyledir;
TenantId : Benzersiz tenant kimliği Name : Firma adı Subdomain: Subdomain kullanan müşteriler CustomDomain: Kendi domainini kullanan müşteriler IsActive: Abonelik veya kullanım durumu CreatedAt: Oluşturulma tarihi
Gerçek projelerde bunlara ek olarak;
gibi alanlar da eklenebilir.
IIS'in görevi tenant'ı çözmek değildir. IIS yalnızca gelen isteği doğru uygulamaya yönlendirir.
DNS tarafında;
*.blabla.com
tek IP adresine yönlendirilir. IIS üzerinde ise tek site çalışır.
Host Name : blabla.com
Böylece bütün subdomain'ler aynı uygulamaya gelir.
Müşteri kendi domainini CNAME ile SaaS uygulamasına yönlendirir.
www.afirmasi.com
İstek yine aynı uygulamaya ulaşır. Sonrasında tenant çözümleme işlemi tamamen uygulama içerisinde yapılır. Yani IIS yalnızca isteği karşılar, hangi tenant'ın kullanılacağına uygulama karar verir.
ASP.NET Core projelerinde bu işlem genellikle middleware ile yapılır.
public class TenantMiddleware
{
private readonly RequestDelegate _next;
public TenantMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
{
var host = context.Request.Host.Host;
var tenant = tenantService.GetBySubdomainOrCustomDomain(host);
if (tenant == null)
{
context.Response.StatusCode = 404;
await context.Response.WriteAsync("Tenant bulunamadı.");
return;
}
context.Items["Tenant"] = tenant;
await _next(context);
}
}
Bu middleware her istekte çalışır.
Önce host bilgisini alır.
Ardından ilgili tenant'ı bulur ve HttpContext içerisine ekler.
Böylece uygulamanın diğer katmanları tekrar tekrar tenant sorgulamak zorunda kalmaz.
Daha büyük projelerde bu bilgi genellikle ITenantContext benzeri bir servis üzerinden tüm uygulamaya taşınır. Böylece servisler, repository katmanı ve yetkilendirme mekanizmaları aynı tenant bilgisini güvenli şekilde kullanabilir.
Tenant bilgisini bulmak tek başına yeterli değildir. Asıl önemli konu, her tenant'ın yalnızca kendi verilerini görebilmesidir. EF Core kullanıyorsanız bunu Global Query Filter ile oldukça temiz şekilde çözebilirsiniz.
modelBuilder.Entity<Call>()
.HasQueryFilter(c => c.TenantId == _tenantContext.TenantId);
Bu sayede yazılan sorguların büyük kısmında ayrıca TenantId filtresi eklemek gerekmez.
Ancak burada dikkat edilmesi gereken önemli bir nokta vardır.
Global Query Filter kullanılıyor olsa bile, yönetici ekranları, raporlama servisleri veya arka plan görevlerinde filtrelerin bilinçli şekilde devre dışı bırakılması gerekiyorsa bunun kontrollü yapılması gerekir. Yanlış yapılandırılmış sorgular farklı tenant verilerine erişilmesine neden olabilir.
Bu soru multi-tenant projelerde en sık karşılaşılan konulardan biridir. Aslında tek bir doğru cevap yoktur.
Avantajları:
Dezavantajları:
Avantajları:
Dezavantajları:
Gerçek hayatta birçok SaaS ürünü başlangıçta ortak veritabanı modeliyle ilerler. Müşteri sayısı ve kurumsal ihtiyaçlar arttığında ise büyük müşteriler için ayrı veritabanına geçiş yapılabilir. Bu hibrit yaklaşım hem maliyet hem de ölçeklenebilirlik açısından sık tercih edilen bir yöntemdir.
Doğru tasarlanmış bir tenant mimarisi birçok avantaj sağlar.
Örneğin Premium pakette çalışan bir müşteriye ek raporlama modülü açarken, diğer müşteriler aynı uygulamayı kullanmaya devam edebilir. Bunun için çoğu zaman yalnızca tenant bazlı bir özellik tanımı yeterlidir.
Tipik bir istek aşağıdaki adımlarla ilerler:
subdomain veya custom domain) okunur.Bu akış sayesinde tüm müşteriler aynı uygulamayı kullanırken birbirlerinin verilerine erişemez.
Tenant kavramı, modern SaaS uygulamalarının temel yapı taşlarından biridir. Doğru kurgulanmış bir tenant yönetimi; veri izolasyonu, güvenlik, ölçeklenebilirlik ve bakım kolaylığı gibi birçok avantaj sağlar. İster subdomain ister custom domain kullanın, önemli olan tenant bilgisini uygulamanın erken aşamasında doğru şekilde çözümlemek ve tüm katmanlarda tutarlı olarak kullanmaktır. Proje büyüdükçe tenant yönetimi yalnızca bir kimlik bilgisi olmaktan çıkar; yetkilendirme, lisanslama, özellik yönetimi ve veri mimarisinin merkezine yerleşir. Bu nedenle mimariyi en baştan doğru planlamak, ileride karşılaşılabilecek birçok operasyonel ve güvenlik probleminin önüne geçecektir.