Przejdź do treści głównej
Architektura SaaS

Budowa Multi-Tenant SaaS z .NET i Azure

9 min czytaniaMichał Wojciechowski

Multi-tenancy to fundament opłacalnej ekonomii SaaS. Pojedyncza infrastruktura obsługująca tysiące klientów jest 10-20x bardziej opłacalna niż dedykowane instancje per klient. Według Bessemer Venture Partners Cloud Index, udane firmy SaaS osiągają marże brutto 70-80%, głównie dzięki efektywnym architekturom multi-tenant.

Nowoczesna platforma SaaS - zespół pracujący nad infrastrukturą chmurową

Projektowałem platformy SaaS multi-tenant obsługujące od 50 do 5,000+ tenant. Ten przewodnik omawia wzorce architektoniczne, kwestie bezpieczeństwa, strategie bazodanowe i implementacje specyficzne dla Azure, które sprawiają że multi-tenant SaaS działa na dużą skalę.

01Modele Multi-Tenancy

1. Wspólna baza danych, wspólny schemat

Wszyscy tenant dzielą tabele z kolumną dyskryminatora TenantId. Najprostszy i najbardziej opłacalny model. Idealny dla platform korzystających z baz danych PostgreSQL, które oferują doskonałą wydajność dla dużej liczby tenant.

✓ Zalety

  • • Najniższy koszt infrastruktury
  • • Proste wdrożenia
  • • Łatwe backupy i utrzymanie
  • • Efektywne wykorzystanie zasobów
  • • Proste skalowanie horyzontalne

✗ Wady

  • • Ryzyko wycieku danych jeśli filtr pominięty
  • • Problemy noisy neighbor
  • • Ograniczona customizacja per-tenant
  • • Skomplikowany recovery tenant
  • • Wyzwania compliance

Najlepszy dla:

SMB SaaS z 100-10,000 tenant, podobne wzorce użycia, niskie wymagania compliance. Przykłady: narzędzia do zarządzania projektami, CRM dla małych firm.

2. Wspólna baza danych, osobne schematy

Pojedyncza baza danych ze schematem-per-tenant (schematy PostgreSQL lub SQL Server). Umiarkowana izolacja.

✓ Zalety

  • • Lepsza izolacja tenant
  • • Uprawnienia na poziomie schematu
  • • Łatwiejszy backup/restore tenant
  • • Dobra równowaga koszt-izolacja

✗ Wady

  • • Skomplikowane migracje schematu
  • • Overhead połączeń do bazy
  • • Ograniczona skalowalność (max kilka tysięcy schematów)
  • • Wolniejsze onboardowanie tenant

Najlepszy dla:

B2B SaaS dla średnich firm z 10-1,000 tenant, umiarkowane wolumeny danych per tenant, niektóre potrzeby compliance. Przykłady: vertical SaaS dla healthcare, legal.

3. Osobne bazy danych (Database-per-Tenant)

Każdy tenant dostaje dedykowaną bazę danych. Maksymalna izolacja i customizacja.

✓ Zalety

  • • Maksymalna izolacja bezpieczeństwa
  • • Łatwy backup/restore tenant
  • • Customizacja per-tenant
  • • Przyjazny dla compliance
  • • Izolacja noisy neighbor

✗ Wady

  • • Wysoki koszt infrastruktury
  • • Skomplikowane wdrożenia
  • • Overhead migracji schematu
  • • Złożoność zarządzania
  • • Trudna analityka cross-tenant

Najlepszy dla:

Enterprise B2B SaaS z wymaganiami regulacyjnymi, duże dane per tenant, customowe SLA. Przykłady: platformy HR, SaaS dla finansów.

4. Model hybrydowy (Rekomendowany dla wzrostu)

Łącz modele w zależności od warstwy tenant: wspólna baza dla SMB, dedykowane bazy dla enterprise.

Warstwa 1 (Free/Starter): Wspólna baza, wspólny schemat, 1,000+ tenant

Warstwa 2 (Professional): Wspólna baza, osobne schematy, 100-500 tenant

Warstwa 3 (Enterprise): Dedykowane bazy, 10-50 tenant

To maksymalizuje efektywność kosztową dla małych klientów jednocześnie oferując izolację dla klientów enterprise gotowych płacić premium.

02Implementacja z .NET i EF Core

Architektura oprogramowania - kod i diagramy struktury aplikacji

