Event Sourcing ve CQRS uygulaması: komuttan projeksiyona akış
Beceri rehberi

Event Sourcing ve CQRS uygulaması: komuttan projeksiyona akış

Hazırlayan: Mindset Institute EditörlüğüGüncellendi: 6 dk okuma

Event Sourcing ve CQRS uygulama rehberi: olay deposu, komut ve sorgu ayrımı, projeksiyonlar, nihai tutarlılık, anlık görüntü ve sık yapılan tasarım hataları.

Tek fikir, farklı desenler: durumu değil, olayları saklamak

Geleneksel uygulamada bir sipariş tablosunda yalnızca son durum durur: "sipariş gönderildi". Event Sourcing (olay kaynaklaması) bu yaklaşımı tersine çevirir. Sistem durumu doğrudan saklamak yerine, durumu oluşturan olayları sırayla saklar: sipariş oluşturuldu, ürün eklendi, ödeme alındı, sipariş gönderildi. Güncel durum, bu olayların baştan oynatılmasıyla elde edilir. Olaylar silinmez ve değiştirilmez; yalnızca eklenir.

CQRS (Command Query Responsibility Segregation), sistemin yazma ve okuma sorumluluklarını ayırır. Komutlar (siparişi iptal et) durumu değiştiren isteklerdir; sorgular (siparişlerimi listele) yalnızca veri okur. Yazma modeli iş kurallarını korumaya, okuma modeli ise hızlı ve amaca uygun veri sunmaya göre ayrı ayrı tasarlanır.

Bu desenler birbirinden bağımsız kullanılabilir, ancak sık birlikte uygulanır: olay deposu yazma tarafının doğruluk kaynağı olur, okuma modelleri bu olaylardan türetilir. Birlikte kullanım güçlüdür, fakat tasarımsal ve operasyonel maliyeti de büyüktür; bu yüzden her sisteme uygun değildir.

Ne zaman uygun, ne zaman gereksiz?

Event Sourcing şu ihtiyaçlar olduğunda anlam kazanır: • Değişikliklerin tam geçmişi iş gereği önemlidir: finansal hareketler, sözleşme süreçleri, denetime tabi kayıtlar. • Geçmiş bir tarihteki durumu yeniden kurmak, "o gün sistem nasıl görüyordu?" sorusunu yanıtlamak gerekir. • Yeni bir raporlama ya da görünüm sonradan çıkarsa, eski olaylardan yeniden üretilebilmesi istenir. • Sistem olay güdümlü ve diğer servisler bu olayları zaten tüketiyordur.

CQRS ise okuma ve yazma yükleri çok farklı olduğunda, okuma tarafında farklı veri şekilleri gerektiğinde ya da karmaşık iş kuralları sorgu ihtiyaçlarıyla çatışmaya başladığında değer üretir.

Basit liste, form ve yönetim ekranı uygulamalarında ise bu desenler de gereksiz karmaşıklık getirir. Sıradan bir kayıt-düzenle-sil uygulamasını Event Sourcing ile yazmak, geliştirme hızını ve işletme kolaylığını düşürür. Karar şu soruyla verilir: tarihçe ve yeniden üretim yeteneği gerçekten iş değeri taşıyor mu?

Yazma tarafı: komut, aggregate ve olay deposu

Yazma akışı şu adımlarla işler: • Bir komut gelir (örneğin "ödemeyi onayla"). • Komut işleyici, ilgili aggregate'in (tutarlılık sınırı içindeki iş nesnesi, örneğin Sipariş) olaylarını olay deposundan okur ve bunları sırayla uygulayarak güncel durumu belleğe kurar. • Aggregate iş kurallarını kontrol eder (zaten ödenmiş mi, iptal mi edilmiş?). Kurallar geçerse yeni olay(lar) üretir; geçmezse komutu reddeder. • Yeni olaylar, aggregate'in sürüm numarası kontrol edilerek olay deposuna eklenir. Aynı anda başka bir komut aynı aggregate'i değiştirmişse sürüm uyuşmazlığı oluşur ve işlem yeniden denenir (iyimser eşzamanlılık kontrolü).

Olay deposu, olayları aggregate kimliği ve sıra numarasıyla saklayan, yalnızca ekleme kabul eden bir kayıt yapısıdır. Bunun için özel olay deposu ürünleri de, ilişkisel veritabanı üzerine kurulu tablolar da kullanılabilir; kritik olan sıralı ekleme ve sürüm kontrolüdür.

