Veri merkezli yazılımların iş katmanlarından veriye ulaşım işlemleri sırasında meydana gelen ve gözardı edilen bazı ayrıntılar, yazılımın ilerki aşamalarında önümüze bir çığ misali yığılıp kalmaktadır. Özellikle katmanlı mimaride geliştirilen yazılımlarda iş kuralları ve katmanlar, düzgün oluşturulmadığı taktirde bir işi N defa yapmak zorunda kalabiliriz. Ancak nesneye yönelik (object oriented) yapıların ilkelerinden biri de, bir kuralı bir kez belirleyip N kez çalıştırmaktır. Örneğin veritabanındaki öğrenciler tablosuna öğrenci eklemeyi yapan bir nesne ve bir metod olmalıdır.
İş katmanında dikkat edilmesi gereken bazı kurallar vardır. Bunlar:
- Kod tekrarlarından kaçınmak
- Hata yakalamayı kolaylaştırmak
- Kod yazımını ve okunuşunu kolaylaştırmak
- Test edilebilir bir yapı kurgulamak
Repository pattern de bizi bu kurallara uymaya zorlamaktadır. Peki repository pattern nedir?
Veri merkezli uygulamalarda veriye erişimin ve yönetimin tek noktaya indirilmesini sağlayan bir tasarım desenidir. Veri merkezli uygulama olması şart değil elbette. Ama bu yazıdaki uygulama veriye erişim kurallarını belirleyen bir örnek üzerinden yürütülecektir.
Örneğimiz, bir ASP.NET MVC web uygulaması içerisinden alınmış repository uygulanışını göstermektedir. Verileri, tek noktadan taşıma işlemini yapacak olan repository yapısı da Entity Framework altyapısını kullanmaktadır. Repository pattern için kullanılan teknoloji çokta önemli değildir. MVC veya Entity Framewok gibi detaylara bu örnekte gerek yoktur, yeri gelmişken bahsetmeden geçemedim. İstersek kendi veri katmanımızı (data layer) hazırlayarak da yola devam edebiliriz.
ASP.NET MVC mimarisinin tanıtımı konusunda en etkili örnek olan açık kaynak kodlu Nerddinner uygulaması üzerinden işlemlerimizi yürütmeye çalışacağız. Neden hazır bir uygulama üzerinden gittim? Kendim de bir sınıf hazırlayıp burada kullanabilirdim pek tabi. Ancak, Nerddinner uygulamasını merak edip bakmanızı da istedim, çok faydalı olacağından eminim.
Nerddinner nedir? Hikaye olarak, insanların yemek yiyebilecek yerleri internet üzerinden kaydedebilecekleri veya daha önceden, başkaları tarafından kaydedilen yemek yenilebilecek yerleri görebilmelerini sağlamak şeklinde özetlenebilir. Bu kısmı da yeri gelmişken kısaca özetledikten sonra konumuza dönebiliriz.
Projede yemek yerleri, Dinner tablosunda tutulmaktadır. Entity framework tarafında da, veritabaındaki Dinner tablosu Dinner sınıfı tarafından temsil edilmektedir. Bu noktada Dinner tablosuna CRUD(Create, Update, Delete) işlemlerini gerçekleştirebilmek için bir arayüz (interface) tanımlanır. Bu arayüzü uygulayan(implement) eden sınıflar Dinner tablosuna CRUD işlemlerini yapmaya hazır demektir.
Hazırladığımız interface aşağıdaki gibidir.
public interface IDinnerRepository { IQueryable<Dinner> FindAllDinners(); IQueryable<Dinner> FindDinnersByText(string q); Dinner GetDinner(int id); void Add(Dinner dinner); void Delete(Dinner dinner); void Save(); }
Bu arayüzü uygulayan bir sınıfı da aşağıdaki gibi gösterebiliriz.
public class DinnerRepository : NerdDinner.Models.IDinnerRepository { NerdDinnerEntities db = new NerdDinnerEntities(); Public IQueryable<Dinner> FindDinnersByText(string q) { return db.Dinners.Where(d => d.Title.Contains(q) || d.Description.Contains(q) || d.HostedBy.Contains(q)); } public IQueryable<Dinner> FindAllDinners() { return db.Dinners; } public Dinner GetDinner(int id) { return db.Dinners.SingleOrDefault(d => d.DinnerID == id); } public void Add(Dinner dinner) { db.Dinners.AddObject(dinner); } public void Delete(Dinner dinner) { foreach (RSVP rsvp in dinner.RSVPs.ToList()) db.RSVPs.DeleteObject(rsvp); db.Dinners.DeleteObject(dinner); } public void Save() { db.SaveChanges(); } }
Buradaki NerdDinnerEntities tipi Entity Framework tarafına ait, ObjectContext tipinden türemiş ve kullanıcı tarafından oluşturulan nesneleri alıp kaydetme, güncelleme veya silme işlemleri için oluşturulmuş, motor(engine) görevindeki sınıftır.
Save metodu içerisinde de zaten NerdDinnerEntities tipinin SaveChanges metodu çağrılmıştır ki bu metod, yapılan değişiklikleri veritabanına yansıtır.
Artık Dinner tipindeki bir nesnenin veritabaına kaydedilmesi, güncellenmesi veya silinmesi gibi işlemlerden sorumlu olan sınıf DinnerRepository sınıfıdır. Başka hiçbir yerde bu işlemler için kod yazmaya gerek yoktur. Bir süre sonra, sistemde tarihe göre arama işlemi yapılmak istendiğinde, ilgili metod IDinnerRepository arayüzünde belirlenerek, DinnerRepository sınıfına uygulandığında işlem tamamdır. Tek sorumluluk(Single Responsibility) DinnerRepository sınıfına aittir. Bu sayede yazılım kurallarından “Tek Sorumluluk” ilkesine de uymuş olduk.
Repository pattern sayesinde test edilebilirlik te mümkün hale gelmektedir. Bu daha ayrıntılı bir konu olduğu için ileriki yazılarımızda işlemeye çalışacağız.
Repository patternle karşılaşmadan önce, kendi kendime desenler oluşturup, tek noktadan veri yönetimini sağlamaya çalışıyordum. Ancak bu desen bana daha düzgün geldi ve artık bu deseni iyice benimsedim. Test gücünü de gördükten sonra vazgeçilmezim oldu. Herkese de tavsiye ederim.