Middleware rozpoznawania Tenant

Kluczowym elementem każdej platformy multi-tenant jest poprawne rozpoznawanie tenant. W nowoczesnej platformie .NET middleware stanowi idealny punkt do implementacji tej logiki.

// Strategie identyfikacji tenant
public class TenantResolutionMiddleware
{
    public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
    {
        // Strategia 1: Subdomena (tenant1.app.com)
        var host = context.Request.Host.Host;
        var tenantId = ExtractTenantFromSubdomain(host);

        // Strategia 2: Header (X-Tenant-ID)
        if (string.IsNullOrEmpty(tenantId))
            tenantId = context.Request.Headers["X-Tenant-ID"];

        // Strategia 3: JWT Claim
        if (string.IsNullOrEmpty(tenantId) && context.User.Identity?.IsAuthenticated == true)
            tenantId = context.User.FindFirst("tenant_id")?.Value;

        // KRYTYCZNE: Nigdy nie ufaj tenant ID wysłanym przez klienta dla dostępu do danych
        // Zawsze waliduj względem dozwolonych tenant uwierzytelnionego użytkownika
        var tenant = await tenantService.GetAndValidateTenantAsync(tenantId, context.User);
        context.Items["TenantId"] = tenant.Id;
    }
}

Globalne filtry zapytań (EF Core)

Automatycznie filtruj wszystkie zapytania po tenant żeby zapobiec wyciekom danych:

public class ApplicationDbContext : DbContext
{
    private readonly ITenantService _tenantService;

    public ApplicationDbContext(DbContextOptions options, ITenantService tenantService)
        : base(options)
    {
        _tenantService = tenantService;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Zastosuj globalny filtr zapytań do wszystkich encji w zakresie tenant
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            if (typeof(ITenantEntity).IsAssignableFrom(entityType.ClrType))
            {
                var method = typeof(ApplicationDbContext)
                    .GetMethod(nameof(SetGlobalQueryFilter), BindingFlags.NonPublic | BindingFlags.Static)
                    ?.MakeGenericMethod(entityType.ClrType);

                method?.Invoke(null, new object[] { modelBuilder, _tenantService });
            }
        }
    }

    private static void SetGlobalQueryFilter<T>(ModelBuilder modelBuilder, ITenantService tenantService)
        where T : class, ITenantEntity
    {
        modelBuilder.Entity<T>().HasQueryFilter(e => e.TenantId == tenantService.GetCurrentTenantId());
    }
}

// Interfejs encji
public interface ITenantEntity
{
    string TenantId { get; set; }
}

// Przykładowa encja
public class Order : ITenantEntity
{
    public int Id { get; set; }
    public string TenantId { get; set; } // Automatycznie filtrowane
    public decimal Amount { get; set; }
}

Database-per-Tenant z rozpoznawaniem Connection String

public class TenantDbContextFactory
{
    private readonly ITenantService _tenantService;
    private readonly IConfiguration _configuration;

    public ApplicationDbContext Create()
    {
        var tenantId = _tenantService.GetCurrentTenantId();
        var connectionString = GetConnectionStringForTenant(tenantId);

        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
        optionsBuilder.UseNpgsql(connectionString);

        return new ApplicationDbContext(optionsBuilder.Options);
    }

    private string GetConnectionStringForTenant(string tenantId)
    {
        // Opcja 1: Dedykowana baza per tenant
        return $"Host=postgres.app.com;Database=tenant_{tenantId};Username=app;Password=...";

        // Opcja 2: Azure SQL Elastic Pool z bazami
        // return $"Server=tcp:app.database.windows.net;Database=tenant_{tenantId};...";

        // Opcja 3: Wyszukiwanie z tabeli konfiguracji tenant
        // var tenant = await _tenantRepo.GetAsync(tenantId);
        // return tenant.ConnectionString;
    }
}

03Usługi Azure dla Multi-Tenant SaaS

Infrastruktura chmurowa Azure - centrum danych i serwery

Przy wdrożeniu w Azure Kubernetes lub tradycyjnym App Service, Azure oferuje szereg usług idealnych dla platform multi-tenant. Kluczem jest optymalizacja kosztów chmury poprzez odpowiedni dobór warstw i auto-scaling.

Azure SQL Elastic Pools

Dziel zasoby obliczeniowe między wieloma bazami danych tenant zachowując izolację.

S3 Elastic Pool

100 eDTU, $200/miesiąc

