Geri Dön

CQRS Pattern Nedir? MediatR Kütüphanesi İle Nasıl Uygulanır?

Bu yazıda CQRS (Command Query Responsibility Segregation) pattern’ını ele alacağız.

CQRS Pattern Nedir? MediatR Kütüphanesi İle Nasıl Uygulanır?

Merhaba,

Bu yazıda CQRS (Command Query Responsibility Segregation) pattern’ını ele alacağız.

CQRS nedir, hangi problemleri çözmeyi amaçlar ve ne zaman tercih edilmelidir? Ayrıca MediatR kütüphanesi ile CQRS yaklaşımı nasıl uyumlu bir şekilde uygulanabilir, buna da pratik örnekler üzerinden değineceğim.

Uygulama üzerinden kullanıcıdan gelen istekler temelde iki gruba ayrılır. Gelen bir istek, ya mevcut bir veri üzerinde değişiklik yapar, olmayan bir veriyi oluşturur ya da mevcut veriyi siler; ya da herhangi bir değişiklik yapmadan yalnızca verinin okunmasını sağlar. Bu doğrultuda, yalnızca okuma işlemi yapan isteklere Query, veri üzerinde oluşturma, güncelleme veya silme işlemleri gerçekleştiren isteklere ise Command adı verilir.

    • Command: Veriler üzerinde değişiklik yapan işlemleri ifade eder. INSERT(Create), UPDATE ve DELETE gibi işlemler bu kapsama girer.
    • Query: SELECT yapan isteklerdir. Yani verileri sadece listelemek ve okumak için yapılan isteklerdir.

İşte CQRS, uygulamalarda veriyi okuyan işlemler ile veriyi oluşturan, güncelleyen ve silen işlemleri birbirinden ayırır. Amaç, bu iki farklı ihtiyacı tek bir yapı üzerinden yönetmek yerine, her birini kendi gereksinimlerine uygun şekilde ele alır.

Özellikle büyük projelerde, yoğun trafik nedeniyle okuma ve yazma işlemlerini ayırmak faydalı olabilir. Örneğin, okuma (query) işlemleri için daha hızlı veri erişimi sağlayan bir NoSQL veri tabanı tercih edilirken, Command işlemleri için ilişkisel bir veritabanı kullanılabilir. Bu sayede performans artar ve sistem daha sağlıklı çalışır.

CQRS Pattern Uygulaması

Aslında temel akış aşağıdaki gibidir.

    • Client, uygulamaya bir istek gönderir.
    • Controller, Client’tan gelen isteği karşılar.
    • Controller, isteği ilgili Request (Command veya Query) nesnesine dönüştürür.
    • Bu Request, MediatR aracılığıyla ilgili Request Handler sınıfına iletilir.
    • Handler, gerekli iş kurallarını çalıştırır ve işlemi gerçekleştirir.
    • İşlem sonucunda bir Response üretilir.
    • Controller, oluşan Response’u Client’a geri döner.

CQRS, verileri güncellemek, oluşturmak, silmek için “Command” sınıflarını, okumak için ise “Query” sınıflarını kullanmaktadır. Burada ilk önce CQRS pattern’inin tasarımını konuşacağız, sonrasında ise CQRS i MediatR kütüphanesiyle kuracağız.

Mediator Pattern

CQRS ile birlikte MediatR, Command ve Query isteklerinin doğru Handler’lara yönlendirilmesini sağlayan bir köprü görevi görür.

CQRS MediatR Kütüphanesi İle Nasıl Uygulanır?

ASP.NET Core API içerisinde MediatR Kurulumu

    1. MediatR kütüphanesini kullanabilmemiz için Package Manager Console kısmından aşağıdaki paketleri yüklememiz gerekir.
    • MediatR
    • MediatR.Extensions.Microsoft.DependencyInjection
    1. Program.cs dosyasına MediatR servisini register etmemiz gerekiyor

MediatR kütüphanesinin getirisi olan IRequest ve IRequestHandler

IRequest: Query veya Command requestlerini karşılayacak olan sınıflar tarafından implemente edilecek olan bir arayüzdür. Generic olarak bu request karşılığında hangi nesnenin döndürüleceğini bildirmemizi ister.

IRequestHandler: IRequestHandler, Command veya Query requestlerinin işlenmesini sağlayan Handler sınıfının interface idir. Bu ise bizden request ve response sınıflarının bildirilmesini ister.

CQRS ve MediatR’ı birlikte kullanmadan önce, örnek proje için oluşturduğumuz dosya yapısını inceleyelim. Aşağıdaki yapı, Command ve Query işlemlerinin ayrımını daha net görmemizi sağlayacaktır.

Commands

Create İşleminin Adım Adım Uygulanması

    1. CreateAddressesCommandRequest
    • MediatR ile çalışabilmesi için bizden IRequest<Response> arayüzünü implemente etmemizi ister.
    • Peki neden geriye <Response> dönüyoruz? bunun nedeni: IRequest<TResponse> yapısındaki generic TResponse, ilgili request’in işlendiğinde hangi tipte bir cevap döneceğini belirterek tip güvenliği ve net bir handler–response ilişkisi sağlar.
    • Yani Client, adres oluşturmak için Controller’a bir istek gönderdiğinde, CQRS yaklaşımında bu istek doğrudan işlenmez. Bunun yerine, adres oluşturma işlemi bir Command Request nesnesi içerisinde temsil edilir. Bu Request, adres oluşturmak için gerekli olan tüm verileri taşır ve MediatR aracılığıyla ilgili Handler’a iletilir.

