MİMARİ~5 dakika okuma süresi

Veritabanı Mimarisi: Hard Delete, Soft Delete ve Audit Stratejileri

Bir veriyi silmek, sadece bir SQL komutu çalıştırmak değil, sistemin geleceğini etkileyen mimari bir karardır. Bu yazıda, Hard Delete ve Soft Delete kavramlarını, teknik borç yaratmadan nasıl uygulanacağını, ORM tuzaklarını ve modern sistemlerde kullanılan hibrit stratejileri inceliyoruz.

Veritabanı Mimarisi: Hard Delete, Soft Delete ve Audit Stratejileri

Eğer backend dünyasında bir süredir kod yazıyorsanız, şu durumu mutlaka yaşamışsınızdır: Bir "Sil" butonu yaparsınız, arkasına basit bir DELETE sorgusu koyarsınız ve iş biter. Ancak proje büyüyüp veriler karmaşıklaştıkça, o masum silme işlemi başınıza bela olmaya başlar.

Yanlışlıkla silinen bir müşteri kaydı geri getirilebilir mi? Silinen bir kategorinin altındaki ürünler raporda görünecek mi? Peki ya yasal zorunluluklar (KVKK/GDPR) veriyi gerçekten yok etmemizi isterse?

Bir junior geliştirici için silme işlemi "veriyi yok etmekten" ibarettir. Bir sistem mimarı için ise silme işlemi, verinin "durumunu değiştirmek" ve sistemin bütünlüğünü korumaktır. Yanlış kurgulanmış bir silme stratejisi, ileride performans sorunlarına, "unique constraint" hatalarına, N+1 problemlerine ve tutarsız raporlara yol açar.

Bu yazıda, veritabanı mimarisinde kullanılan temel silme stratejilerini, ORM seviyesindeki gizli maliyetleri ve production ortamında karşılaşacağınız sorunlara çözüm üreten pratik desenleri paylaşıyorum.

1. Hard Delete (Fiziksel Silme)

The Terminator Approach

SQL dünyasındaki DELETE FROM table WHERE id = 1 komutunun ta kendisidir. Veri diskten fiziksel olarak kaldırılır ve geri dönüşü (yedekler hariç) yoktur. Genellikle "asla veri silme" mantrasından dolayı korkulan bir yöntem olsa da, doğru yerde kullanıldığında sistemin sağlığı için elzemdir.

Bu yaklaşım şunları sağlar:

  • KVKK ve GDPR Uyumu: Kullanıcı "Unutulma Hakkı"nı kullandığında, veriyi saklamaya devam etmek yasal bir risk oluşturabilir. Hard delete, en temiz çözümdür.
  • Maliyet ve Performans: Milyarlarca satırlık log, session veya geçici cache tablolarını saklamanın bir anlamı yoktur. Bu verileri fiziksel olarak silmek, index boyutunu küçültür ve sorguları hızlandırır.
  • Cascade Riski: Hard Delete kullanırken en büyük risk ON DELETE CASCADE yapısıdır. Bir ana kaydı sildiğinizde, veritabanı arka planda ona bağlı binlerce alt kaydı da silebilir. Bu, veritabanında kilitlenmelere (deadlock) ve sistemin anlık olarak kilitlenmesine yol açabilir. Bu yüzden Hard Delete işlemleri genellikle transaction yönetimi açısından dikkat gerektirir.

2. Soft Delete (Mantıksal Silme)

The Safety Net

Veriyi silmek yerine, tabloya bir deleted_at (timestamp) veya is_deleted (boolean) kolonu ekleyerek işaretleme yöntemidir. Veri fiziksel olarak oradadır ama uygulama katmanı (WHERE deleted_at IS NULL diyerek) onu yok sayar.

Bu yaklaşım şunları sağlar:

  • Hata Toleransı: "Pardon, yanlışlıkla sildim!" diyen bir kullanıcıyı kurtarmak, sadece tek bir UPDATE sorgusuna bakar.
  • İlişkisel Bütünlük (Referential Integrity): Bir siparişi sildiğinizde, o siparişe bağlı fatura veya kargo kayıtlarının "yetim" (orphan) kalmasını engeller. Geçmişe dönük raporlamalarda veri tutarlılığını korur.
  • Sorgu Karmaşıklığı: Soft Delete'in gizli maliyeti sorgulardadır. Yazdığınız her JOIN işleminde silinme durumunu kontrol etmek zorundasınız (users JOIN orders ON ... AND users.deleted_at IS NULL). Bunu unuttuğunuz tek bir sorgu, silinmiş kullanıcıların sisteme sızmasına neden olur.

3. Partial Index (Kısmi İndeks) Stratejisi

Soft Delete'in Kurtarıcısı

Soft Delete kullanırken en sık düşülen tuzak "Unique Constraint" problemidir. Örneğin, bir kullanıcı ([email protected]) hesabını sildi (soft delete). Bir ay sonra aynı mail ile tekrar kaydolmak istediğinde veritabanı "Bu mail zaten var" hatası verir. Çünkü veri silinmemiştir, sadece gizlenmiştir.

