CQRS Design Pattern Nedir, Neden Kullanılır? Bir CQRS Design Pattern İncelemesi

Yusuf Yılmaz
Sahibinden Technology
5 min readJan 17, 2022
Photo by Jake Givens on Unsplash

Günümüz sistemleri bir önceki sistemlere nazaran artık daha fazla trafik almaktadır. Bu tür durumlarda uygulamaların erişilebilir olmasını, yüksek trafik altında da makul yanıt verme sürelerini elde etmeyi ve performanslı bir ürün ortaya çıkartmayı amaç edinmekteyiz.

Bu yazıda ise uygulamarımızı; daha performanslı, ölçeklendirilebilir ve güvenli bir hale getirmemize yardımcı olan CQRS tasarım deseni hakkında bilgiler edineceğiz.

CQRS Design Pattern Nedir?

CQRS, “Command and Query Responsibility Segregation” kelimelerinin baş harflerinden meydana gelmektedir ve veri kaynağına ait command ve query işlemlerinin birbirinden ayırmayı amaçlar.

Command

Command, temel olarak veri kaynağındaki verinin değiştirilmesini ifade eder. Bu değişimler; Insert, Update, Delete gibi işlemler sayesinde olur. Genel olarak geriye bir veri döndürmesi beklenmez, geriye değer döndürmek bir query/okuma tarafından beklenen bir davranıştır.

Command, uygulama içerisindeki iş/business kurallarının ve diğer şartlı ifadelerin kontrolü gibi sorguların olduğu kısımdır ve isimlendirme olarak da imperative/emirsel bir şekilde ifade edilir çünkü uygulamaya bir değişiklik olacağını belirtir. Yazının devam eden kısımlarında ise command ifadesi yazma olarak adlandırılacaktır.

Query

Query, temel olarak veri kaynağındaki verilerin alınmasından sorumludur veriye ait herhangi bir değeri değiştirmemesi gerekmektedir.

Query, genellikle isimlendirme standartında Get anahtar kelimesi ile başlar. Yazının devam eden kısımlarında ise query ifadesi okuma olarak adlandırılacaktır.

Problem?

Aşağıda geleneksel bir monolith uygulama yer almaktadır.

https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs

Geleneksel yapıda, okuma ve yazma işlemleri için aynı veri modeli kullanılır. Bu temel CRUD işlemlerini yerine getiren uygulamalar için basit olsa da büyüyen uygulamalarda hantal bir hale gelebiliyor.

Örneğin; uygulama içerisinde okuma işlemlerinin gerçekleştiği birçok kısım var ve hepsi de kendi içerisinde kompleks sorgular içermektedir ve bu sorgu sonuçlarını barındıracak DTO ise karmaşıktır. Burada bu verinin DTO’ya (Data Transfer Object) çevrilmesi gibi işlemler de yer almaktadır, mapping* işlemi maliyetli olmakla birlikte model içerisindeki sorgular iyice kompleks olmaya başlamaktadır. Okuma ve yazma işlemleri aynı yerde yapıldığından dolayı performans olarak da yavaş bir yapı ortaya çıkacaktır.

Çözüm

The Command and Query Responsibility Segregation (CQRS) pattern separates read and update operations for a data store.

CQRS, okuma ve yazma işlemleri için farklı modellere ayrılmasını amaçlamaktadır. Bu sayede; command yazma işlemleri için, query de okuma işlemleri için farklı veri modellerini kullanacaktır.

Bu her ne kadar okuma gibi işlemleri yazmadan ayırma ve asimetrik olarak işlemler gerçekleştirmemize yardımcı olsa da bu farklı modeller aynı veri kaynağında bulunduğu için gene de bir performans ve ölçekleme gibi problemler devam etmektedir…

CQRS’in temel bir şekilde uygulanmış halini ifade eden görsel.

Ayrık Veri Tabanları

Yukarıda bahsedilen CQRS’in temel olarak uygulanmasında okuma ve yazma verileri ayrı bir halde aynı veri kaynağı içerisinde yer almaktadır. CQRS ile çalışılınca ayrı veri tabanı kullanmak zorunlu değildir fakat avantajları yer almaktadır.

KISS is an acronym for Keep It Simple, Stupid. This principle says about to make your code simple. You should avoid unnecessary complexity. A simple code it’s easier to maintain and easier to understand.

KISS prensibi, bizlere uygulamayı daha basit tutmamızdan ve daha kompleks hale getirmemizden sakınmamızı söyler fakat artan trafik ve uygulamaya ait kaynakları verimli kullanmak amacıyla bu tür senaryolara da gidilebilmektedir. Sonuç itibariyle aynı veri kaynağında bulunduğundan dolayı aynı kaynağa hem okuma hem de yazma istekleri gelecektir.

Okuma ve yazma işlemleri asimetrik işlemlerdir, birbirinden farklı performans ve ölçekleme gereksinimleri barındırır.

Örnek vermek gerekirse yazma işlemi için ilişkisel bir veri tabanı uygun iken, okuma işlemi için document-based bir veri tabanı daha sağlıklı olabilmektedir. Bu sayede her iki taraf da kendi gereksinimlerine göre verimli bir süreç kullanmış olacaktır ve okuma işlemi için gelen trafik yazma işlemi için olan trafiğe etki etmeyecektir.

