Furkan Özmen
folksdev
Published in
4 min readApr 16, 2022

--

CQRS(Command Query Responsibility Segregation) Pattern İmplementation With Spring-Java-Kotlin And Kafka

Herkese selam, bu yazımda CQRS’in ne olduğundan , neye yaradığına kadar kadar basitçe anlatıp implementasyon detaylarına değineceğiz. CQRS tasarım modelini uygularken Spring Boot modulünü kullanıp bir Topluluk sayfası yaratma projesi yazacağız. Bu yazıda sadece Java’yı kullanmayacağım. Beraberinde Kotlin’i de kullanacağım. Model sınıflarını tasarlarken Java ile çok fazla boilerplate kodlar yazdığımızın farkındayızdır sanırım. Bu boilerplate kodlardan kurtulmak için çok popüler bir kütüphane var. Adı lombok, duymayan yoktur sanırım. Ama bu projede kullanmayacağım. Sebepleri başka bir yazının konusu olabilir sanırım, bu yüzden bu detaylara değinmeyi düşünmüyorum. Gel gelelim projede Kotlin’i nerede konumlandıracağımıza. Burada Entity sınıflarımı yazarken getter-setter-constructor gibi boilerplate kodları yazmak yerine Kotlin ile data class’ları kullanacağız. Bizim için arka planda getter-setter methodlarını oluşturacak.

CQRS NEDİR VE NE İŞE YARAR

CQRS aslında command ve query prensiplerinin birbirinden ayrılma prensibini savunuyor.

Uygulamalarınızda CQRS mimari modeline göre oluşturmaya başladığınızda; uygulamanızın performansını, ölçeklenebilirliğini ve güvenliğini bir üst seviyeye çıkarabilirsiniz.

Bu yaklaşımda eventler 2 farklı modele ayrılmalıdır:

  • Commands: Objenin durumunu değiştirme.
  • Queries: Geriye sadece objenin kendisini döner. Herhangi bir update,delete,put işlemi yapmaz.

Queries

Veritabanından sadece veri alma ile yükümlüdür. Veri üzerinde bir değişiklik yapmaz.

Commands

Yeni bir veri eklemek ya da var olan veri üzerinde update işlemi yapmak için kullanılır. Örnek olarak ; Insert, Update, Delete. Geriye herhangi bir veri döndürmez.

burada geriye boolean değer dönüyoruz, veritabanından herhangi bir veriyi read edip dönmüyoruz. Yazı boyunca veri kelimesinden DB’den okunan değeri bahsediyor olacağım.

Ne Zaman CQRS

  • Yüksek trafiğin olduğu sistemlerde.
  • Complex business kurallarının olduğu sistemlerde.

Ne Zaman Kullanmamalıyız

Burada kesin bir yargıdan söz etmek mümkün değil ama basic CRUD işlemlerinin yapıldığı bir sistemde uygunlanması pek doğru olmayabilir. İmplemente edilebilir fakat getirdiği iş yükü ve maliyet hesaplandığı zaman bu tasarım modelini implemente etmek pek doğru olmayabilir.

CQRS’in Getirdiği Avantajlar

  • Read ve Write işlemlerini birbirinden ayırdığımız için her iki işlem içinde farklı veritabanları kullanmamızın önü açılmış oluyor. Bu sayede Write için performansı iyi olan bir DB seçimi serbest bırakılırken aynı şekilde Read içinde performansı iyi olan bir DB kullanmak mümkün hale geliyor.
  • Read ve Write birbirinden ayrıldığı için ölçeklenebilirliği ve performansı arttıracaktır.

Teorik bilgilerden kısaca bahsettikten sonra implementasyon detaylarını uygulamaya geçebiliriz. Bir Spring uygulaması oluşturarak web projesi oluşturalım. Bu yazıda bir spring projesi oluşturma adımlarına değinmeyeceğim için biz direkt docker-compose dosyamızı oluşturmakla başlayabiliriz.

Buradaki imagelere bakmak gerekirse Kafka’nın çalışabilmesi için zookeper kullandık. Kafka-ui belki yabancı gelmiş olabilir. Bu yazılım sayesinde Kafka’daki topicleri ve topiclere yazılmış mesajları görselleştirilmiş bir halde görmemize olanak sağlayan open-source bir yazılımdır. Onun haricinde elasticSearch’ü ekleyip write işlemlerim için postgreSQL containerına da ekledim.

