CQRS Nedir ? Muhteşem İkili Ayrılıyor mu?

CQRS Nedir ?

Açılımı Command Query Responsibility Segregation ‘dır. Komut ve Sorgu Sorumluluklarının Ayrışması anlamına gelir. Bu desene göre metotlar Commands ve Queries olarak iki parçaya ayrılmalıdır.

Commands: Objenin veya sistemin durumunu değiştirir.
Queries: Bir objenin veya sistemin durumunu değiştirmez, sadece response döner.

Sorun

Şimdiye kadar projelerde veritabanı sorgulama ve güncelleştirme için aynı veri modeli kullanıyorduk. Temel CRUD (Create-Read-Update-Delete) işlemleri için yeterli bir senaryo idi. Fakat sistemler daha fazla geliştikçe ve karmaşıklaşmaya başladıkça bu basit yaklaşım yetersiz hale gelebilir. Veri okuma işleminde birden fazla sorgu çalıştırılarak farklı yapıda DTO döndürülebilir. Object mapping karmaşık hale gelebilir.
Veri yazma işleminde ise validation ve business logic gibi multi işlem gerçekleştiren bir durumla karşı karşıya kalabiliriz.

Bu geleneksel yapı sistem üzerinde performans kaybına neden olabilmektedir.

CQRS’nin Faydaları

  • Bağımsız ölçekleme

Çözüm

CQRS, verileri güncellemek için komutları ve verileri okumak için sorguları kullanarak okuma ve yazma işlemlerini farklı modellere ayırır.
Okuma ve yazma iş yüklerinin bağımsız olarak ölçeklendirilebilmesine olanak tanır. Okuma ve yazma taraflarının ayrılması daha sürdürülebilir ve esnek modellerin oluşmasını sağlayabilir. Karmaşık business logic daha çok yazma modelinde olur. Okuma modeli sade olabilir.

CQRS’nin bazı implementasyonları Event Sourcing desenini kullanır.
Bu kalıpla, uygulama durumu bir olaylar dizisi olarak saklanır. Her olay, verilerdeki bir dizi değişikliği temsil eder. Mevcut durum, olaylar tekrarlanarak oluşturulur. Event Sourcing kullanmanın bir yararı, aynı olayların diğer bileşenlerini(özellikle de okuma modelini) bildirmek için kullanılabilmesidir. Ancak, tasarıma karmaşıklık katar.

CQRS uygulamanın bazı zorlukları

  • Karmaşıklık

Aslında CQRS mantığı basittir fakat çok komplex uygulama tasarımları ve event sourcing deseni karmaşıklık çıkartabilir.

  • Mesajlaşma

CQRS deseni mesajlaşma gerektirmemesine rağmen, komutları işlemek ve güncelleme olaylarını yayınlamak için mesajlaşmayı kullanabilir. Uygulama bu durumda hataları işlemelidir.

  • Tutarlılık

Okuma ve yazma veritabanlarını ayırırsanız, okunan veriler eski kalmış olabilir. Okuma modeli deposu, yazma modeli deposundaki değişiklikleri yansıtacak şekilde güncellenmelidir. Bu durumda kullanıcı eski veriye sahip data okursa tespit edilmesi zor olabilir.

Mesajlaşma için “RabbitMQ”, Query için ise “ElasticSearch” gibi teknolojiler kullanılabilir.

Peki şu ana kadar bahsettiğimiz konuları projemizde nasıl uygularız ona bakalım. Örneğimizi .NET Core’da yapacağız ve MediatR kütüphanesini kullanacağız.

MediatR ve CQRS ile .Net Core API Oluşturma

MediatR Nedir ?

MediatR süreç mesajlaşmasına izin veren bir kütüphanedir. Mediator desenini uygulamamızı sağlar.

Mediator Nedir?

En çok verilen örneklerden biri havalimanlarında uçakların iznini veren kule yapısıdır. Uçaklar kendi aralarında konuşmazlar sadece kule ile iletişimde olurlar ve ona göre işlemler gerçekleşir. Mediator burada kule görevini görür.

.Net Core Web API Oluşturma

dotnet new webapi -o CQRSApi

Proje dizinine girip VS Code’u açalım.
cd CQRSApi
code .

MediatR Kurulumu

IDE olarak Visual Studio Code kullanarak .Net CLI ile MediatR kütüphanesini yükleyelim.

dotnet add package MediatR --version 8.0.1
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection --version 8.0.0

Startup sınıfında ConfigureServices metodunda MediatR’ı ekleyelim.
services.AddMediatR(typeof(Startup));

3 farklı metot sağlayan bir API yazıp MediatR Kullanımını inceleyelim.

Proje Yapısı
Queries, Commands, Handlers, Repository, Helper, Responses şeklinde projemizi tasarlayarak belli bir düzen içerisinde yazmaya başlayalım.

Aşağıdaki yapıda GetToDos, GetToDo, CreateToDo endpointleri bir request aldığı zaman MediatR, Send yardımıyla ilgili metot için yazılan handler’a request’i gönderir.

/api/ToDos için request geldiğinde; GetToDosHandler , /api/ToDos/1 için request geldiğinde; GetToDoHandler request’i yakalar.
Peki handler bu request’i nasıl yakalıyor?

Controller içerisinde aşağıda görüldüğü gibi bir query nesnesi oluşturmuştuk.

Bir request için handler tanımı aşağıdaki şekildeIRequestHandler ile yapılıyor. TRequest,TResponse tipleri veriliyor.
Send metodu, eğer GetToDosQuery tipinde bir request gönderirse bunu GetToDosHandler handler’ı yakalayacaktır, çünkü handler’ın Request tipi GetToDosQuery dir.

Örnek projeyi debug yaparak metot ve handler ilişkisini daha iyi kavrayabilirsiniz.
Proje detaylarına Github hesabımdan ulaşabilirsiniz.
İyi Çalışmalar..

Software Engineer @hepsiburada

Software Engineer @hepsiburada