CQRS ve Event Sourcing Diyagramı: Komutlar, Olaylar ve Okuma Modelleri

CQRS ve event sourcing diyagramla anlatılıyor: komut tarafı, yalnızca ekleme yapılan olay deposu, projektörler, okuma modelleri, yeniden oynatma ve sürümleme.

· Kurucu ve geliştirici

Yayımlandı:

CQRS ve Event Sourcing Diyagramı: Komutlar, Olaylar ve Okuma Modelleri

Çoğu uygulama aynı tabloları aynı model üzerinden okur ve yazar. Bu, şekiller ayrışana kadar işe yarar: yazmalar tek seferde tek bir sipariş üzerinde katı iş kuralları ister, okumalar ise binlerce sipariş üzerinden listeler, aramalar ve toplamlar ister. CQRS ikisini ayırır; event sourcing ise en baştan neyi sakladığınızı değiştirir.

Çoğu zaman bir çift olarak sunulurlar ama ayrı kararlardır. Bu yazı her birini, birbirleriyle nasıl uyduklarını, ikisini birleştiren bir sistemin diyagramının neye benzediğini ve birçok ekibin onları erken benimsediğine pişman olmasına yol açan maliyetleri anlatıyor.

Tek paragrafta CQRS

Command Query Responsibility Segregation, veriyi değiştirmek için bir model, okumak için başka bir model kullanmak demektir. Komutlar (PlaceOrder, CancelOrder) kuralları uygulayan yazma tarafına gider. Sorgular, onlara ihtiyaç duyan ekranlara göre şekillendirilmiş okuma tarafına gider. Deseni Greg Young'a atfeden Martin Fowler'ın CQRS yazısı, tekrar etmeye değer bir uyarı da içerir: çoğu sistem için CQRS riskli bir karmaşıklık ekler. Onu bütün uygulamaya değil, okumaların ve yazmaların gerçekten ayrıştığı yerlere uygulayın.

CQRS iki veritabanı gerektirmez. Bir yazma modeli ve aynı tablolar üzerinde bir dizi SQL view zaten CQRS'tir. Ayrı depolar daha sonra, okuma yükü ya da okuma şekilleri bunu gerektirdiğinde gelir.

Tek paragrafta event sourcing

Event sourcing mevcut durumu değil, değişiklikleri saklar. Bir sipariş, durum sütunu olan bir satır değildir; olaylardan oluşan bir akıştır: OrderPlaced, PaymentAuthorized, OrderShipped. Mevcut durum, bunları yeniden oynattığınızda elde ettiğiniz şeydir. Fowler'ın event sourcing yazısı fikri ayrıntılı anlatır. Hiçbir şeyin üzerine yazılmadığı için tam bir denetim geçmişi bedavaya gelir ve geçmişin yeni görünümlerini istediğiniz zaman oluşturabilirsiniz.

Birbirlerine nasıl uyarlar

Event sourcing neredeyse CQRS'i zorunlu kılar. Olayları yeniden oynatmak, tek bir siparişi değiştirmeden önce yüklemenin iyi bir yoludur, ama "100 doların üzerindeki tüm açık siparişleri göster" sorusunu yanıtlamanın berbat bir yoludur. Bu yüzden yazma tarafı olay akışlarıyla çalışır, okuma tarafı ise bu olaylardan güncellenen ayrı okuma modelleri tutar.

Akış şöyle işler:

  1. Komut tarafına bir komut gelir.
  2. İşleyici, aggregate'i (siparişi) akışını yeniden oynatarak yükler, iş kurallarını kontrol eder ve hangi yeni olayları üreteceğine karar verir.
  3. Olayları, akışın beklenen sürümüyle birlikte olay deposuna ekler. Bu arada başka bir komut ekleme yaptıysa ekleme başarısız olur ve komut yeniden denenir. Bu iyimser eşzamanlılık kontrolüdür ve satır kilitlerinin yerini alır.
  4. Projektörler olay akışına abone olur ve okuma modellerini günceller: sipariş listesi için bir tablo, bir arama dizini, bir pano için sayaçlar.
  5. Sorgular yalnızca okuma modellerinden okur.

Olay deposu

