Skip to main content
DiyanetCleanArchitectureDbContext data katmanının merkezidir. Hem klasik DbContext’tir hem de IUnitOfWork arayüzünü uygular — yani transaction sınırı ve domain event dispatch noktası buradadır.

DbSet’ler

Tüm aggregate root’lar (ve bazı child entity’ler) DbSet olarak tanımlıdır:
Citizen (vatandaş portalı) ile User (personel/admin) ayrı aggregate’lerdir ve ayrı tablolara yazılır. Aynı kişi iki tarafta da bağımsız kayıt tutar.

OnModelCreating

Üç iş yapar: configuration’ları toplar, MassTransit messaging tablolarını ekler, global soft-delete filtresini kurar.
Global filtre her ISoftDeletable için DeletedAt == null koşulu ekler — detaylar Soft Delete sayfasında.

UnitOfWork: SaveEntitiesAsync

SaveEntitiesAsync business akışının kullandığı kayıt metodudur. SaveChanges çağırır, ardından değişen aggregate’lerin domain event’lerini dispatch eder:
DispatchDomainEventsAsync (SeedWork/MediatorExtension.cs) change tracker’daki her Entity’nin event’lerini toplar, temizler ve tek tek yayar. IntegrationEvent’ler IEventBus (MassTransit Outbox), kalan domain event’ler MediatR ile in-memory işlenir:
Bir event handler’ı patlarsa akış kesilmez (catch/continue) ama hata DbContext logger’ına yazılır. Eski versiyonda catch sessizdi ve hatalar görünmüyordu.
DbContext ayrıca manuel transaction desteği sunar: BeginTransactionAsync (ReadCommitted), CommitTransactionAsync, RollbackTransaction. Çoğu komut tek SaveEntitiesAsync ile yetinir; çapraz-aggregate atomikliği gereken yerlerde açık transaction kullanılır.

EntityTypeConfiguration örnekleri

Value object’leri kolona çevirme — HasConversion

User.Email, User.Phone, User.FullName value object’tir; tek kolona serialize edilir:

Owned type — OwnsOne

TOTP konfigürasyonu nested owned type’tır (aynı users satırına kolon olarak yazılır):
Citizen.ProfileImage ise owned type olmasına rağmen ayrı tabloya (citizen_profile_images) yazılır — bytea payload’ı liste sorgularını şişirmesin diye AsSplitQuery ile lazy yüklenir:

DB-generated referans numarası — IDENTITY kolon

User.ReferenceNumber PostgreSQL IDENTITY kolonudur (insan-okuyabilir, artan numara), 100’den başlar ve insert sonrası salt-okunur:

Optimistic concurrency

users tablosu shadow row_version kolonuyla optimistic concurrency control yapar:

Repository’ler

İki ayrı path var: write (IRepository<T>) ve read (IReadRepository<T>). İkisi de Ardalis.Specification tabanlıdır.

EFRepository — write tarafı

EFRepository.SaveChangesAsync bilerek exception fırlatır. Kayıt repository üzerinden değil, UnitOfWork.SaveEntitiesAsync (yani DbContext) üzerinden yapılır — böylece domain event dispatch atlanmaz. Repository’ler sadece change tracker’ı düzenler (AddAsync, UpdateAsync, DeleteAsync).
UnitOfWork property’si _context’in ta kendisidir; handler _repository.UnitOfWork.SaveEntitiesAsync(...) ile kaydeder.

CachedRepository — read tarafı (HybridCache decorator)

IReadRepository<T> implementasyonu, gerçek sorguyu EFCacheRepository<T>’ye delege eder ve spec cache-enabled ise HybridCache (L1 memory + L2 Redis) ile sarmalar:
Cache yalnızca iki şart birden sağlanınca devreye girer: global Cache:Enabled açık ve spec’te EnableCache(...) zinciri kurulmuş olmalı:
TTL ve tag’ler spec’ten okunur; TTL belirtilmemişse config’teki Cache:L2:Ttl kullanılır. Handler/query cache’ten habersizdir — cache tamamen decorator’ın işidir.

DI kaydı

AddInfrastructureEFCore (DependencyInjection.cs) tüm bağımlılıkları kurar:
Connection string adı sabittir: DiyanetCleanArchitectureDatabaseConnection. Aynı isim DesignTimeDbContextFactoryBase tarafından da kullanılır (migration araçları için).

İlgili

Audit Interceptor

AuditInterceptor’ın SaveChanges anında nasıl çalıştığı.

Soft Delete

Global query filter ve restore senaryoları.

Migrations

Bu DbContext’ten migration üretme ve uygulama.

Data Genel Bakış

Katmanın rolü, şemalar, klasör yapısı.