Bu strateji şunları sağlar:

  • Benzersizlik Sorununun Çözümü: İndeksi WHERE deleted_at IS NULL koşuluyla oluşturarak, sadece aktif kullanıcıların maillerinin benzersiz olmasını sağlarsınız. Silinmiş kullanıcıların mailleri boşa çıkar.
  • İndeks Performansı: İndeksleriniz sadece aktif kayıtları içerir. 10 milyon silinmiş, 1 milyon aktif kaydınız varsa, indeksiniz sadece 1 milyonluk boyutta olur ve sorgularınız hızlanır.
  • Mimari Esneklik: Yazılım tarafında "kirli" kontroller (checkIfEmailExists) yapmadan, veri bütünlüğünü veritabanı seviyesinde garanti altına alırsınız.

4. Audit Logging & Archive Pattern

The Clean Slate

Eğer Soft Delete kullanma sebebiniz sadece "geçmişi görmek" veya "denetim" (audit) ise, ana operasyonel tablonuzu kirletmek yanlış bir tercihtir. Bu desende, veri ana tablodan gerçekten silinir (Hard Delete), ancak silinmeden hemen önce bir kopyası audit_logs veya users_archive tablosuna taşınır.

Bu yaklaşım şunları sağlar:

  • Maksimum Performans: Ana tablonuz (users, orders) her zaman küçük ve temiz kalır. Sorgularınızda sürekli WHERE is_deleted = 0 yazmak zorunda kalmazsınız.
  • Modern CDC (Change Data Capture) Entegrasyonu: Büyük ölçekli sistemlerde bu işlemi veritabanı trigger'ları ile yapmak yerine Debezium veya Kafka gibi CDC araçlarıyla yapmak daha yaygındır. Veritabanındaki silme işlemi (DELETE log) okunur ve asenkron olarak bir veri ambarına (Data Warehouse) taşınır. Bu sayede ana uygulamanız hiç performans kaybetmez.

5. ORM ve Framework Tuzakları (Hibernate / Entity Framework)

The Hidden Cost

Birçok backend geliştirici, Soft Delete işini framework'lere (Spring Boot, Django, .NET vb.) bırakır. Örneğin Hibernate'te @SQLDelete ve @Where(clause="deleted_at is null") anotasyonları çok popülerdir. Ancak bu kolaycılığın ciddi bedelleri olabilir:

  • Native Query Riski: Eğer projenizde performans için saf SQL (Native Query) yazarsanız, ORM'in otomatik filtrelemesi devre dışı kalır. Silinmiş verileri yanlışlıkla raporlara dahil edebilirsiniz.
  • İlişkisel Yükleme (Lazy Loading): Soft delete yapılmış bir kayda, başka bir entity üzerinden erişmeye çalıştığınızda (örneğin silinmiş bir kullanıcının siparişlerini listelerken), ORM'ler bazen EntityNotFoundException fırlatabilir veya beklenmedik NULL hataları üretebilir. Bu durum, kodun içinde gereksiz try-catch bloklarına yol açar.

6. Hibrit Yaklaşım: Ertelenmiş Silme (Delayed Hard Delete)

The Trash Can Model

Soft Delete ve Hard Delete'in en iyi yönlerini birleştirir. Tıpkı işletim sisteminizdeki "Çöp Kutusu" veya Apple Photos'daki "Son Silinenler" mantığı gibi çalışır. Veri önce Soft Delete ile işaretlenir, ancak belirli bir süre (örneğin 30 gün) sonra bir zamanlanmış görev (Cron Job) tarafından Hard Delete ile tamamen silinir.

Bu yaklaşım şunları sağlar:

  • Kullanıcı Güveni: Kullanıcıya pişman olması için bir zaman penceresi (grace period) tanırsınız.
  • Sürdürülebilirlik: Veritabanınız sonsuza kadar şişmez; kendi kendini temizleyen bir mekanizmaya sahip olur.
  • Yasal ve Operasyonel Denge: Hem veriyi kurtarma şansınız olur hem de uzun vadede "gereksiz veri saklama" riskinden kurtulursunuz. Özellikle GDPR gereği veriyi belli bir süre sonra tamamen yok etmeniz gerektiğinde bu model hayat kurtarır.

Son Söz

Veritabanı tasarımı yaparken "Her tabloya bir is_deleted kolonu ekleyelim, güvenli olsun" demek, genellikle tembel bir mimari yaklaşımdır.

Verinin doğasına göre karar vermelisiniz:

  • Finansal ve Yasal Kayıtlar (Fatura, Sipariş) için Soft Delete + Audit,
  • Geçici ve Önemsiz Veriler (Log, Session, Cache) için Hard Delete,
  • Benzersizlik Gerektiren Hesaplar (Kullanıcılar, E-postalar) için Partial Index destekli çözümler,
  • Kullanıcı İçeriği ve "Geri Alma" İhtiyacı (Notlar, Fotoğraflar, Taslaklar) için Delayed Hard Delete,
  • Büyük veri ve analiz için CDC tabanlı Arşivleme en doğrusudur.

Unutmayın; kodunuzu her gün değiştirebilirsiniz, framework'leri güncelleyebilirsiniz ama veritabanı mimarisi bir binanın temeli gibidir. Yanlış atılan bir temel, bina yükseldikçe düzeltilmesi imkansız çatlaklara yol açar. "DELETE" tuşuna basarken (veya basmazken) bu prensipleri göz önünde bulundurun.

Tüm Mimari yazıları