Skip to main content
Yazılım Geliştirme

CQRS ve Event Sourcing: Pratik Uygulama Rehberi

Mart 06, 2026 7 dk okuma 14 views Raw
Ayrıca mevcut: en
CQRS ve Event Sourcing yazılım mimari pattern'i
İçindekiler

CQRS ve Event Sourcing Nedir?

Modern yazılım sistemlerinin karmaşıklığı arttıkça, geleneksel CRUD yaklaşımları yetersiz kalmaya başlar. Özellikle yüksek trafikli, domain odaklı uygulamalarda okuma ve yazma operasyonlarının farklı gereksinimleri olduğu gerçeğiyle yüzleşmek kaçınılmazdır. İşte tam bu noktada CQRS (Command Query Responsibility Segregation) ve Event Sourcing devreye girer.

CQRS, bir uygulamanın okuma (query) ve yazma (command) taraflarını birbirinden ayıran bir mimari pattern'dir. Geleneksel yaklaşımda tek bir model hem okuma hem yazma için kullanılırken, CQRS bu iki sorumluluğu tamamen farklı modellere böler. Bu ayrım, her iki tarafın bağımsız olarak optimize edilmesine olanak tanır.

Event Sourcing ise uygulama durumunu (state) doğrudan kaydetmek yerine, duruma yol açan olayları (event) sıralı bir şekilde saklama yaklaşımıdır. Bir banka hesabını düşünün: geleneksel yöntemde sadece güncel bakiyeyi saklarsınız, Event Sourcing ile her para yatırma, çekme ve transfer işlemini bir olay olarak kaydedersiniz.

Neden CQRS Kullanmalıyız?

CQRS pattern'ini benimsemenin birçok önemli avantajı bulunmaktadır. Bu avantajları anlamak, pattern'in ne zaman ve nasıl kullanılacağına karar vermek açısından kritiktir.

Performans Optimizasyonu

Çoğu sistemde okuma operasyonları, yazma operasyonlarından çok daha fazla gerçekleşir. CQRS sayesinde okuma tarafını bağımsız olarak ölçeklendirebilir, özel denormalize edilmiş okuma modelleri oluşturabilirsiniz. Yazma tarafı ise iş kurallarına odaklanarak domain bütünlüğünü korur.

Karmaşıklık Yönetimi

Büyüyen projelerde tek bir model üzerinden hem okuma hem yazma yapmak, modelin aşırı karmaşıklaşmasına neden olur. CQRS bu karmaşıklığı doğal bir şekilde böler. Command tarafında zengin domain modelleri kullanırken, query tarafında basit DTO'lar ile çalışabilirsiniz.

Bağımsız Geliştirme ve Dağıtım

Okuma ve yazma tarafları ayrı olduğu için farklı ekipler tarafından bağımsız olarak geliştirilebilir ve dağıtılabilir. Hatta farklı veritabanları bile kullanılabilir; yazma tarafında ilişkisel bir veritabanı, okuma tarafında ise NoSQL veya arama motoru tercih edilebilir.

Event Sourcing'in Temelleri

Event Sourcing, CQRS ile birlikte sıklıkla kullanılan tamamlayıcı bir pattern'dir. Temel prensibi oldukça basittir: uygulamanın durumunu doğrudan saklamak yerine, bu duruma ulaşmak için gerçekleşen tüm olayları kronolojik sırayla saklayın.

Event Store Kavramı

Event Store, olayların saklandığı özel bir veri deposudur. Her olay değişmez (immutable) bir kayıttır ve sadece ekleme (append-only) yapılabilir. Olaylar asla güncellenmez veya silinmez. Bu yaklaşım tam bir denetim izi (audit trail) sağlar ve herhangi bir andaki sistem durumunu yeniden oluşturmanıza olanak tanır.

Event'lerin Yapısı

İyi tasarlanmış bir event aşağıdaki bilgileri içermelidir:

  • Benzersiz bir olay kimliği (Event ID)
  • Olayın ait olduğu aggregate kimliği
  • Olay tipi (örneğin OrderCreated, PaymentReceived)
  • Olay verisi (payload)
  • Zaman damgası
  • Versiyon numarası

Bu yapı, olayların sıralı ve tutarlı bir şekilde işlenmesini garanti eder. Versiyon numarası özellikle eşzamanlılık kontrolü (concurrency control) için kritik öneme sahiptir.

.NET ile CQRS Implementasyonu

CQRS pattern'ini .NET ekosisteminde uygulamanın en popüler yolu MediatR kütüphanesini kullanmaktır. MediatR, mediator pattern'ini uygulayarak command ve query nesnelerini ilgili handler'lara yönlendirir.

Command Tarafının Tasarımı

