Skip to main content
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ı.

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.

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.

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.

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.

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ış.