~50 małych baz (2 eDTU każda)

S6 Elastic Pool

400 eDTU, $800/miesiąc

~100 średnich baz (4 eDTU śr.)

S12 Elastic Pool

3000 eDTU, $6,000/miesiąc

~500 baz z burstingiem

Azure AD B2C dla Multi-Tenant Auth

Zarządzaj użytkownikami tenant z Azure AD B2C: custom policies, social login, MFA, user flows.

  • • Przechowuj kontekst tenant w claimach JWT
  • • Wspieraj provisioning użytkowników oparty na zaproszeniach
  • • Włącz SSO dla tenant enterprise
  • • Ceny: Pierwsze 50K MAU za darmo, potem $0.00325/MAU

Application Insights - Telemetria Per-Tenant

// Dodaj kontekst tenant do całej telemetrii
services.AddApplicationInsightsTelemetry();
services.AddSingleton<ITelemetryInitializer, TenantTelemetryInitializer>();

public class TenantTelemetryInitializer : ITelemetryInitializer
{
    private readonly ITenantService _tenantService;

    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is ISupportProperties propertiesTelemetry)
        {
            propertiesTelemetry.Properties["TenantId"] = _tenantService.GetCurrentTenantId();
            propertiesTelemetry.Properties["TenantName"] = _tenantService.GetCurrentTenantName();
        }
    }
}

// Zapytanie w portalu Azure:
// requests | where customDimensions.TenantId == "tenant-123" | summarize count() by bin(timestamp, 1h)

04Skalowanie i Wydajność

Zespół deweloperski pracujący nad optymalizacją wydajności

Skalowanie platformy multi-tenant wymaga przemyślanej strategii. W architekturze mikrousług każdy komponent można skalować niezależnie, co idealnie wpasowuje się w modele multi-tenant z różnym obciążeniem per tenant.

Connection Pooling

Problem: Database-per-tenant tworzy overhead połączeń

Rozwiązanie: Użyj poolingu opartego na connection string z limitami max pool size

// .NET connection string
"...;Max Pool Size=20;Min Pool Size=2;"

20 połączeń × 100 baz = 2,000 max połączeń. Monitoruj z PgBouncer lub Azure SQL monitoring.

Cache Per-Tenant

Użyj Redis z kluczami z prefixem tenant:

var cacheKey = $"tenant:{tenantId}:orders:{orderId}";
await _cache.SetStringAsync(cacheKey, json,
    new DistributedCacheEntryOptions {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
    });

Umożliwia invalidację cache per-tenant i zapobiega zanieczyszczeniu cache cross-tenant.

Database Sharding

Dla 1,000+ tenant, sharduj między wiele serwerów bazodanowych:

  • • Shard 1: Tenant 1-500 → postgres-01
  • • Shard 2: Tenant 501-1000 → postgres-02
  • • Routuj na podstawie consistent hashing tenant ID
  • • Użyj Azure Database for PostgreSQL z read replicas

Zapobieganie Noisy Neighbor

Implementuj rate limiting per tenant:

[TenantRateLimit(MaxRequests = 1000, WindowMinutes = 1)]
public async Task<IActionResult> GetOrders()
{
    // Tenant automatycznie limitowany do 1000 req/min
}

Użyj Redis dla rozproszonego rate limiting między wieloma instancjami app.

05Wdrożenie w praktyce

Zespół pracujący nad wdrożeniem platformy SaaS

B2B SaaS do zarządzania projektami

Wybory architektoniczne

  • • Model hybrydowy: wspólna baza dla SMB, dedykowana dla enterprise
  • • 850 SMB tenant we wspólnym PostgreSQL (2TB łącznie)
  • • 15 tenant enterprise z dedykowanymi bazami
  • • Azure App Service P1V3 z autoscaling (2-10 instancji)
  • • Azure SQL Elastic Pool S9 (1600 eDTU)
  • • Redis Premium P1 dla cache i rate limiting
  • Komunikacja real-time z SignalR dla współpracy zespołowej

Koszty miesięczne

App Service P1V3 (śr. 4 instancje)$600
Azure SQL Elastic Pool S9$3,200
15 baz Enterprise (P2 tier śr.)$3,750
Redis Premium P1$250
Application Insights$150
Storage i CDN$180
Infrastruktura razem$8,130/miesiąc

Koszt per tenant: $9.50/miesiąc (850 SMB + 15 enterprise). MRR: $45K. Infrastruktura: 18% przychodu.

