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.
JwtBearer şeması (Vatandas, Personel) tanımlıdır. Bkz. Keycloak › Realm Yapısı.
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_messagetablosuna 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.
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
CacheInvalidationIntegrationEventile broadcast edilir.
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_idclaim’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.
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→ otomatikModified’a çevrilir,DeletedAt/Byset edilir (fiziksel silme yok).- Tek noktadan, unutulamaz. Bkz. Veri Katmanı › 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.