Olay çok uzadığında aggregate'i baştan kurmak yavaşlar. Bu durumda belirli aralıklarla durumun anlık görüntüsü (snapshot) alınır ve yükleme son görüntüden itibaren olaylarla yapılır. Anlık görüntü bir optimizasyondur, doğruluk kaynağı değildir.

Okuma tarafı: projeksiyonlar ve nihai tutarlılık

Olay deposu sorgu için elverişli değildir; "bu müşterinin tüm siparişlerini toplam tutara göre sırala" gibi bir sorgu için olayları baştan taramak gerekir. Bunun çözümü projeksiyonlardır: olayları dinleyen, bunlardan okuma amaçlı ayrı bir veri yapısı (tablo, arama dizini, önbellek) üreten bileşenler.

Her projeksiyon belirli bir ekranın ya da raporun ihtiyacına göre yazılır. Aynı olaylardan birden çok farklı okuma modeli çıkabilir. Yeni bir rapor gerektiğinde yeni bir projeksiyon yazılır ve eski olaylar baştan oynatılarak doldurulur; bu, Event Sourcing'in en somut avantajlarından biridir.

Bunun bedeli nihai tutarlılıktır (eventual consistency): komut başarıyla tamamlandığı anda okuma modeli henüz güncellenmemiş olabilir. Kullanıcı sipariş verip hemen listeye baktığında siparişi göremeyebilir. Bu durum arayüz tasarımında ve iş akışında hesaba katılmalıdır: işlem sonrası kullanıcıya bekleme durumu göstermek, komut sonucunu doğrudan geri döndürmek ya da kritik okumaları yazma tarafından yapmak yaygın çözümlerdir.

Projeksiyonlar aynı olayı birden çok kez alabilir; bu nedenle idempotent (tekrar uygulandığında sonucu değiştirmeyen) yazılmalıdır.

Olay tasarımı, sürümleme ve sık hatalar

Olaylar geçmişte olmuş iş gerçekleridir ve geçmiş zaman kipiyle adlandırılır: SiparişOluşturuldu, ÖdemeAlındı. Teknik değişiklik bildirimi gibi ("SiparişSatırıGüncellendi") genel adlar iş anlamı taşımaz ve olayı dar tutmaz. Olay ayrıntıları ve şema evrimi ayrı bir konudur; ilkesi şudur: olaylar silinmediği için eski sürümlerle yaşamayı baştan tasarlamak gerekir. Yeni alan eklemek geriye dönük uyumlu yapılırken, alan anlamı değişiyorsa eski olayı yeni biçime çeviren bir dönüştürücü (upcaster) kullanılır.

Sık yapılan hatalar: • Her yerde Event Sourcing kullanmak. Desen, tarihçenin değerli olduğu çekirdek alanlarda uygulanır. • Olay deposundan ilişkisel sorgu yapmaya çalışmak yerine projeksiyon kurmamak. • Olayları veritabanı satır değişikliği gibi tasarlamak ve iş anlamı taşımamak. • Kişisel verileri doğrudan olaya yazıp silinemez bir kayıt oluşturmak; silme hakkı yükümlülüklerinde sorun çıkar. Bu tür verileri olayın dışında tutmak veya şifreleyerek saklamak tasarım kararıdır ve hukuk ekibiyle birlikte alınmalıdır. • Nihai tutarlılığı kullanıcı deneyiminde hiç ele almamak. • Test etmemek. Olay akışı testi (verilen olaylar, çalışan komut, beklenen yeni olaylar) deseni doğal biçimde test edilebilir kılar ve avantaj olarak kullanılmalıdır.

Başlangıçtan ileriye öğrenme ve uygulama yolu

Desenleri öğrenirken şu sırayla ilerlemek çoğu kişi için en verimli yoldur: önce basit bir alanı (hesap bakiyesi, sepet ya da sipariş) olay listesi olarak modelleyin ve mevcut durumu olaylardan elle hesaplayın. Sonra bir komut işleyici ve olay deposu yazın. Ardından tek bir projeksiyon ekleyip nihai tutarlılığı deneyimleyin. Sonraki aşamada anlık görüntü, sürüm uyuşmazlığı yönetimi, olay şeması evrimi ve yeniden oynatma ile projeksiyon yenileme konularına geçin.

