Repository ve Unit Of Work Tasarım Kalıpları

Engin UNAL
Bilişim Hareketi
Published in
4 min readJan 31, 2018

Bu yazıda Repository design pattern konusunda temel bilgi vermeye çalışacağım, devamında Unit Of Work pattern konusunda kısa bir özet ve kod örneği ile yazıyı tamamlamış olacağım.

Öncelikle DbContext nedir? Bu noktadan başlayalım.

DBContext

Veritabanına karşılık gelen obje yapısıdır. İçinde tablo yapısında karşılık gelen DbSet objelerini bulundurur.

DbContext kullanarak tablo ve view yapılarına erişebilir, DbSet yapısını kullanarak tablo üzerinde CRUD işlemlerini gerçekleştirebilirsiniz.

Repository Design Pattern

Repository temel olarak veritabanı sorgulama işlemlerinin bir merkezden yapılmasını sağlayarak iş katmanına taşınmasını önler ve bu şekilde sorgu ve kod tekrarına engel olmuş olur. Yani asıl amaç veri işlem ve sorgulamaların tekrarlardan kaçınılarak merkezi bir yapıya çekilmesidir. Bu sayede veritabanı işlemlerimizi tekrar ve tekrar iş katmanı içinde yazmak durumununda kalmamış oluruz.

Repository tasarım kalıbının ilk ve en önemli amacı budur, bunun yanında yukarıdaki tanıma ek olarak repository tasarım kalıbı, programınızda asıl işi yapan bölümler ile veriye erişen bölümlerin birbirinden soyutlanması mantığını da getirmiştir.

Yani veri katmanı ve bu katmanı kullanan iş katmanı arasında bir arabirim olarak yer alır ve bu iki katman arasında soyutlama görevi de üstlenir.

Repository tasarım kalıbı ile ilgili yazılan her yazıda olduğu gibi Martin Fowler’den özlü sözler köşesi olmazsa olmazdır. Martin Fowler repository tanımı için der ki:

“Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.”

“Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.”

Bir çok yazıda paylaşılan ve bence konuyu çok iyi özetleyen aşağıdaki repository şemasını da eklemeliyim.

microsoft asp.net konu anlatımı kısmından alınmıştır.

Şema, repository kullanımının ve araya katmanlar koymanın bize nasıl modüler bir yapı sunduğunu ortaya koymaktadır. En soldaki kısım repository kullanılmadığı durumlarda doğrudan veritabanına DbContext aracılığı ile erişilmesini göstermekte. Sağdaki kısımda ise Unit Of Work pattern kullanılarak katmanlarla ayrılan EF ve Controller bölümlerinin arasına istenildiği anda gerçek repository değil bir moc repository yerleştirme sayesinde test edilebilirliğin kolaylaşması sağlanmaktadır.

Repository dediğimiz kalıp nedir? Neler içermelidir?
Repository tanım gereği objeler içeren bir collection yapısındadır. Bu objeler memory’de tutulur ve objelere erişmek için collection nesnesinde kullandığımız bazı ortak metodları kullanırız. Bu metodlar add, remove, get, getAll, find olarak örneklendirilebilir. Dikkat ederseniz metodlar arasında save veya update bulunmamakta. Bunun nedeni ise repository bir obje koleksiyonu olduğundan görevi veritabanını güncellemek veya ona birşey eklemek değildir.

Temel olarak repository katmanı ile elde ettiğimiz obje koleksiyonu üzerinde işlemlerimizi gerçekleştirir, eğer kalıcı bir saklama alanına(veritabanı, dosya, vs.) bu objelerdeki değişikliklerin aktarılması gerekiyorsa bunu da unit of work tasarım kalıbı ile sağlarız.

Bu aşamada Unit Of Work tasarım kalıbı nedir? İnceleyelim.

Unit Of Work Design Pattern

Bir transaction tarafından etkilenen nesnelerin listesini tutar ve değişikliklerin yazılması ile eşzamanlılık sorunlarının çözümünü koordine eder.

Martin Fowler der ki:

“Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.”

“A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you’re done, it figures out everything that needs to be done to alter the database as a result of your work.”

Kodlama ve Pratik

Teorik bilgiler sanırım bu kadar yeterli. Kod örneklerine geçip yukarıda anlattığım konuların pratikteki karşılıklarını inceleyelim. Öncelikle basit bir model kodlayalım.

public class Kisi
{
public int Id { get; set; }
public string Ad { get; set; }
public string Soyad { get; set; }
}

Kişi modelimiz için DbContext hazırlayalım.

Örnek model ve DbContext yapımız hazır. Artık repository kodlamasına geçebiliriz. Ortak kullanılabilecek yapı olan Generic Repository Interface kodlamasını yapalım.

Şimdi bu interface implementasyonunu gerçekleştirelim.

Görüldüğü üzere, repository içerisinde save veya update metodları bulunmuyor. Tasarımın kurallarına tam olarak uyulduğunda bu şekilde kodlamak önerilmektedir.

Bunun yanında dikkat çeken başka önemli nokta ise entity listesini IQueryable değil IEnumerable olarak döndürmekteyiz. Bunun nedeni IQueryable kullanmanın repository design pattern tasarımına aykırı olmasıdır. IQueryable kullanırsak, repository’i kullanan üst katmanların interface’e sorgular üretip göndermesine imkan tanımış oluruz ve tasarımda delik anlamına gelir. Sorgulama işlemleri repository içinde yapılmalıdır.

Yukarıda tanımını yaptığımız generic repository’den kişileri çekebileceğimiz yapı üretmek üzere KişilerRepository türetelim. Öncelikle IRepositoryKisiler interface tanımlamasını yapalım. Interface içine örnek olsun diye sadece kişi listesini alabileceğim bir metod ekleyeceğim.

public interface IKisilerRepository : IRepository<Kisi>
{
IEnumerable<Kisi> KisileriGetir();
}

Görüldüğü gibi IKisilerRepository interface’i IRepository’den (Generic Repository) türemekte ve Kişi tipini almaktadır. İmplementasyonu gerçekleştirelim.

KisilerRepository, Repository’den türemekte ve IKisilerRepository interface’ini implemente etmektedir. Repository’lerimizi tanımladık, devamında persistence yani veri kayıt işlemlerini gerçekleştirken kullanacağımız tasarım kalıbı olan Unit of Work yapısını oluşturalım.

Öncelikle IUnitOfWork interface tanımlamasını yapalım.

public interface IUnitOfWork : IDisposable
{
IKisilerRepository Kisiler { get; }
int Complete();
}

IUnitOfWork, Kisiler repository’i dışa aktarır. Bunun getirdiği fayda şudur; Test süreçlerinde Mock Interface ile yer değiştirip gerçek repository’ler yerine mock repository’leri kullanmamıza imkan tanır ve bu sayede test yazma süreçlerine katkı sağlamaktadır. UnitofWork implementasyonu aşağıdaki gibidir.

Tüm interface ve class tanımlamalarımızı yaptık. Artık kurduğumuz yapıyı kullanabiliriz. Bunun için kısa bir örnek:

Detayda kaybolmadan basit örnek ve kısa anlatımla konuya dair bir bakış açısı vermeye çalıştım. Umarım amacıma ulaşmışımdır. Okuduğunuz için teşekkürler.

Engin ÜNAL

--

--