Bir olay deposunun az özelliğe ihtiyacı vardır, ama bunların tam olarak doğru çalışması gerekir:

  • Yalnızca ekleme yapılan akışlar, aggregate başına bir tane ve her olay için bir sürüm numarasıyla.
  • Beklenen sürüme göre koşullu ekleme, atomik olarak.
  • Bir akışı okuma, baştan ya da belirli bir sürümden itibaren.
  • Abonelikler: tüm yeni olaylara sırayla, bir projektörün saklayıp kaldığı yerden devam edebileceği bir konumla.

Özel olay depoları bunu sağlar; akış kimliği ve sürüm üzerinde benzersizlik kısıtı olan ilişkisel bir tablo da, abonelikler için bir outbox ya da değişiklik akışıyla birlikte aynı işi görür.

Okuma modelleri ve nihai tutarlılık

Projeksiyonlar olay kaydedildikten sonra çalışır, bu yüzden okuma modelleri milisaniyeler, birikme anlarında ise çok daha uzun süreler geriden gelir. Tasarımı buna göre yapın:

  • Bir komuttan sonra yeni sürümü döndürün ve istemcinin okuma modeli yetişene kadar beklemesine izin verin ya da sonucu komut yanıtından gösterin.
  • Her projektöre kendi konumunu verin; böylece yavaş bir arama dizini sipariş listesini geciktirmez.
  • Projeksiyonları idempotent yapın. Projektörler yeniden başlar ve olayları yeniden okur; OrderShipped olayını iki kez uygulamak iki gönderi saymamalıdır.

Karşılığında okuma modelleri atılabilir hale gelir. Yeni bir ekran yeni bir şekil mi istiyor: bir projektör yazın ve geçmişi ona yeniden oynatın. Bir projeksiyon hatası mı var: düzeltin, okuma modelini silin ve yeniden oluşturun.

Somut bir örnek: yeni bir rapor

Lansmandan bir ay sonra finans ekibi, geçmiş dahil olmak üzere ürün kategorisi başına günlük gelir ister. CRUD bir sistemde bir migration ve bir backfill betiği yazarsınız; değişiklikten önceki geçmiş ise çoktan kaybolmuş olabilir. Event sourcing ile OrderPlaced ve OrderRefunded olaylarını dinleyen bir projektör yazar, onu olay logunun başına yönlendirir ve yeniden oynatmasına izin verirsiniz. Yetiştiğinde rapor lansmandan bu yana her siparişi kapsar ve o andan itibaren kendini güncel tutar.

Mimari diyagram

Bir kullanıcı komut tarafına PlaceOrder komutu gönderir; komut tarafı olay deposuna olay ekler. Olaylar bir akış üzerinden projektörlere gider ve üç okuma modelini günceller: sipariş listesi, arama dizini ve günlük toplamlar. Sorgular yalnızca bu modelleri okuyan sorgu tarafına gider.

10 bileşen · 11 bağlantı

Archboard'da aç
Diyagram içeriği
  • User
  • Command side
  • Event store
  • Event stream
  • Projectors
  • Read model: order list
  • Read model: search index
  • Read model: daily totals
  • Query side
  • PlaceOrder
  • append
  • subscription
  • GET /orders
Event sourcing ile CQRS. Komutlar bir aggregate üzerinde kontrol edilir ve yalnızca ekleme yapılan bir olay deposuna olay olarak yazılır; projektörler olay akışını birkaç okuma modeline dönüştürür ve sorgular yalnızca bu modelleri okur.

Nasıl okunur:

  • Üst sıra, yazma yolu: kullanıcının PlaceOrder komutu komut tarafına gider; komut tarafı olay deposuna olay ekler. Bir abonelik olay akışını besler.
  • Sağ: projektörler akışı tüketir ve üç okuma modelini güncel tutar; her biri kendi sorgularına uyan depolamada durur.
  • Sol alt, okuma yolu: sorgular, yalnızca okuma modellerini okuyan ve hiçbir iş kuralı içermeyen sorgu tarafına gider.
  • Markdown bloğu sürümleriyle birlikte tek bir akışı gösterir; bu da beklenen sürüm kontrolünü anlatmayı kolaylaştırır.