Üretime yaklaşırken izleme, geri alma ve veri yönetişimi konuları gündeme gelir: projeksiyon gecikmesi izlenir, hatalı projeksiyon silinip olaylardan yeniden üretilir, olay deposunun yedeği ve erişim yetkileri belirlenir. Mevcut bir sisteme geçiş yapılıyorsa tüm sistemi dönüştürmek yerine, bir sınırlı alandan başlayıp kademeli ilerlemek riski azaltır.

Mesaj aracısı (broker) ile birlikte kullanımda, olayların diğer servislere güvenle yayımlanması ve aynı olayın tekrar işlenmesine karşı koruma ayrıca tasarlanır.

Bu beceriyi Mindset programlarında nerede çalışırsınız?

Event-Driven Mimari eğitiminin modül başlıkları arasında Olay Güdümlü Mimariye Giriş, Olay Kaynaklaması (Event Sourcing), CQRS ile Entegrasyon, Olay Tasarımı ve Şema, Mesaj Aracısı Seçimi ve Yapılandırma, Üretici Tasarım Desenleri, Tüketici Tasarım Desenleri ile EDA Test Etme ve İşletme yer alır. Bu başlıklar yukarıdaki adımların tamamıyla örtüşür; backend geliştiriciler, yazılım mimarları ve platform ekipleri için uygundur.

Kurumsal ekipler kendi alanlarından bir süreci (ödeme, sipariş, sözleşme) çalışma örneği olarak getirip olay modeli ve projeksiyon tasarımını uygulamalı olarak çalışabilir.

İlgili Mindset programı

Event-Driven Mimari

Sık sorulanlar

Sık sorulan sorular

Event Sourcing ve CQRS mutlaka birlikte mi kullanılır?

Hayır. CQRS olay saklamadan, yalnızca okuma ve yazma modellerini ayırarak uygulanabilir. Event Sourcing de tek bir okuma modeliyle kullanılabilir. Yine de olay deposu sorgu için elverişsiz olduğundan, pratikte bu desenler sık birlikte görülür: olay deposu yazma tarafının kaynağı, projeksiyonlar okuma tarafı olur.

Olay deposu olarak ilişkisel veritabanı kullanılabilir mi?

Evet. Aggregate kimliği, sıra numarası, olay türü ve içerik alanlarıyla basit bir tablo, yalnızca ekleme kuralı ve sürüm benzersizliği kısıtıyla çalışabilir. Özel olay deposu ürünleri yeniden oynatma ve abonelik gibi hazır özellikler sağlar. Seçim ekibin tecrübesine, hacme ve işletme gereksinimlerine göre yapılır.

Eski olayların şeması değişirse ne yapılır?

Olaylar değiştirilmediği için eski sürüm olayları saklanmaya devam eder. Yeni alanlar geriye uyumlu eklenir; anlamı değişen olaylar okunurken yeni biçime çeviren bir dönüştürücü (upcaster) ile işlenir. Şema evrimi baştan planlanmazsa eski olaylar zamanla taşınması zor bir yük haline gelir.

Nihai tutarlılık kullanıcı deneyimini nasıl etkiler?

Komut tamamlandığı anda okuma modeli güncellenmemiş olabilir; kullanıcı yaptığı işlemin sonucunu hemen göremeyebilir. Bunu yönetmek için işlem sonucunu doğrudan komut yanıtında döndürmek, arayüzde bekleme durumu göstermek ya da kritik okumayı yazma tarafından yapmak gibi yöntemler kullanılır. Karar, işin tolerans düzeyine bağlıdır.

Mevcut bir uygulamaya Event Sourcing nasıl eklenir?

Tüm sistemi birden dönüştürmek yerine, tarihçenin değerli olduğu sınırlı bir alandan başlanır. Bu alanda olaylar üretilir, okuma tarafı projeksiyonlarla beslenir ve diğer alanlar mevcut yapıda kalır. Başarı kanıtlandıktan sonra kapsam genişletilir. Kademeli geçiş, risk ve öğrenme maliyetini kontrol altında tutar.

Event Sourcing ve CQRS Nasıl Uygulanır? Adım Adım