CQRS Pattern Nedir?
Merhabalar, başka bir içerikle tekrardan sizlerleyim 🙂 Bu makalede, son zamanlarda adı sık duyulmaya başlanan CQRS pattern üzerinde duracağız, olayını anlamaya ve uygulamalı olarak gerçekleştirmeye çalışacağız.
CQRS pattern ile tanışmam benim de henüz yeni oldu, o yüzden hatalı veya eksik kısımlar olursa kusura bakmayın, lütfen eleştirmekten çekinmeyin.
Bu arada unutmadan, uygulama aşamasında gencayyildiz.com isimli siteden oldukça yararlandım, biz tabi biraz daha özelleştirerek ilerleyeceğiz, ancak oradaki örneğe de göz gezdirebilirsiniz. Eğer hazırsak konuşmaya başlayabiliriz.

CQRS (Command Query Responsibility Segregation) pattern, adından da anlaşılacağı üzere, “Command” ve “Query” sorumluluklarının ayrılması gerektiğini belirten bir tasarım desenidir. Nedir bu “Command” ve “Query”?
Bildiğimiz üzere, kullanıcıların uygulamalar üzerinde gerçekleştirdiği istekler genel olarak iki tiptir, bunlardan biri olmayan verinin oluşturulması veya veri üzerinde değişiklik yapılması, bir diğeri de olan verilerin okunması işlemidir. Yani kısaca, okuma ve yazma işlemleri olarak bahsedebiliriz.
İşte, sisteme gelen veri yazma isteklerine “Command” , okuma isteklerine ise “Query” denmektedir. CQRS, bu iki tip işlemin birbirinden ayrılması gerektiğini savunur.
Mantığını tam olarak oturtmak adına biraz daha detaya inelim. Katmanlı mimaride çoğunlukla kullandığımız “Repository” tasarım deseninde, veri erişim işlemleri tipine bakılmaksızın repository isimli sınıflar içerisinde bulunur.

Diyagramdanda görebileceğiniz gibi, business içerisinde yapılan işlemlerden sonra, veri erişim katmanına erişerek repository sınıfları aracılığıyla veri tabanı işlemleri gerçekleştirilir.
CQRS, Repository patternda gerçekleştirilen bu olayın daha detaylı ele alınması gerektiğini, bir sınıf içerisinde hem command hem query işlemlerinin bulunmaması gerektiğini savunur. Bu bağlamda CQRS patterni, Repository patternin biraz daha “Single Responsibility” denilen prensipe uyarlanmış hali olarak düşünebiliriz.

CQRS patternin uygulandığı bir projede yapı tam olarak bu şekile dönüşmektedir.
CQRS tasarım desenini elimden geldiğince anlatmaya çalıştım. Şimdi, artık bu desende karşımıza çıkan ana yapıları ve kavramları inceleyelim. Önceden bahsettiğim kavramların da kısaca tekrar üstünden geçeceğim.
Command : Veriyi manipüle etme işlemlerine verilen genel isimdir. (Insert-Update-Delete)
Request Command : Yapılacak command isteklerini karşılayan sınıflardır.
Response Command : Yapılan command isteklerine karşılık olarak dönülecek sınıflardır.
Query : Veriyi okuma işlemlerine verilen genel isimdir. (Select)
Request Query: Yapılacak query isteklerini karşılayan sınıflardır.
Response Query: Yapılan query isteklerine karşılık olarak dönülecek sınıflardır.
Handler : Uygulama üzerinde yapılan tüm command ve query isteklerini işleyen, ve geriye sonuç döndüren snıflardır. Handler yapısı, ek olarak object mapping dediğimiz işlemleri oldukça kolaylaştırmaktadır.
Command Handlers : Yapılan command isteklerini işleyen ve response dönen handle sınıflarıdır.
Query-Handlers : Yapılan query isteklerini işleyen ve response dönen handle sınıflarıdır.
Kavramlara da değindiğimize göre, artık uygulamamıza geçebiliriz. Uygulamada istek ve handler yönetimini önce manuel kendimiz yapacağız. Bunun dezavantajını canlı olarak deneyimledikten sonra, mediator pattern ve kütüphanesi ile yönetimi daha da kolaylaştıracağız.
Öncelikle, temel konfigürasyonları tekrardan yapmamak adına önceden entity framework ve dapper implementasyonu gerçekleştirdiğim hazır projemin üzerine kodlamaya devam edeceğim. Data Access katmanımın içerisine “CQRS” isimli bir klasör açıyorum. Bu klasör içerisine command, query ve handler isimli üç klasör oluşturuyorum.

Artık cqrs patterni uygulayarak kodlamamıza başlayabiliriz. Adres entitysi ile ilgili tüm crud işlemleri gerçekleştirecek, ancak kısa tutmak amacıyla burada bir command ve bir query örneği veriyor olacağım. Dilerseniz makalenin altından kaynak kodlara ulaşabilirsiniz. Önce Command işlemini yapalım. her bir command veya query işlemi için bir request, bir response ve bir de handler sınıfımızın olacağını belirtmiştik.


