Unit of Work ile Transaction Yönetimi
Birden fazla repository üzerinde eşzamanlı değişiklik yaparken, tutarlılığı sağlamak kritik önem taşır. Unit of Work (UoW) deseni, bir dizi veri erişim işlemini tek bir transaction altında toplar. Böylece:
- Atomicity: Tüm işlemler ya hep başarılı olur ya da hiçbir değişiklik veritabanına yansımaz.
- Tek Commit Noktası:
SaveChanges()veyaSaveChangesAsync()yalnızca bir kez çağrılır. - Temiz Kod: Servis katmanında transaction detayları yerine iş mantığına odaklanırsınız.
Tanım ve Prensipler
Unit of Work deseni, farklı repository’lerden gelen ekleme, güncelleme veya silme işlemlerini bir araya getirir ve bunları tek bir transaction içinde commit eder.
Başlıca sorumlulukları:
- Değişiklikleri takip etmek
- Tüm işlemleri tek seferde commit etmek
- Hata durumunda rollback sağlamak
Avantajlar & Dezavantajlar
Avantajlar
- Transaction Tutarlılığı: Birden fazla repository çağrısı tek transaction’da toplanır.
- Performans: Sadece bir
SaveChanges()çağrısı, gereksiz tekrar commit’leri önler. - SRP Uyumu: Repository’ler sadece CRUD odaklı kalır; transaction yönetimi UoW’da toplanır.
Dezavantajlar
- Ekstra Katman: Küçük projelerde basit CRUD işlemleri için gereksiz soyutlama olabilir.
- Complexity: Transaction kapsamını geniş tutmak bazen performansı olumsuz etkileyebilir.
Pratik Not: Çok kısa ve basit bir ekleme/güncelleme işlemi için doğrudan repository üzerinden
SaveChanges()çağırmak, küçük ve tek seferlik senaryolarda daha hızlı ve anlaşılır olabilir.
ASP.NET Core Örnek Uygulama
1. IUnitOfWork Arayüzü
public interface IUnitOfWork : IDisposable
{
IRepository<Product> Products { get; }
IRepository<Order> Orders { get; }
Task<int> CommitAsync();
}
2. UnitOfWork Sınıfı
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext _context;
public UnitOfWork(AppDbContext context)
{
_context = context;
Products = new EfRepository<Product>(_context);
Orders = new EfRepository<Order>(_context);
}
public IRepository<Product> Products { get; }
public IRepository<Order> Orders { get; }
public async Task<int> CommitAsync()
{
// Tüm değişiklikleri tek transaction içinde commit et
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
3. Servis Katmanında Kullanımı
public class OrderService
{
private readonly IUnitOfWork _unitOfWork;
public OrderService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task PlaceOrderAsync(Order order)
{
// Yeni siparişi ekle
await _unitOfWork.Orders.AddAsync(order);
// Stok güncelle
var product = await _unitOfWork.Products.GetByIdAsync(order.ProductId);
product.Stock -= order.Quantity;
_unitOfWork.Products.Update(product);
// İşlemleri commit et
await _unitOfWork.CommitAsync();
}
}
Not:
CommitAsync()çağrısı yalnızca bir kez yapıldığı için, hem ekleme hem de güncelleme tek transaction’da gerçekleşir. Birinde hata olursa tüm işlem rollback olur.
Gerçek Projelerde Dikkat Edilmesi Gerekenler
- Transaction Süresi: Uzun süren işlemler transaction’ı kilitleyebilir. Büyük batch işlemler için chunking stratejisi kullanın.
- Concurrency Kontrolü:
RowVersionveya optimistik kilitleme ile eş zamanlı güncellemelerde çakışmayı yönetin. - Logging: Hangi transaction’da hangi repository çağrılarının yapıldığını kaydedin; debug sürecini kolaylaştırır.
- Hatayla Karşılaşma:
CommitAsync()sırasında exception fırlarsa, işlem otomatik rollback olur; üst katmanda kullanıcıya anlamlı hata mesajı döndürün.
İleri Okuma ve Kaynaklar
Sonuç
Unit of Work deseni, özellikle çoklu repository çağrılarında veri tutarlılığını garanti altına alır.
Küçük, tek seferlik CRUD işlemlerinde ekstra katman getirmemek için pragmatik bir yaklaşım benimseyin. Projenizin ihtiyaçlarına göre UoW kullanımını dengeli tutarak kodunuzun hem temiz hem de güvenilir olmasını sağlayabilirsiniz.