CQRS Pattern ile Repository Pattern Kullanımı
Yazılım projeleri oluştururken karmaşık yapılar yerine daha stabil, esnek ve performansı yüksek olacak şekilde mimariler uygulayıp yazılım projelerini şekillendirmek en doğrusudur. Çeşitli tasarım desenleri altında birçok farklı türdeki tasarım deseni, yazılım uygulamalarının daha esnek hale gelmesini, sürdürülebilir ve performanslı olmasını da sağlar. CQRS pattern de yine bu tasarım prensiplerinden biri olup, merkezinde query ve commandları birbirinden ayırarak isteklerin ortak noktadan değil, farklı kollardan gerçekleştirilmesini de sağlar. Command ve Query nedir? Projelerdeki karşılıkları nelerdir?
Command, projelerde mevcut olmayan verilerin oluşturulmasını, mevcut verilerin güncellenip silinmesini sağlayan isteklerdir. Query ise mevcut verilerin listelenmesi ve sunulmasını sağlayan isteklerdir. Command tarafında değişiklikler meydana gelirken herhangi bir değer döndürülmez. Query tarafında ise durum tam tersi olup veriler üzerinde değişiklik meydana getirilmeden geriye değer döndürmek amaçlanır.
CQRS yapısının en temelindeki fikir bir metodun geriye bir değer döndürebilmesi ya da objeyi değiştirmesidir. İkisini bir arada yapmayı kabul etmeyip farklı farklı ele alarak değerlendirir.
CQRS yapısı kullanılmadan kullanıcı tarafındaki isteklerin database tarafından işlenip geri dönüdürülmesi şu yapı ile açıklanabilir.
Bu yapıda tüm command ve query işlemleri tek bir elden yönetilmektedir. Bu durum zamanla karışıklığı, projelerdeki performansın düşmesine ve zamanla isteklerin istenen verimlilikte karşılanmamasına neden olabilir. Bu durumun önüne geçebilmek için command ve query kısımları birbirinden ayrılarak projeleri oluşturmak en doğru yöntemdir. CQRS tarafında ise aşağıdaki yapı ele alınabilir.
Bu yapıda query ve command işlemleri birbirinden ayrılmıştır.
CQRS İle Repository Pattern Kullanımı
Katmanlı mimariye sahip olan projelerde yazılım geliştirme aşamasında iş kuralları ve katmanların olabildiğince düzgün kurulması gerekmektedir. Düzgün bir mimariye sahip olmayan projelerde bir işi N defa yapmak zorunda kalınabilir. Nesneye yönelik programlama disiplinlerinden biri de bir kuralın tek bir sefer yazılarak N kez çağrılmasıdır. Nesneye yönelimli programlama ilkeleri kapsamınca iş katmanın uyulması gereken bazı kurallar şunlardır:
- Kod tekrarının önüne geçmek
- Hataları daha kolay ve hızlı yakalamak
- Kod düzeni sağlamak ve dolayısıyla kod okunabilirliği kolay hale getirmek
- Son aşamada projenin test edilebilir haline ulaşmak
Repository pattern yapısı da yine bu kurallar etrafında oluşturulan ve bu kurallara uyulmayı sağlayan bir yapıdır. Özellikle de kompleks ve veri merkezli uygulamalar için ideal olan repository pattern yapısında yapıya ayak uydurarak yazılım projesini gerçekleştirmek en doğrusu olacaktır.
Northwind veritabanını SQL Server üzerine kurarak ve .NET Core tarafında bağlantısını gerçekleştirerek bu veritabanındaki değerler üzerinden Web API projesi oluşturmayı amaçladım. Proje genelinde CQRS yapısını uyarladım ve Repository Pattern disiplinine uygun biçimde projenin tasarımını oluşturmaya çalıştım.
Repository pattern için şu diyagramı örnek olarak çıkardım:
Yazılım uygulamaları içerisinde yoğun bir veritabanı işlemi mevcuttur. Projenin her noktasında veritabanı ile alakalı işlemlerin tek tek yazılması yerine fonksiyonların tekrar kullanılabilir prensibine göre çalışması gerekir.
Repository Pattern içerisinde generic yapılanmaların olması da gerekir. Temel veri tabanı metotlarını barındıracak olan sınıf, yukarıdaki diyagramda da görüldüğü gibi IEntityRepository intarfecesidir. Oluşturulacak her bir entitye ait repositoryler IEntityRepository ve bu sınıftaki fonksiyonların somut anlamda yine generic olarak oluşturulan EfEntityRepositoryBase sınıflarından türemesi gerekmektedir.
Uygulama aşamasında IEntityRepository aşağıdaki gibi doldurulabilir.
Projede Repository Pattern Uygulaması
İmzası oluşturulan tüm bu metotlar, somut bir sınıf olan EfEntityRepositoryBase sınıfı tarafından içleri yine generic olarak doldurulur. Tanımlanması ve içlerinin doldurulması tek bir sefer yapılarak repository pattern tasarımı için ilk adımlar atılır.
Bundan sonrasi ise oluşturulacak her bir entity class için gerekli olan repositorylerin IEntityRepository ve EfEntityRepositoryBase sınıflarından türemesidir.
.NET Core tarafında katmanlı mimari oluşumu Business, Core, DataAccess ve Entities etrafında oluşturulur. Bu yapılara CQRS pattern ise şu şekilde kazandırılabilir:
CQRS temel mantığındaki query ve command alanlarının birbirinden ayrılma işlemleri Business altındaki Handlers klasörü içerisinde gerçekleştirilmektedir. Handlers altında oluşturulan her bir entitye ait klasörler oluşturulmalıdır. Bu klasörler altında ise Command ve Query alanları ayrılabilir. Ayrıca proje içerisinde Fluent Validation da kullanılması gerektiğinde Validation Rules adında klasör de oluşturulur.
Örnek olarak Categories adındaki entitye ait Handlers yukarıdaki gibi oluşturulur.
Repository Pattern uygulandığından dolayı IEntityRepository tarafında projede bir defa oluşturulan veritabanı metotlarını tekrar diğer repositorylerde oluşturmaya gerek kalmaz. Sadece ilişkili veritabanı işlemlerinde 2 veya daha fazla tablodan veri alınmak istendiğinde entitye ait IRepository interface alanında buna ait fonksiyon oluşturulur ve bu fonksiyon ilgili entitynin somut repository sınıfında ele alınır. Örnek olarak aşağıdaki yapılardan söz edilebilir.
Örneğin, projede yer verdiğim IProductRepository tarafında IEntityRepository interfacesini implement almama rağmen, veritabanı metotlarını burada almaya gerek duymadım. Veritabanındaki product tablosunun illişkili olduğu diğer tablolarla arasında ilişki kurmak adına bu fonksiyonları oluşturdum. Bu fonksiyonları oluştururken ise DTO klasöründe oluşturduğum entity classlardan yararlandım. DTO tarafında, ilişki kurmak istediğim tablolardaki alanları göstermeyi yaptım. Örneğin Product ile Category tabloları arasında ilişki kurmak istediğimde şu DTO yapısını ele aldım:
Örnek olması açısından Product ile Category tablolarından ayrı ayrı gelen ProductId, ProductName ve CategoryName alanlarını ele aldım.
IProductRepository tarafında harici olarak oluşturduğum fonksiyonları ProductRepository içerisinde de çağırmam ve ilişkili tabloyu kurabilmek için içini doldurmam gerekiyor.
ProductRepository tarafında ilişkiyi kurmak adına fonksiyonu doldurdum.
Projede Northwind veritabanındaki tüm tabloları ele almaya çalıştım.
Web API projesini oluştururken tablolar arası ilişkiyi daha iyi okuyabilmek ve kurabilmek için sql server üzerinde bu database diyagramını oluşturdum.
Projeyi çalıştırdığımda swagger üzerinde gerekli testleri yaparak sonuçlar elde ettim. Projenin çalışmsaıyla birlikte gelen swagger ekranım ise şu şekildedir:
Testlere ilişkin örnekler aşağıdaki gibidir.
Categories tablosundaki tüm verileri GET işlemiyle getirmeyi sağladım. Bir tane de POST işlemi göstermek istediğimde aşağıdaki sonuca ulaşıyorum:
Postman üzerinde Categories tablosuna POST ile veri ekleme işlemini gerçekleştirdim.
Diğer tüm tablolardaki API testlerini de GET, POST, PUT ve DELETE olarak gerçekleştirdim.
CQRS ile Repository Pattern’i bir arada kullandığım projeye ilişkin detaylar aşağıdaki gibidir.
Projeyi yazdığım IDE: Visual Studio 2022
Kullandığım Versiyon: .NET 6
Programlama Dili: C#, .Net Core, Web API
Proje Github Linki: https://github.com/Yuemwrite/CQRS-and-Repository-Pattern-WEB-API-.NET-CORE
Farklı projelerde ve yazılarımda görüşmek üzere 🙂