Yazma işlemleri MySQL üzerinde, okuma işlemleri ise Elasticsearch üzerinden yapılabilir. Bu sayede daha performanslı ve gereksinimlere göre ölçeklenilebilen bir yapı ortaya çıkmış olur.

Bu tür yapıya geçişin kendi içerisinde de bir maliyeti söz konusudur. Sistemdeki veri kaynaklarını ayırmanın getirdiği bakım ve yönetme maliyetinin dışında uygulama içerisindeki verilerin tutarlılığını da sağlamak gerekmektedir. Yazma işlemlerinin olduğu veri kaynağına ait değişikliklerin okuma işlemlerinin gerçekleştirildiği veri kaynağına da iletilmesi gerekiyor, bu tür senaryolarda event yapısı gibi birçok yapı da kullanılabilir.

CQRS’in ayrık veri tabanlarında uygulanmış halini temsil etmektedir.

CQRS’in Avantajları

Yukarıda da bahsedildiği gibi birçok avantajı yer almaktadır. CQRS’in bazı avantajları;

  • Independent scaling: okuma ve yazma işlemleri birbirinden ayrıldığı için bu yapılar birbirinden bağımsız ölçeklenebilir bir yapı haline gelmektedir.
  • Optimized data schemas: Okuma tarafında bulunan veri kaynağı okumaya özgü, yazma tarafına ait veri kaynağı da yazmaya özgü şema kullandığından dolayı iki tarafta da kendi sorgularına göre optimize edilmiş bir yapı kullanıyor.
  • Separation of concerns: Okuma ve yazma kısımlarını ayırmak, daha sürdürülebilir ve esnek modellerle sonuçlanabilir. Karmaşık iş/business mantığının çoğu yazma modeline girer. Okuma modeli nispeten basit olabilir.
  • Simpler queries: Okuma işlemlerini yapacağımız veri kaynağı hali hazırda okumaya hazır/hazırlanmış bir veri içerdiği için kompleks join işlemlerinden de kaçınılmış olunacaktır.

CQRS’in Dezavantajları

  • Complexity: CQRS mantık çerçevesinde basit gibi dursa da uygulama tasarımına ekstra karmaşıklık ekleyebilmektedir özellikle de Event Sourcing pattern uygulandığı zaman.
  • Messaging: CQRS uygulama içerisindeki mesajlaşmayı (command sonrası event gönderilmesi gibi) zorunlu tutmamaktadır fakat bu işlem yaygın olarak kullanılmaktadır. Buradaki durumda ise, uygulama içerisindeki mesajın işlenememesi ya da mesajın duplicate* olması durumunu da göz önünde bulundurmak ve buna göre de bir aksiyon almak gerekmektedir.
  • Eventual consistency: Okuma ve yazma işlemlerini ayırdığımızda, okuma işlemlerini gerçekleştirdiğimiz veri kaynağındaki veriler eski kalabilir. Yazma işlemlerinden sonra yapılan değişikliklerin okuma kısmına ait veri kaynağına da yansıması gerekmektedir. Bu tür durumlarda hangi verinin eski kaldığının tespiti ya da kullanıcının istekte bulunduğu verinin eski olup olmadığının tespiti zor olmaktadır. Özetle farklı veri kaynakları arasındaki verilerin tutarlı olması durumudur.

Ne Zaman CQRS’i Kullanmalıyız

Aşağıdaki senaryolar için CQRS göz önünde bulundurulabilir;

  • Okuma ve yazma işlemlerinin birbirinden bağımsız* ilerlemesini istediğimiz durumlarda. Bu bağımsızlık; ölçeklenebilirlik, erişilebilirlik gibi konular için yapılan çalışmalar olarak da düşünülebilir.
  • Okuma işlemlerinin uygulandığı kısmın istek sayısının yazmaya nazaran çok fazla olduğu durumlarda. Bu sayede farklı ölçeklenme sayılarına erişilmiş olunacaktır. Okuma için gerçekleşmesi gereken instance* sayısı yazmaya nazaran daha fazla olabilir.
  • Okuma ve yazma işlemlerine spesifik olarak adapte olmuş takımların olduğu durumlarda, örneğin sahibinden.com içerisinde birçok küre(takım) yer almaktadır bunlardan bazıları ise ilan küresi ve arama&reklam küresidir. İlana ait yazma ve okuma işlemleri farklı küreler tarafından işlenmektedir ve ilana ait bilgiler de farklı veri kaynaklarında tutulmaktadır.
  • Diğer sistemler ile entegrasyon noktasında da kullanılabilir bu sayede birinde meydana gelecek olan hata direkt olarak diğer sistemi de kitlememiş olacaktır.

Ne Zaman CQRS’i Kullanmamalıyız

  • Domain ya da business kuralları basit olan sistemlerde.
  • Temel bir CRUD işlemini gerçekleştiren uygulamalarda.

Yukarıda sayılan maddelerde uygulamanın gerek basitliği gerek de kurallarının kompleks olmaması uygulamayı CQRS’i entegre etmeye zorunlu kılmamaktadır aksi taktirde fazla efor ve karmaşıklık getiren bir sürece doğru itilmesi anlamına gelecektir.

Beni; Github, Twitter ve diğer sosyal ağlardan takip edebilirsiniz.

--

--