Command'lar sistemde bir değişiklik yapmak isteyen niyetleri temsil eder. Her command bir handler tarafından işlenir ve genellikle bir sonuç döndürmez veya sadece başarı/başarısızlık bilgisi döndürür. Bir sipariş oluşturma command'ı müşteri bilgilerini, ürün listesini ve teslimat adresini içerir. Handler ise bu bilgileri doğrular, iş kurallarını uygular ve domain olaylarını üretir.

Command handler'larında dikkat edilmesi gereken en önemli nokta, tek bir sorumluluk ilkesine bağlı kalmaktır. Her handler yalnızca bir command türünü işlemeli ve yan etkilerini açıkça belirtmelidir.

Query Tarafının Tasarımı

Query'ler sistemden veri okumak için kullanılır ve hiçbir yan etkisi olmamalıdır. Bu prensip, query'lerin güvenle cache'lenebilmesini ve tekrarlanabilmesini sağlar. Bir sipariş detayı query'si sipariş ID'sini parametre olarak alır ve tüm gerekli bilgileri içeren bir DTO döndürür.

Query tarafında performans optimizasyonu için denormalize edilmiş okuma modelleri kullanılabilir. Bu modeller doğrudan veritabanı view'larından veya özel olarak tasarlanmış tablolardan beslenebilir.

MediatR ile Pipeline Behavior'lar

MediatR'ın en güçlü özelliklerinden biri pipeline behavior'lardır. Bu yapılar, her command veya query işlenmeden önce ve sonra çalışan ara katmanlar oluşturmanıza olanak tanır.

Validation Behavior

FluentValidation ile entegre bir validation behavior, her command işlenmeden önce otomatik doğrulama yapabilir. Bu sayede handler içinde ayrıca doğrulama kodu yazmanıza gerek kalmaz. Validator sınıfları command'a özel kurallar tanımlar ve pipeline behavior bu kuralları otomatik olarak çalıştırır.

Logging ve Performance Behavior

Her request'in işlenme süresini ölçen bir performance behavior, yavaş sorguları otomatik olarak tespit edip loglayabilir. Bu yaklaşım, üretim ortamında performans sorunlarını proaktif olarak belirlemenize yardımcı olur.

  • Validation behavior ile giriş verilerini otomatik doğrulama
  • Logging behavior ile tüm operasyonları kayıt altına alma
  • Performance behavior ile yavaş sorguları tespit etme
  • Transaction behavior ile veritabanı işlemlerini yönetme
  • Caching behavior ile okuma performansını artırma

Event Sourcing Implementasyonu

Event Sourcing'i .NET'te uygulamak için öncelikle sağlam bir Event Store altyapısı oluşturmanız gerekir. Bu altyapının temel bileşenleri event'lerin seri hale getirilmesi (serialization), saklanması ve geri yüklenmesi (replay) işlemleridir.

Aggregate Root ve Event'ler

Domain-Driven Design'daki Aggregate Root kavramı, Event Sourcing'de merkezi bir rol oynar. Her aggregate, kendisine ait olayları üretir ve bu olaylar üzerinden durumunu yeniden oluşturabilir. Bir sipariş aggregate'i oluşturulduğunda OrderCreated eventi, bir ürün eklendiğinde OrderItemAdded eventi, sipariş onaylandığında OrderConfirmed eventi üretilir.

Bu yaklaşımda aggregate'in mevcut durumu, tüm olayların sırayla uygulanmasıyla elde edilir. Bu işleme "event replay" veya "rehydration" denir.

Snapshot Mekanizması

Bir aggregate'e ait çok sayıda olay biriktiğinde, her seferinde tüm olayları tekrar oynatmak performans sorunlarına yol açabilir. Snapshot mekanizması bu sorunu çözer. Belirli aralıklarla aggregate'in mevcut durumunun bir anlık görüntüsü (snapshot) alınır ve sonraki yüklemelerde bu snapshot noktasından itibaren sadece yeni olaylar uygulanır.

Snapshot stratejisi belirlerken, aggregate başına ortalama olay sayısını ve yükleme sıklığını göz önünde bulundurun. Genellikle her 50-100 olayda bir snapshot almak iyi bir başlangıç noktasıdır.

Event Handler'lar ve Projeksiyon

Event Sourcing mimarisinde olaylar sadece saklanmaz, aynı zamanda çeşitli event handler'lar tarafından işlenir. Bu handler'lar okuma modellerini güncellemek, bildirim göndermek veya başka sistemlerle entegrasyon sağlamak gibi görevleri üstlenir.

Projeksiyon Türleri

