Repository Pattern Nedir? Neden Önemli?
Repository Pattern, iş katmanını (business logic) veri erişim detaylarından soyutlayan bir tasarım desenidir. Böylece:
- Bağımlılık azalır: Servisleriniz doğrudan
DbContextveya ADO.NET’e değil, bir arayüze (IRepository<T>) bağlı kalır. - Test edilebilirlik artar: Mock nesneleri aracılığıyla veri katmanını izole ederek birim testler yazabilirsiniz.
- Kod tekrarı azalır: CRUD işlemlerini tek bir yerde toplar, projeler arası yeniden kullanım sağlar.
Çok katmanlı projelerde, karmaşık query’ler ya da farklı veri kaynakları gerektiğinde Repository Pattern, kodun sürdürülebilirliğini ve genişletilebilirliğini büyük ölçüde iyileştirir.
Temel Kavramlar
- IRepository<T> arayüzü CRUD metotlarını tanımlar.
- Somut sınıf (EfRepository<T>) bu arayüzü EF Core veya başka bir teknolojiyle uygular.
- Unit of Work birden fazla repository işlemini tek bir transaction altında toplar.
Uyarı: Küçük, basit CRUD senaryolarında gereksiz katman eklemekten kaçının; doğrudan
DbContextkullanmak daha pratiktir.
Avantajlar ve Dezavantajlar
Avantajlar
- Tek Sorumluluk (SRP): Veri erişim kodu, iş mantığından ayrılır.
- Test Edilebilirlik: Repository arayüzünü mock’layarak izole testler yazabilirsiniz.
- Genişletilebilirlik (OCP): Yeni bir veri kaynağı eklemek için sadece yeni bir sınıf implementasyonu yeterlidir.
- Tekrarlanabilirlik: Farklı projelerde aynı repository’yi rahatça kullanabilirsiniz.
Dezavantajlar
- Boilerplate Kod: CRUD metodlarının tekrarı bazı projelerde gereksiz yük oluşturabilir.
- Aşırı Soyutlama: Basit uygulamalarda karmaşıklığı artırabilir.
- Async Yönetimi:
SaveChangesAsync()metodunu servis katmanında mı, yoksa repository içinde mi çağıracağınızı netleştirmek gerekiyor.
İpucu:
SaveChangesAsync()’i servis katmanında tutmak, birden çok repository çağrısını tek transaction’da toplamanıza yardımcı olur.
ASP.NET Core Örnek Uygulama
1. Arayüz Tanımı
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
void Update(T entity);
void Delete(T entity);
}
2. EF Core Somut Sınıf
public class EfRepository<T> : IRepository<T> where T : class
{
protected readonly AppDbContext _context;
public EfRepository(AppDbContext context)
{
_context = context;
}
public async Task<T> GetByIdAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}
public async Task<IEnumerable<T>> GetAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
public async Task AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
}
public void Update(T entity)
{
_context.Set<T>().Update(entity);
}
public void Delete(T entity)
{
_context.Set<T>().Remove(entity);
}
}
3. Unit of Work Entegrasyonu
public interface IUnitOfWork
{
IRepository<Product> Products { get; }
IRepository<Order> Orders { get; }
Task<int> CommitAsync();
}
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()
{
return await _context.SaveChangesAsync();
}
}
Not: UnitOfWork.CommitAsync() tüm değişiklikleri tek bir transaction altında commit eder; bu, veri tutarlılığı için önemlidir.
Servis Katmanına Entegrasyon
public class ProductService
{
private readonly IRepository<Product> _repository;
private readonly IUnitOfWork _unitOfWork;
public ProductService(IRepository<Product> repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
public async Task<Product> GetProductByIdAsync(int id)
{
return await _repository.GetByIdAsync(id);
}
public async Task AddProductAsync(Product product)
{
await _repository.AddAsync(product);
await _unitOfWork.CommitAsync();
}
}
- Servis katmanı sadece iş kurallarını yönetir; veri kaydetme detayı tamamen
IUnitOfWorkaracılığıyla yapılır. - Hata durumlarında her iki katmanda da uygun loglama yaparak hatayı hızlıca tespit edebilirsiniz.
Gerçek Projelerde Dikkat Edilmesi Gerekenler
- Yaşam Döngüsü (Lifetime):
DbContextve repository’leriniziScopedolarak yapılandırın; Singleton repository,DbContextsızıntılarına neden olur. - Performans İzleme: Sık sorgu yapan metotları Application Insights veya EF Core logging ile izleyin; N+1 sorgu problemlerine dikkat edin.
- Metot Granülaritesi: Gereksiz büyük veri çeken
GetAllAsyncyerine, filtreli metotlar (GetByCategoryAsync) oluşturun. - Transaction Yönetimi:
UnitOfWorkveyaTransactionScopekullanırkenasync/awaituyumluluğunu göz önünde bulundurun.
İleri Okuma ve Kaynaklar
Sonuç
Repository Pattern, karmaşık ve büyüyen projelerde sürdürülebilirlik, test edilebilirlik ve kolay genişletilebilirlik sağlar. Küçük, basit CRUD uygulamalarında ise doğrudan DbContext kullanımı daha hızlı ve basit bir yaklaşımdır. Projenizin ölçeğine ve ihtiyaçlarına göre doğru dengeyi kurarak bu deseni verimli şekilde kullanabilirsiniz.