> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Tenancy

> organization_id / tenant_id JWT claim, soft-tenant yaklaşımı, ICurrentTenant ve pipeline izolasyonu

Sistem **soft multi-tenancy** kullanır: tüm tenant'lar tek veritabanını paylaşır, izolasyon
kayıt seviyesinde `organization_id` ile sağlanır. DB-per-tenant veya schema-per-tenant **yoktur**.

## Tenant kimliği nereden gelir

Tenant, JWT claim'inden okunur. İki eşdeğer claim kabul edilir:

* `tenant_id` (birincil)
* `organization_id` (Keycloak Protocol Mapper uyumluluğu; dev provisioning bunu kurar)

`HttpContextCurrentTenant` (`SeedWork/Security/HttpContextCurrentTenant.cs`) fallback uygular:

```csharp theme={null}
public Guid? TenantId
{
    get
    {
        var value =
            User?.Claims.FirstOrDefault(c => c.Type == ...TenantId)?.Value          // önce tenant_id
         ?? User?.Claims.FirstOrDefault(c => c.Type == ...OrganizationId)?.Value;   // sonra organization_id
        return value != null && Guid.TryParse(value, out var id) ? id : null;
    }
}
public bool HasTenant => TenantId.HasValue;
```

`ICurrentTenant` `Program.cs` → RBAC bölümünde kaydedilir:

```csharp theme={null}
builder.Services.AddScoped<ICurrentTenant, HttpContextCurrentTenant>();
```

## Domain arayüzleri

Tenant'a ait varlıklar SharedKernel'deki marker arayüzlerini implement eder:

```csharp theme={null}
// Tenant'a ait OLABİLEN (nullable — global kayıtlar da olabilir)
public interface IMayHaveTenant
{
    Guid? OrganizationId { get; }
}

// Her zaman bir tenant'a ait
public interface ITenant
{
    Guid OrganizationId { get; }
}
```

`IMayHaveTenant` global (tenant-agnostic) kayıtlara da izin verir; `ITenant` ise zorunlu tenant
bağı ifade eder.

## Pipeline-level izolasyon

Command/query bir tenant kaynağına eriştiğinde `IRequireTenantAccess` implement eder; bunu
`AuthorizationPipelineBehavior` (`Application/SeedWork/PipelineBehaviors/`) handler çalışmadan
önce doğrular:

```csharp theme={null}
public interface IRequireTenantAccess
{
    Guid TenantId { get; }   // route/body'den gelen organizasyon ID
}
```

```csharp theme={null}
if (request is IRequireTenantAccess tenantRequest)
{
    if (!_currentUser.IsInTenant(tenantRequest.TenantId))
        throw new ForbiddenException("Farklı bir organizasyona ait kaynağa erişim engellendi.");
}
```

Yani kullanıcı kendi token'ındaki tenant dışında bir `TenantId` ile istek atarsa 403 alır.
Bu kontrol controller attribute'undan bağımsızdır; use-case seviyesinde garanti verir.

```mermaid theme={null}
flowchart TD
    REQ["Command : IRequireTenantAccess<br/>TenantId = X"] --> PB["AuthorizationPipelineBehavior"]
    PB --> CHK{"token tenant == X ?"}
    CHK -- evet --> H["Handler çalışır"]
    CHK -- hayır --> F["ForbiddenException → 403"]
```

## Neden DB-per-tenant değil

Soft-tenant tercih edildi çünkü:

* **Tek migration, tek bağlantı havuzu** — N tenant için N veritabanı yönetmek operasyonel yük.
* **Müşteri on-prem ortamı** — mevcut tek Postgres instance'ı paylaşılır; dinamik veritabanı
  oluşturma yetkisi her zaman olmayabilir.
* **Cross-tenant raporlama** tek sorguda mümkün (yetki dahilinde).

Karşılığında izolasyon disiplini uygulamaya düşer: tenant'a ait sorgular `organization_id`
filtresini taşımalı ve mutasyonlar `IRequireTenantAccess` ile korunmalıdır.

<Warning>
  Soft-tenant'ta izolasyon "varsayılan olarak açık" değildir — bir sorguda tenant filtresini
  unutmak veri sızıntısıdır. Tenant'a ait yeni bir aggregate eklerken `IMayHaveTenant`/`ITenant`
  implement etmeyi ve ilgili command/query'lerde `IRequireTenantAccess` kullanmayı ihmal etmeyin.
</Warning>

## İlgili

<CardGroup cols={2}>
  <Card title="Authorization" href="/security/authorization">
    Permission ve pipeline behavior.
  </Card>

  <Card title="Realm Yapısı" href="/keycloak/realm-structure">
    organization\_id Protocol Mapper.
  </Card>

  <Card title="Tasarım Kararları" href="/architecture/design-decisions">
    Soft-tenant tercihi ve gerekçeleri.
  </Card>

  <Card title="DbContext" href="/data/dbcontext">
    Query filter ve şema yapısı.
  </Card>
</CardGroup>