Projeksiyonlar, event stream'lerinden okuma modelleri oluşturma sürecidir. İki temel projeksiyon türü vardır:

  1. Senkron projeksiyonlar: Event kaydedildiği anda okuma modeli güncellenir. Tutarlılık garantisi yüksektir ancak yazma performansını etkileyebilir.
  2. Asenkron projeksiyonlar: Event'ler bir kuyruk veya stream üzerinden asenkron olarak işlenir. Eventual consistency modeli geçerlidir ancak yazma performansı etkilenmez.

Hangi projeksiyon türünün kullanılacağı, uygulamanın tutarlılık gereksinimlerine bağlıdır. Finansal işlemler gibi kritik senaryolarda senkron projeksiyon tercih edilirken, raporlama gibi alanlarda asenkron projeksiyon yeterli olabilir.

CQRS ve Event Sourcing Uygulama Stratejileri

Bu pattern'leri başarılı bir şekilde uygulamak için dikkat edilmesi gereken önemli stratejiler bulunmaktadır.

Kademeli Geçiş

Mevcut bir sisteme CQRS uygulamak için büyük bir yeniden yazım yerine kademeli bir geçiş stratejisi izlemeniz önerilir. Önce en karmaşık veya en çok performans sorunu yaşayan bölümlerden başlayın. Tüm sistemi CQRS'e taşımak zorunlu değildir; hibrit bir yaklaşım çoğu durumda en pragmatik çözümdür.

Olay Versiyonlama

Zamanla event yapıları değişebilir. Bu durumu yönetmek için olay versiyonlama stratejisi belirlemeniz gerekir. Yeni alanlar eklendiğinde varsayılan değerler kullanmak, yapı tamamen değiştiğinde ise event upcaster'lar oluşturmak yaygın yaklaşımlardır.

Test Stratejisi

CQRS ve Event Sourcing, test edilebilirliği doğal olarak artırır. Command handler'lar belirli girdiler için belirli event'ler üretmelidir, bu da birim testleri son derece öngörülebilir kılar. Given-When-Then formatında testler yazarak senaryolarınızı açık ve anlaşılır hale getirebilirsiniz.

  • Given: Belirli olaylar gerçekleşmiş durumda
  • When: Bir command gönderildiğinde
  • Then: Beklenen olaylar üretilmeli veya hata fırlatılmalı

Sık Karşılaşılan Zorluklar ve Çözümler

CQRS ve Event Sourcing güçlü pattern'ler olmakla birlikte, bazı zorlukları da beraberinde getirir. Bu zorlukları önceden bilmek, daha sağlam bir mimari tasarlamanıza yardımcı olur.

Eventual Consistency

CQRS'te okuma ve yazma modelleri ayrı olduğu için, yazma sonrası okuma modelinin güncellenmesi zaman alabilir. Kullanıcı arayüzünde bu durumu yönetmek için iyimser güncelleme (optimistic update) stratejileri uygulanabilir. Kullanıcıya işlemin başarıyla alındığını gösterirken arka planda okuma modelinin güncellenmesini bekleyebilirsiniz.

Idempotency

Dağıtık sistemlerde mesajlar birden fazla kez iletilebilir. Event handler'larınızın idempotent olması, yani aynı event'i birden fazla kez işlese bile aynı sonucu üretmesi kritik öneme sahiptir. Her event'e benzersiz bir kimlik atayarak ve işlenmiş event'leri takip ederek idempotency sağlanabilir.

Karmaşıklık Dengesi

CQRS ve Event Sourcing her proje için uygun değildir. Basit CRUD uygulamalarında bu pattern'leri uygulamak gereksiz karmaşıklık yaratır. Bu pattern'ler özellikle karmaşık iş kuralları olan, yüksek ölçeklenebilirlik gerektiren ve tam denetim izine ihtiyaç duyan sistemlerde en büyük faydayı sağlar.

CQRS ve Event Sourcing'i bir araç olarak görün, her soruna uygulanan evrensel bir çözüm olarak değil. Doğru bağlamda kullanıldığında muazzam faydalar sağlarken, yanlış bağlamda gereksiz karmaşıklığa yol açabilir.

Sonuç

CQRS ve Event Sourcing, modern yazılım mimarisinde güçlü ve esnek sistemler tasarlamanın temel yapı taşlarıdır. MediatR gibi araçlarla .NET ekosisteminde bu pattern'leri uygulamak oldukça pratik hale gelmiştir. Önemli olan, bu pattern'leri dogmatik bir şekilde her yerde uygulamak yerine, gerçekten ihtiyaç duyulan yerlerde bilinçli bir tercih olarak kullanmaktır.

Başarılı bir implementasyon için kademeli geçiş stratejisi izleyin, kapsamlı testler yazın ve eventual consistency gibi zorlukları önceden planlayın. Bu sayede hem geliştirme sürecinizi iyileştirecek hem de ölçeklenebilir, sürdürülebilir sistemler inşa edeceksiniz.

Bu yazıyı paylaş