Request sınıfı içerisinde, adres oluşturmak için gerekli alanlar bulunmaktadır. Response sınıfı içerisinde ise, işlem yapıldıktan sonra kullanıcıya dönülecek bilgi alanları bulunmaktadır. Eklenen adresin idsini kullanıcıya geri döneceğiz. Şimdi, bu requesti çalıştıracak ve bir response dönecek olan handler sınıfımızı oluşturalım.

Handler sınıfını da görüldüğü şekilde kodladık. Şimdi, query işlemi gerçekleştirelim.



İşlemlerimizi gerçekleştirdik. Artık business katmanında cqrs patterni kullanarak işlemler gerçekleştirelim.
CQRS patterne göre kodlanmış servis sınıfımızı yukarıda görüyorsunuz. Controllerdan gelen istekleri karşılayan, ve geriye bir response dönen yapılar şeklinde kodlandı. Ancak, burada 5 adet temel işlem gerçekleştirdik, şuanda bile göze batan ve ileride çok daha büyük bir soruna yol açacak sorunu görebiliyor musunuz?
Her bir handle sınıfını yapıcı methot içerisinde enjekte ettiğimizden dolayı, daha şimdiden yapıcı methotumuz “constructor hell” dediğimiz karmaşıklığa sahip oldu. Proje büyüdükçe ve handle sınıfları arttıkça, yapıcı methot içerisi şişecektir.
Bu durumu , mediatr pattern ve kütüphanesi ile çözebiliriz.
Mediator Nedir?
Mediator, tek bir nesne içerisinde çeşitli nesneler arasındaki karmaşık ilişkileri yönetmemize olanak veren bir tasarım desenidir. Mediatr kütüphanesini kullanarak projemiz üzerinde uygulayabilmekteyiz. Böylelikle her bir handler sınıfını enjekte etmek zorunda kalmayacağız, gönderilen request nesnesine göre, hangi handler sınıfının çalıştırılacağını mediator bizim yerimize yönetecek.
Projemize mediatr, ve mediatr.extension.dependencyinjection kütüphanelerini ekleyelim.

Unutmadan, startup içerisinde de servislerimiz içerisine ekleyelim.

Artık kullanmaya hazırız. Öncelikle, mediatorun bizim için request-handler ilişkisini yönetebilmesi için, request sınıflarımıza “IRequest” , ve handler sınıflarımıza “IRequestHandler” generic interface’lerini implemente etmek zorundayız.
IRequest<TResponse> : Command veya query request sınıflarının implemente edeceği interface’dir. Generic olarak, geriye döndüreceği response sınıfını bildirmemiz gerekmektedir.
IRequestHandler<TRequest,TResponse> : Tüm handler sınıflarının implemente edeceği interface’dir. Generic olarak, alacağı request ve döneceği response tipini bildirmemiz gerekmektedir.
Mediator bu interfacelerdeki request ve response değerlerini analiz ederek, hangi request sınıfı geldiğinde hangi handlerin çalıştırılacağı, ve hangi response sınıfının döndürüleceği işleminlerini yönetir.
“Interface Segregation” prensipi dahilinde anlaşılması kolay olması amacıyla interface’leri ayırıyorum. Command ve query işlemleri için ayrı interfaceler oluşturuyorum, ancak hepsi özünde aynı interface’i miras almaktadır.




Artık, request ve handler sınıflarımızı tekrar düzenleyebiliriz. Command işlemi için;


Query işlemi için;


Bu şekilde interface’lerimizi implemente ettik. Projede yer alan diğer tüm işlemler içinde bu işlem uygulanmıştır. Artık, servis sınıfımızın aldığı son hali görelim.
Handler sınıflarının yönetimi bu kadar basitleşmiş oldu. Tek bir IMediator tipinde nesne enjekte ediyoruz, içerisine hangi request nesnesi gönderilirse, ona karşılık gelen handler sınıfını çalıştırarak bize bir sonuç döndürüyor. Controller sınıfımızın da son haline bakabiliriz.
Böylece uygulamamızın sonuna gelmiş olduk. CQRS patternin mediator pattern ile uygulanmasını elimden geldiğince sizlere aktarmaya çalıştım. Umarım faydalı bir içerik olmuştur.
Uygulama aşamasında yardım aldığım siteye, ve projenin kaynak kodlarına aşağıdaki linklerden ulaşabilirsiniz. Bir sonraki yazıda görüşmek üzere, kendinize çok iyi bakın. 🤗
https://github.com/ErenYilmaz97/CQRS-Mediator-Pattern
https://www.gencayyildiz.com/blog/cqrs-pattern-nedir-mediatr-kutuphanesi-ile-nasil-uygulanir/