Skip to main content
Önce Domain: aggregate metodunu (gerekirse yeni value object / domain event) yazın. Sonra Application: Command/Query + Validator + Handler. Ardından API: ince bir controller action (_mediator.Send). Cache/e-posta/SMS gerekiyorsa bir INotificationHandler<TDomainEvent> ekleyin.Adım adım reçete: Playbook’lar → Command’ı uçtan uca ekle ve Application → Command’lar ve Query’ler.
Çünkü domain event’ler yalnızca IUnitOfWork.SaveEntitiesAsync üzerinden dispatch edilir — SaveEntitiesAsync, SaveChanges’ten sonra biriken event’leri MediatR’a yollar (DispatchDomainEventsAsync). Düz DbContext.SaveChangesAsync bunu atlar, event’ler hiç çalışmaz. Bu yüzden EFRepository.SaveChangesAsync bilerek exception fırlatır.Doğru kullanım her zaman: await _unitOfWork.SaveEntitiesAsync(ct);. Detay: Domain Event’ler.
Birkaç olası neden:
  • Aggregate metodu ilgili domain event’i yaymıyor (AddDomainEvent eksik).
  • O event için bir invalidation handler yok (örn. InvalidateFaqCacheDomainEventHandler).
  • Handler farklı bir tag düşürüyor; spec’teki WithTags(...) ile handler’daki RemoveByTagAsync(...) aynı string olmalı (bu yüzden CacheTags sabitleri kullanılır).
  • Çoklu-instance’ta broadcast kapalı: Cache:BroadcastTagInvalidation ve Redis kayıt sırası (AddRedisCache → AddCache → AddHybridCache) doğru olmalı; aksi halde L1-only kalır.
Reçete: Cache Invalidation playbook’u.
Token doğrulama JWKS metadata’sına erişemiyordur. Kontrol listesi:
  • Keycloak ayakta mı (dev 8080)?
  • Keycloak config’indeki authority/realm URL’i API’den erişilebilir mi (container ağı vs. host)?
  • Realm gerçekten kurulu mu? İlk kurulumda KeycloakProvisioning realm/client/user/mapper’ı RunOnce ile oluşturur.
  • JWKS cache 60 dk; yeni kurduysanız anahtar henüz çekilmemiş olabilir.
Daha fazlası: Keycloak → Sorun Giderme.
dotnet ef migrations add <Ad> \
  -p src/DiyanetCleanArchitecture.Infrastructure.EFCore \
  -s src/DiyanetCleanArchitecture.API
-p projesi DbContext’i (Infrastructure.EFCore), -s startup projesi (API). “No DbContext found” alıyorsanız genelde bu iki parametre eksiktir. Şemalar: public ve messaging (MassTransit outbox/inbox). Detay: Veri → Migration’lar.
Şablon dotnet new template’i olarak paketlidir:
dotnet new install .
dotnet new diyanet-ca -n MyProject
sourceName DiyanetCleanArchitecture olduğundan tüm namespace’ler MyProject ile yeniden adlandırılır. Semboller: Framework (net10.0), ProjectKeycloakId (kebab-case, varsayılan my-project), SkipRestore. Detay: Başlarken → Template Kullanımı.
NetGSM yapılandırması Services:Sms altındadır. Dev’de gerçek SMS göndermek istemiyorsanız Phones[] test numaralarını kullanın. Kontrol:
  • Username / Password / MessageHeader dolu mu?
  • Numara geçerli mobil formatta mı? Gönderim Phone.ToNetGsmFormat() ile yapılır.
  • Kayıt başarılı olduysa SMS bir kez gönderilmiştir; gelmezse yeniden kayıt değil, resend-otp çağrılır (60 sn cooldown, max 3 — bkz. OTP playbook’u).
  • SmsServiceException → API 503 döner; log’larda (Seq) gateway hatasını arayın.
Detay: SMS Servisi.
Yönetim (personel) realm’inde access token süresi bilerek 5 dakikadır — yetki değişikliklerinin (rol/permission) hızlı yansıması ve sızan token’ın kısa ömürlü olması için. keycloak-js sessiz refresh ile token’ı yeniler; refresh anında IUserContextProvider.InvalidateAsync(sub) permission cache’ini düşürür. Vatandaş realm’inde token 2 saattir. Detay: Personel Girişi playbook’u.
Hayır. Domain hiçbir altyapıya (EF Core, DbContext) bağımlı olmaz; yalnızca IRepository<T> / IReadRepository<T> / IUnitOfWork soyutlamalarını bilir. DbContext bağımlılığı Application/Infrastructure katmanlarında kalır. Aggregate içinde sorgu gerekiyorsa bunu bir domain service’e (örn. UserRegistrationService, repository alır) veya Application handler’a taşıyın. Detay: Mimari → Bağımlılık Akışı.
  • Stage (docker-compose.stage.yml / .stage.local.yml): nginx-proxy + path-based routing; .stage.local.yml HTTP-only yereldir, .stage.yml Let’s Encrypt ile TLS.
  • Prod (docker-compose.prod.yml): nginx-proxy + acme-companion ile otomatik TLS. Hetzner çoklu-proje için .platform.yml + .shared-infra.yml + .app.yml.
  • Müşteri on-prem’de Let’s Encrypt çalışmaz (intranet/kurumsal cert) ve mevcut Postgres kullanılır. Detay: Operasyon → Docker Prod.
Bir node tag’i düşürdüğünde (RemoveByTagAsync) L1+L2 temizlenir ve bir CacheInvalidationIntegrationEvent RabbitMQ’ya yayılır. Diğer node’lar bunu tüketip yalnızca kendi L1’lerini temizler (RemoveByTagLocallyAsync — tekrar broadcast etmez). Kendi yayınını yeniden işlememek için SourceNodeId == NodeId olan event’ler atlanır (echo guard). L2 (Redis) ortak olduğundan zaten paylaşımlıdır; broadcast yalnızca L1’leri senkron tutar. Detay: Çoklu-instance cache.

İlgili

Sözlük

Terimlerin Türkçe açıklamaları.

Playbook'lar

Uçtan uca senaryolar.