Skip to main content
Sistemde fiziksel silme yoktur (DELETE SQL’i üretilmez). ISoftDeletable taşıyan her aggregate, silindiğinde DeletedAt/DeletedBy damgalanır ve sorgulardan otomatik gizlenir.

ISoftDeletable

EntityBase bu arayüzü taşır; alanlar ve IsDeleted() kontrolü buradan gelir:
Bir kayıt “silinmiş” sayılır ancak DeletedAt != null ise. DeletedAt == null olan kayıtlar aktiftir.

Silme nasıl gerçekleşir

Repository Remove/DeleteAsync çağrısı entity’yi EntityState.Deleted’a alır; AuditInterceptor bunu yakalayıp soft-delete’e çevirir:
Sonuç: DB’de DELETE yerine UPDATE ... SET deleted_at = now, deleted_by = userId çalışır. Detaylar Audit Interceptor sayfasında.

Global query filter

OnModelCreating, ISoftDeletable uygulayan her entity tipine DeletedAt == null filtresini reflection ile otomatik ekler:
Böylece her sorgu (ve include edilen navigation) otomatik olarak WHERE deleted_at IS NULL koşulunu taşır. Ardalis.Specification EF Core’un altındaki IQueryable’ı kullandığı için spec tabanlı sorgularda da filtre aktif kalır.
Pratik sonuç: provisioning, lookup ve uniqueness check akışları soft-deleted kayıtları görmez. Örneğin iptal edilmiş bir üyelik yeniden login olduğunda eski (silinmiş) kayıt görünmediği için yeni bir Citizen oluşturulur.

Silinmişleri görme — IgnoreQueryFilters

Audit raporlama gibi senaryolarda soft-deleted kayıtları dahil etmek için sorgu açıkça filtreyi devre dışı bırakır:

Restore — geri alma

Soft-delete’i geri almak DeletedAt/DeletedBy’ı null yapmaktır. RolePermission bunu domain metoduyla yapar:
Restore edilecek (silinmiş) kaydı bulmak için önce IgnoreQueryFilters() ile sorgulanır, sonra Restore() çağrılır.

Unique index sorunu ve partial index çözümü

Soft-delete, naif unique constraint’leri bozar: aynı (role_id, permission_id) çifti bir kez silinip yeniden eklenmek istendiğinde, silinmiş satır hâlâ tabloda durduğu için unique ihlali oluşur. Çözüm, partial unique index’tir — yalnızca aktif (silinmemiş) satırlar için benzersizlik zorlanır:
PostgreSQL’de bu CREATE UNIQUE INDEX ... WHERE deleted_at IS NULL üretir. Böylece:
  • Aktif kayıtlar arasında çift benzersizdir.
  • Soft-deleted kayıtlar index dışında kalır → aynı çift yeniden eklenebilir veya Restore() ile geri alınabilir.
Soft-delete’i olan bir tabloda unique kısıt tanımlarken HasFilter("deleted_at IS NULL") eklemeyi unutmayın; yoksa “sil-ve-yeniden-ekle” senaryosu duplicate hatası verir. RolePermission’ın Guid PK’ye geçmesinin (eski composite PK yerine) nedeni de tam olarak budur — bkz RolePermissionsRevamp migration’ı.

İlgili

Audit Interceptor

Deleted → Modified dönüşümünün kaynağı.

DbContext & Repository

Global query filter’ın OnModelCreating’de kurulması.

Migrations

Partial unique index’in migration’larda üretimi.

Data Genel Bakış

Katman rolü ve şemalar.