2. CreateAddressesCommandResponse

    • Bu sınıf geriye mesaj döndürmek için oluşturulur. Örneğin Client başarılı bir şekilde adres ekledikten sonra handler sınıfımın içerisinde geriye aşağıdaki gibi bir mesaj döndürmek için.

3.CreateAddressCommandHandler

    • CreateAddressCommandHandler, adres oluşturma işleminin tüm iş mantığını barındıran sınıftır.
    • Controller’dan gelen CreateAddressCommandRequest, MediatR aracılığıyla bu handler’a iletilir ve adresin veritabanına eklenmesi işlemi burada gerçekleşir

4.CreateAddressController

Bu noktada, oluşturduğumuz Command yapısını Controller üzerinden tetikliyoruz. Aslında Controller’ın görevi iş mantığını bilmek değil, sadece gelen isteği alıp MediatR aracılığıyla ilgili Command’a iletmektir.

Update İşleminin Adım Adım Uygulanması

    1. UpdateAddressesCommandRequest

    • Bu sınıf, hangi adresin güncelleneceğini belirtmek için AddressId bilgisini ve adresin yeni değerlerini içeren alanları barındırır.
    • Yani kullanıcı, adres bilgilerini güncellemek istediğinde yeni değerleri girer. Bu değerler UpdateAddressCommandRequest içerisinde tanımlanan alanlara atanır. Controller, bu Command nesnesini alarak MediatR aracılığıyla ilgili Handler’a iletir ve güncelleme işlemi burada gerçekleştirilir.

2.UpdateAddressesCommandResponse

    • Bu sınıf geriye mesaj döndürmek için oluşturulur. Örneğin Client başarılı bir şekilde adresini güncelledikten sonra handler sınıfımın içerisinde geriye aşağıdaki gibi bir mesaj döndürmek için.

3.UpdateAddressesCommandHandler

    • UpdateAddressesCommandHandler, mevcut bir adres bilgisinin güncellenmesiyle ilgili tüm iş mantığını bu sınıfta barındırır.
    • Controller’dan gelen UpdateAddressCommandRequest, MediatR aracılığıyla bu handler’a iletilir ve adresin veritabanındaki güncel hali burada oluşturulur.

4.UpdateAddressController

Bu noktada, güncellediğimiz Command yapısını Controller üzerinden tetikliyoruz. Aslında Controller’ın görevi iş mantığını bilmek değil, sadece gelen isteği alıp MediatR aracılığıyla ilgili Command’a iletmektir.

Delete İşleminin Adım Adım Uygulanması

1.DeleteAddressesCommandRequest

    • Sistemde mevcut olan bir adresin silinmesi işlemlerini temsil eden Command nesnesidir.

2.DeleteAddressesCommandResponse

3.DeleteAddressesCommandHander

DeleteAddressesCommandHandler, sistemde mevcut olan bir adresin silinmesiyle ilgili tüm iş mantığını barındıran sınıftır.

4.DeleteAddressController

Bu kısımda DeleteAddress(int addressId) parametresini kullanmamızın nedeni, Client’ın silmek istediği adresin AddressId bilgisini Controller’a iletmesidir. Controller, bu değeri alarak bir DeleteAddressCommandRequest oluşturur ve _mediator.Send ile ilgili Handler’a gönderir. Handler ise bu isteği işleyerek adresi veritabanından siler.

Queries

Get All Adress İşlemlerinin Adım Adım Uygulanması

1.GetAllAddressQueryRequest

GetAllAddressQueryRequest sınıfının boş olmasının nedeni, tüm adresleri listelemek için ekstra bir bilgiye ihtiyaç olmamasıdır.

2.GetAllAddressQueryResponse

GetAllAddressQueryResponse, tüm adresleri listeleme işlemi sonucunda Client’a dönecek olan veri modelini temsil eder. Yani Client’a hangi bilgilerin dönmesini istiyorsak, bu alanları Response sınıfı içerisinde tanımlarız. Böylece listeleme işlemi sırasında yalnızca ihtiyaç duyulan veriler Client’a iletilmiş olur.

3.GetAllAddressQueryHandler

GetAllAddressQueryHandler, sistemde kayıtlı olan tüm adresleri listelemek için kullanılan Query Handler sınıfıdır. Controller’dan gelen GetAllAddressQueryHandler, MediatR aracılığıyla bu handler’a iletilir ve adreslerin veritabanında okunması işlemini gerçekleştirir

4.GetAllAddressController

GetById Adress İşlemlerinin Adım Adım Uygulanması

1.GeyByIdAddressQueryRequest

    • Bu sefer bu sınıfta AddressId tanımlamamız gerekiyor. Çünkü Client, bu istekte belirli bir adresi Id bilgisine göre getirmek ister. Hangi adresin getirileceğini belirtmek için bu bilgi QueryRequest içerisinde taşınır.

2. GetByIdAddressQueryResponse

3. GetByIdAddressQueryHandler

GetByIdAddressQueryHandler, sistemde kayıtlı olan Id ye göre adresleri listelemek için kullanılan Query Handler sınıfıdır. Controller’dan gelen GetByIdAddressQueryHandler, MediatR aracılığıyla bu handler’a iletilir ve adreslerin veritabanında okunması işlemini gerçekleştirir

4.GetByIdAddressController

Sonuç

Bu yazıda CQRS ve Mediator pattern’lerinin ASP.NET Core uygulamasında kullanımını inceledik.

Basit bir monolith’den daha büyük bir uygulamaya evrilecek olan uygulamalar için MediatR güzel bir başlangıç noktası sunuyor. Bize okuma ve yazma işlemlerini ayırmamızı, kodlar arasındaki bağımlılıkları minimuma indirmemizi sağlıyor.

proje github repo: https://github.com/Bilalmrgn/ETicaretWebsiteFinalProject