Maliyetler

  • Şema evrimi hiç bitmez. Olaylar sonsuza kadar saklanır, bu yüzden eski olay sürümleri okunabilir kalmalıdır. Upcasting'i (eski olayları okurken güncel şekle dönüştürmeyi) ilk günden planlayın.
  • Veri silmek zordur. Silmeyi zorunlu kılan düzenlemeler değiştirilemez loglarla çatışır; kişisel verileri olayların dışında tutun ya da kişi başına şifreleyip anahtarı silin.
  • Uzun akışların yüklenmesi yavaşlar. Birkaç yüz olayda bir alınan aggregate durumu anlık görüntüleri (snapshot) bunu çözer.
  • Hata ayıklamanın yeri değişir. Hatalar yanlış okuma modelleri olarak ortaya çıkar ve düzeltme bir yeniden oynatmadır; bunun için araç gerekir.
  • Ekip becerileri. Olaylarla modelleme farklı bir düşünme biçimidir. Event sourcing kullanan tek bir çekirdek alan fazlasıyla yeterlidir; sistemin geri kalanı CRUD olarak kalabilir.

Diyagrama ne konmalı

  • Komutlar ve sorgular için açıkça ayrılmış iki yol.
  • Doğruluğun kaynağı olarak olay deposu, türetilmiş olarak çizilmiş okuma modelleriyle birlikte.
  • Her projektör ve okuma modeli; böylece okuyanlar hangi görünümlerin geride kalabileceğini görür.
  • Sürümleriyle örnek bir akış; tartışmayı somutlaştırmak için.

Olay ve tüketici mekanikleri olay güdümlü mimari yazısında ele alınıyor; transactional outbox deseni ise özel bir olay deposu kullanmadığınızda olayları nasıl güvenilir biçimde yayınlayacağınızı açıklıyor. Kafka mimari diyagramı akışın kendisini ele alıyor.

Archboard'da oluşturun

  1. Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
  2. C4 Modeli paketinden bir Kişi ve komut tarafı, projektörler ve sorgu tarafı için Konteyner düğümleri ekleyin.
  3. Olay deposu için bir Veritabanı, okuma modelleri için Veritabanı düğümleri ve olay akışı ile sayaçlar için Veri paketinden Kafka ve Redis ekleyin.
  4. Yazma yolunu üste, okuma yolunu alta çizin; projektörler okuma modellerini sağdan beslesin.
  5. Örnek bir olay akışıyla Temel paketinden bir Markdown bloğu ekleyin.

Örnek 10 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor.

SSS

CQRS ve event sourcing birlikte mi kullanılmak zorunda? Hayır. CQRS normal bir veritabanıyla çalışır, event sourcing de tek bir okuma modelini besleyebilir. Yine de hiçbir CQRS biçimi olmadan event sourcing nadirdir, çünkü yeniden oynatılan akışlar üzerindeki sorgular yavaştır.

CQRS'i ne zaman kullanmamalıyım? Okumalar ve yazmalar verinin aynı şeklini kullandığında; bu, CRUD ekranlarının çoğu için geçerlidir. Onu sistemin ikisinin ayrıştığı kısımlarına uygulayın.

Okuma modelleri olay deposuyla nasıl tutarlı kalır? Nihai olarak. Projektörler olayları sırayla işler ve konumlarını kaydeder; bir hatadan sonra oradan devam ederler. İstemciler kısa bir gecikmeyi tolere etmelidir.

Bir olayın yapısını sonradan nasıl değiştiririm? Olayları sürümleyin ve eski sürümleri okurken dönüştürün (upcasting). Saklanan olayları asla yerinde düzenlemeyin.

Event sourcing'de snapshot nedir? Bir aggregate'in belirli bir sürümdeki durumunun kaydedilmiş kopyasıdır; böylece onu yüklemek, tüm akış yerine snapshot'ı ve ondan sonraki olayları okumak anlamına gelir.

Ücretsiz başlayın — tek kullanıcı, kredi kartı gerekmez.

İlgili yazılar

← Blog'a dön

Bir dakikada canvas açın

Google veya GitHub ile giriş yapın. Tek koltuk ücretsiz; ekip katılınca planı yükseltin.