> ## 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.

# Tasarım Kararları

> Önemli mimari kararlar ve gerekçeleri (ADR özeti)

Bu sayfa şablondaki kritik mimari kararları ve **neden** öyle tercih edildiğini özetler.

## Neden çift-realm Keycloak?

Vatandaş ve personel kimlikleri **ayrı Keycloak realm'lerinde** tutulur (`diyanet-vatandas-*` ve `diyanet-yonetim-*`).

* **Güvenlik izolasyonu:** Vatandaş token'ı asla yönetim paneline erişemez; iki ayrı imza/issuer.
* **Farklı politika:** Personel token ömrü kısa (5 dk), vatandaş uzun (2 saat). Refresh rotation ayrı.
* **Alternatif (tek realm + client ayrımı):** Rol/scope sızıntısı riski ve karmaşık client mapper'ları gerektirir.

API tarafında iki ayrı `JwtBearer` şeması (`Vatandas`, `Personel`) tanımlıdır. Bkz. [Keycloak › Realm Yapısı](/keycloak/realm-structure).

## Neden Outbox pattern?

`SaveChanges` (PostgreSQL) ile RabbitMQ publish **aynı atomik transaction'da olamaz** (dual-write problemi). Çözüm: MassTransit EF Core Outbox.

* Integration event, iş verisiyle **aynı transaction'da** `messaging.outbox_message` tablosuna yazılır.
* MassTransit'in delivery servisi mesajı asenkron RabbitMQ'ya iletir.
* Ya ikisi de commit olur ya ikisi de rollback → veri-mesaj tutarlılığı garanti.

Bkz. [Event-Driven › Outbox Pattern](/events/outbox-pattern).

## Neden HybridCache (L1 + L2)?

Sadece Redis (L2) her okumada ağ gidiş-dönüşü demektir. HybridCache, in-process L1 (memory) + Redis L2 birleşimi sunar.

* **L1:** µs seviyesinde, instance-local (TTL 1 dk).
* **L2:** Redis, instance'lar arası paylaşılan (TTL 5 dk).
* **Stampede koruması:** Aynı anahtar için eşzamanlı çağrılar tek factory'de birleşir.
* **Çoklu instance:** L1 tutarlılığı için tag invalidation `CacheInvalidationIntegrationEvent` ile broadcast edilir.

Bkz. [Cache Mimarisi › HybridCache](/caching/hybrid-cache).

## Neden soft-tenant (JWT claim + query filter)?

Tenant izolasyonu DB-per-tenant veya schema-per-tenant yerine **tek şema + `organization_id`** ile yapılır.

* Tenant kimliği JWT'de `organization_id` / `tenant_id` claim'i olarak taşınır.
* Operasyonel basitlik: tek veritabanı, tek migration seti.
* **Alternatif (DB/schema-per-tenant):** Yüzlerce kurum için migration ve bağlantı yönetimi maliyeti yüksektir.

Bkz. [Kimlik ve Yetki › Multi-Tenancy](/security/multi-tenancy).

## Neden MediatR?

Tek process içinde request/response (Command/Query) ve notification (domain event) için temiz bir pipeline sağlar. Cross-cutting concern'ler (exception, authorization, validation, permission) **pipeline behavior** olarak handler'lara dokunmadan eklenir.

## Neden MassTransit (kendi RabbitMQ yazımı yerine)?

`BuildingBlocks.EventBus.EventBusRabbitMQ` (elle yazım) repoda hâlâ duruyor ama **MassTransit** aktif. MassTransit: EF Outbox, delayed redelivery, retry, DLX, health check ve consumer yaşam döngüsünü hazır verir.

## Neden iki ayrı React SPA?

Admin ve Website tek SPA yerine **ayrı uygulamalardır**.

* Farklı realm/token, farklı rol seti, farklı build (`VITE_BASE_PATH=/admin/` vs `/`).
* Bağımsız deploy ve daha küçük bundle.
* nginx-proxy path-based yönlendirme ile tek domain altında birleşir.

## Neden EF Core SaveChanges interceptor (audit için)?

Audit (`CreatedBy/UpdatedBy/DeletedBy`) ve soft-delete, her handler'da elle yazmak yerine tek **`AuditInterceptor`** ile merkezi olarak uygulanır.

* `Added` → CreatedAt/By, `Modified` → UpdatedAt/By.
* `Deleted` → otomatik `Modified`'a çevrilir, `DeletedAt/By` set edilir (fiziksel silme yok).
* Tek noktadan, unutulamaz. Bkz. [Veri Katmanı › Audit Interceptor](/data/audit-interceptor).

## Neden Guid v7 (GuidFactory)?

`GuidFactory.New()` → `Guid.CreateVersion7()`. Zaman-sıralı UUID'ler, rastgele v4'e göre PostgreSQL index'lerinde **daha iyi yerellik** (locality) ve daha az sayfa parçalanması sağlar.

## Neden Domain Event + Integration Event ayrımı?

* **Domain Event:** Aynı transaction içinde, in-process (MediatR). Aggregate tutarlılığını korur.
* **Integration Event:** Transaction commit sonrası, RabbitMQ üzerinden asenkron. Yan etkiler (SMS, e-posta, başka servis) için.

Bkz. [Event-Driven › Genel Bakış](/events/overview).