Projemizin genel görünümünü bir diagram üzerinde tasarladım. Biraz da neler yapacağımızdan bahsedip kodlamaya girişelim.

Belki çizdiğim görsel gözlerinizi kanatmış olabilir ama yapmak istediğimi anlatabildiğimi düşünüyorum. Write ve Read işlemlerini birbirinden ayıracağız. Write işlemleri için PostgreSQL kullandım. Verilerimi DB’ye yazdım fakat Read için kullandığım ElasticSearch’ün bundan haberi yok şuanlık yok. Bunun için elastic indexlerini güncellemek ve ona haber vermek gerekiyor. Fakat benim istediğim şöyle bir şey var. Write işleminin tamamlanması , indexleme işleminin tamalanmasına bağlı olmamalı. Bu yüzden yazdığımız veriyi bir kuyruğa veya topic’e yazıp geri kalan işlemlerin kuyruğu dinleyen methodların yapmasını istedim. Burada RabbitMQ gibi teknolojileri kullanabilirsiniz. Ben Kafka’yı kullanmayı tercih ettim. Teknoloji tamamen değişiklik gösterebilir. Sizin işinizi hangi teknoloji görüyorsa onu kullanmalısınız.

Şimdi İlk model sınıfımızı oluşturalım.

burada tablo detaylarımı oluşturdum. Hibernate benin için arka planda DB üzerinde tablo yaratma işlemini gerçekleştirecek. Şimdi ise Repository sınıfımızı oluşturalım.

Command işlemleri için bir request sınıfı oluşturalım. Model sınıflarda yine kotlin kullanmaya devam ediyorum.

Şimdi ise Write işlemleri için tetiklenecek işlemi handle eden bir sınıf yazalım.

Burada aldığı request’i Entity nesnesine çeviren ve çevirdiği nesneyi DB’ye yazan bir method yazdık. Sonda ise yazılan veriyi “cqrs-event” topiğine yolladık. Artık gerisi bu topiği dinleyen methoda kaldı. Bu işlemi yaptıktan sonra da geriye bir result döndüm. Peki topiği dinleyen arkadaş ne yapıyor gelin biraz bakalım.

Bu kısımda ise aldığı Payload’ı Publication nesnesine çeviriyor ve elastic servisine ileterek index yaratma işlemini gerçekleştiriyor. createIndex’in ne iş yaptığı görmek isterseniz diye onu da buraya yazıyorum.

Şimdi Write işlemimizi tamamladığımıza göre Read işlemini implemente etmeye başlamamız gerekiyor. Sorgulama için bir Request sınıfına ihtiyac duyabiliriz bunun için PageQuery isimli model sınıfımızı oluşturalım.

Son olarak Read işlemini handle edecek bir sınıfa ihtiyacımız var. Burada direkt elastic servisindeki search methodundan veri çeken bir method yazdık.

index name olarak ilk başta publications verdiğimiz için burada yanlış isimlendirme sorunlara yol açacaktır.

Burada en son elastic üzerinde indexlediğimiz verileri read ediyoruz. Ayrıca sourceBuilder ile detaylı Query’ler de oluşturabiliriz.

Günün sonunda kod örneklerine baktığınız zaman bazı yerleri kaçırmış olabilirsiniz. Tüm implementasyon detaylarını yazmam mümkün olmadığı için sadece önemli olan tarafları yazmayı tercih ettim. Config dosyası ve DTO objelerini bir daha yazma gereği duymadım.

Bu yazımda CQRS tasarım modelini basitçe implemente etmeye çalıştım. CQRS’i uygularken verilerin tutarlılığı ve senkronizasyon problemleri olabiliyor. Bunu çözmek içinde değişik yöntemler bulunuyor. Örnek olarak Trendyol CQRS desenini kullandıkları ve yazma için Couchbase, okuma için ise ElasticSearch kullandıkları projede bu 2 veritabanı arasında oluşan senkronizasyon problemini ve bu problemi nasıl aştıklarını anlattığı şu yazıyı okuyabilirisniz.

--

--