Kluczowe metryki po 18 miesiącach

99.97%

Uptime (SLA: 99.9%)

Zero

Wycieków danych cross-tenant

850 →

2,100 tenant (147% wzrost)

06FAQ

Który model multi-tenancy powinienem wybrać?

Wybierz na podstawie skali i potrzeb izolacji: wspólna baza/wspólny schemat dla 100-10,000 tenant (najniższy koszt), wspólna baza/osobne schematy dla 10-1,000 tenant (umiarkowana izolacja), osobne bazy dla tenant enterprise (maksymalna izolacja). Większość platform SaaS używa podejścia hybrydowego: wspólna baza dla SMB, dedykowana dla enterprise.

Jak obsługiwać customizacje specyficzne dla tenant?

Użyj podejścia opartego na konfiguracji: przechowuj preferencje tenant w tabelach metadanych, używaj flag funkcjonalności dla feature'ów per-tenant, implementuj architekturę plugin dla customowej logiki biznesowej. Unikaj branchy kodu specyficznych dla tenant. Zamiast tego użyj pattern strategy z dependency injection do wymiany implementacji per tenant.

Jakie są dobre praktyki bezpieczeństwa?

Kluczowe praktyki: (1) Nigdy nie ufaj tenant ID wysyłanym przez klienta, zawsze wyprowadzaj z uwierzytelnionej tożsamości, (2) Używaj globalnych filtrów zapytań EF Core żeby zapobiec wyciekom danych cross-tenant, (3) Implementuj middleware kontekstu tenant, (4) Szyfruj wrażliwe dane kluczami specyficznymi dla tenant, (5) Audituj operacje cross-tenant, (6) Testuj izolację tenant z automatycznymi testami, (7) Implementuj rate limiting per tenant.

Ile kosztuje prowadzenie na Azure?

Mały SaaS (100 tenant): $500-1,500/miesiąc. Średni SaaS (1,000 tenant): $3,000-8,000/miesiąc. Duży SaaS (10K+ tenant): $20K-50K+/miesiąc. Kluczem jest odpowiedni dobór warstwy i autoscaling. Użyj Azure Cost Management do monitoringu i reserved instances dla 40% oszczędności na przewidywalnych obciążeniach.

Czy mogę zmigrować z single-tenant do multi-tenant?

Tak, podejście fazowe: (1) Dodaj kontekst tenant do tabel, (2) Implementuj middleware rozpoznawania tenant, (3) Zaktualizuj kod żeby filtrować po tenant ID, (4) Skonsoliduj bazy z migracją danych, (5) Testuj pod kątem wycieków danych, (6) Uruchom równolegle dla walidacji. Spodziewaj się 3-6 miesięcy dla migracji produkcyjnej w zależności od wielkości codebase.

Budujesz Multi-Tenant SaaS?

Pomagam firmom SaaS projektować i implementować skalowalne architektury multi-tenant z .NET i Azure. Porozmawiajmy o Twojej architekturze SaaS.

Powiązane artykuły

Źródła

  1. [1] Microsoft Azure - Oficjalna dokumentacja -https://learn.microsoft.com/en-us/azure/
  2. [2] Microsoft Learn - Centrum szkoleń Azure -https://learn.microsoft.com/en-us/training/azure/
  3. [3] Kubernetes - Oficjalna dokumentacja -https://kubernetes.io/docs/
  4. [4] CNCF Annual Survey 2023 - Stan adopcji Kubernetes -https://www.cncf.io/reports/cncf-annual-survey-2023/
  5. [5] .NET - Oficjalna dokumentacja Microsoft -https://learn.microsoft.com/en-us/dotnet/
  6. [6] .NET Blog - Najnowsze informacje i best practices -https://devblogs.microsoft.com/dotnet/
  7. [7] Flexera State of the Cloud Report 2024 -https://www.flexera.com/blog/cloud/cloud-computing-trends-2024-state-of-the-cloud-report/
  8. [8] FinOps Foundation - Best Practices -https://www.finops.org/
  9. [9] Gartner - Cloud Computing Research -https://www.gartner.com/en/information-technology/insights/cloud-computing
  10. [10] AWS - Oficjalna dokumentacja -https://docs.aws.amazon.com/
Architektura Multi-Tenant SaaS z .NET 2025 | Wojciechowski.app | Wojciechowski.app