Penanganan konkurensi ES / CQRS

20

Baru-baru ini saya mulai terjun ke CQRS / ES karena saya mungkin perlu menerapkannya di tempat kerja. Tampaknya sangat menjanjikan dalam kasus kami, karena akan menyelesaikan banyak masalah.

Saya membuat sketsa pemahaman kasar saya tentang bagaimana aplikasi ES / CQRS akan terlihat seperti dikontekstualisasikan ke kasus penggunaan perbankan yang disederhanakan (menarik uang).

ES / CQRS

Singkatnya, jika orang A menarik uang:

  • perintah dikeluarkan
  • perintah diserahkan untuk validasi / verifikasi
  • suatu acara didorong ke toko peristiwa jika validasi berhasil
  • agregator mengumumkan acara untuk menerapkan modifikasi pada agregat

Dari apa yang saya mengerti, log peristiwa adalah sumber kebenaran, karena ini adalah log FACTS, kemudian kita dapat memperoleh proyeksi apa pun darinya.


Sekarang, apa yang saya tidak mengerti, dalam skema besar ini, adalah apa yang terjadi dalam kasus ini:

  • aturan: keseimbangan tidak boleh negatif
  • orang A memiliki saldo 100e
  • person A mengeluarkan Perintah Penarikan dari 100e
  • melewati validasi dan acara MoneyWithdrewEvent of 100e dipancarkan
  • Sementara itu, orang A mengeluarkan Perintah Penarikan lain dari 100e
  • MoneyWithdrewEvent pertama belum diagregasi, oleh karena itu validasi lolos, karena pemeriksaan validasi terhadap agregat (yang belum diperbarui)
  • MoneyWithdrewEvent of 100e dipancarkan di lain waktu

==> Kami berada dalam kondisi keseimbangan yang tidak konsisten pada -100e dan log berisi 2 MoneyWithdrewEvent

Seperti yang saya pahami ada beberapa strategi untuk mengatasi masalah ini:

  • a) menempatkan id versi agregat bersama dengan acara di toko acara sehingga jika ada ketidakcocokan versi saat modifikasi, tidak ada yang terjadi
  • b) menggunakan beberapa strategi penguncian, menyiratkan bahwa lapisan verifikasi harus entah bagaimana membuatnya

Pertanyaan yang terkait dengan strategi:

  • a) Dalam hal ini, log peristiwa bukan lagi sumber kebenaran, bagaimana menghadapinya? Juga, kami kembali ke klien, OK, padahal benar-benar salah untuk mengizinkan penarikan, apakah lebih baik dalam hal ini menggunakan kunci?
  • b) Kunci == kebuntuan, apakah Anda memiliki wawasan tentang praktik terbaik?

Secara keseluruhan, apakah pemahaman saya benar tentang cara menangani konkurensi?

Catatan: Saya mengerti bahwa orang yang sama menarik dua kali uang dalam waktu sesingkat itu tidak mungkin, tetapi saya mengambil contoh sederhana, tidak tersesat dalam detail

Louis F.
sumber
Mengapa tidak memperbarui agregat di langkah 4 alih-alih menunggu sampai langkah 7?
Erik Eidt
Jadi maksud Anda, dalam hal ini, toko acara hanyalah log yang hanya dapat dibaca saat memulai aplikasi untuk membuat ulang agregat / proyeksi lainnya?
Louis F.

Jawaban:

19

Saya membuat sketsa pemahaman kasar saya tentang bagaimana aplikasi ES / CQRS akan terlihat seperti dikontekstualisasikan ke kasus penggunaan perbankan yang disederhanakan (menarik uang).

Ini adalah contoh sempurna dari aplikasi yang bersumber dari acara. Ayo mulai.

Setiap kali perintah diproses atau dicoba (Anda akan mengerti, bersabar) langkah-langkah berikut dilakukan:

  1. perintah mencapai penangan perintah, yaitu layanan di Application layer.
  2. penangan perintah mengidentifikasi Aggregatedan memuatnya dari repositori (dalam hal ini pemuatan dilakukan dengan new-ing sebuah Aggregateinstance, mengambil semua peristiwa yang dipancarkan sebelumnya dari agregat ini dan menerapkannya kembali ke Agregat itu sendiri; versi Agregat disimpan untuk digunakan nanti; setelah peristiwa diterapkan, Agregat berada dalam keadaan akhir - yaitu saldo akun berjalan dihitung sebagai angka)
  3. penangan perintah memanggil metode yang sesuai pada Aggregate, suka Account::withdrawMoney(100)dan mengumpulkan peristiwa yang dihasilkan, yaitu MoneyWithdrewEvent(AccountId, 100); jika tidak ada cukup uang dalam akun (saldo <100) maka Pengecualian dinaikkan dan semua dibatalkan; jika tidak, langkah selanjutnya dilakukan.
  4. penangan perintah mencoba untuk bertahan Aggregateke repositori (dalam hal ini repositori adalah Event Store); itu melakukannya dengan menambahkan peristiwa baru ke Event streamjika dan hanya jika versiondari Aggregatemasih salah satu yang adalah ketika Aggregatedimuat. Jika versinya tidak sama, maka perintah itu dicoba lagi - lanjutkan ke langkah 1 . Jika versionsama, maka acara ditambahkan ke Event streamdan klien diberikan Successstatus.

Pemeriksaan versi ini disebut penguncian optimis dan merupakan mekanisme penguncian umum. Satu mekanisme lainnya adalah penguncian pesimistis ketika tulisan-tulisan lain diblokir (seperti tidak dimulai) sampai yang sekarang selesai.

Istilah Event streamini adalah abstraksi di sekitar semua peristiwa yang dipancarkan oleh Agregat yang sama.

Anda harus memahami bahwa Event storeini hanyalah jenis kegigihan lainnya di mana disimpan semua perubahan pada Agregat, bukan hanya kondisi akhir.

a) Dalam hal ini, log peristiwa bukan lagi sumber kebenaran, bagaimana menghadapinya? Juga, kami kembali ke klien, OK, padahal benar-benar salah untuk mengizinkan penarikan, apakah lebih baik dalam hal ini menggunakan kunci?

Toko acara selalu menjadi sumber kebenaran.

b) Kunci == kebuntuan, apakah Anda memiliki wawasan tentang praktik terbaik?

Dengan menggunakan penguncian yang optimis Anda tidak memiliki kunci, cukup perintah coba lagi.

Pokoknya, Kunci! = Deadlock

Constantin Galbenu
sumber
2
Ada beberapa optimasi terkait pemuatan suatu Aggregatetempat di mana Anda tidak menerapkan semua peristiwa tetapi Anda menyimpan snapshot Aggregatehingga titik di masa lalu dan hanya menerapkan peristiwa yang terjadi setelah titik itu.
Constantin Galbenu
Ok saya pikir kebingungan saya berasal dari fakta bahwa event store == event bus (karena saya punya kafka) karena itu membangun kembali agregat mungkin mahal karena Anda mungkin perlu membaca kembali BANYAK peristiwa. Dalam hal memiliki snapshot Aggregate, kapan snapshot harus diperbarui? Apakah snapshot store sama dengan event store atau apakah itu tampilan material yang diturunkan dari bus peristiwa?
Louis F.
Ada beberapa strategi tentang membuat snapshot. Salah satunya adalah membuat snapshot setiap n peristiwa. Anda harus menyimpan snapshot bersama dengan peristiwa, di tempat yang sama / kegigihan / database, pada komit yang sama. Idenya adalah bahwa snapshot sangat terkait dengan versi Agregat.
Constantin Galbenu
Ok, saya pikir saya memiliki visi yang lebih jelas tentang cara menangani ini. Sekarang pertanyaan terakhir, apa peran bus acara pada akhirnya? Jika agregat diperbarui secara sinkron?
Louis F.
1
Ya, Anda bisa menggunakan RabbitMQ atau saluran apa pun yang ingin Anda kirimkan acara ke model yang sudah dibaca secara tidak sinkron tetapi hanya setelah Anda bertahan di toko acara. Memang, tidak ada validasi peristiwa yang dilakukan setelah mereka bertahan: peristiwa tersebut mewakili fakta yang terjadi; model baca mungkin atau mungkin tidak suka bahwa sesuatu telah terjadi tetapi tidak dapat mengubah sejarah.
Constantin Galbenu
1

Saya membuat sketsa pemahaman kasar saya tentang bagaimana aplikasi ES / CQRS akan terlihat seperti dikontekstualisasikan ke kasus penggunaan perbankan yang disederhanakan (menarik uang).

Menutup. Masalahnya adalah bahwa logika untuk memperbarui "agregat" Anda ada di tempat yang aneh.

Implementasi yang lebih umum adalah bahwa model data yang disimpan oleh pengendali perintah Anda dalam memori, dan aliran peristiwa di penyimpanan acara tetap disinkronkan.

Contoh mudah untuk dijelaskan adalah kasus di mana penangan perintah membuat menulis sinkron ke toko acara, dan memperbarui salinan lokal dari model jika koneksi ke toko acara menunjukkan bahwa penulisan berhasil.

Jika penangan perintah perlu melakukan sinkronisasi ulang dengan event store (karena model internalnya tidak sesuai dengan store), ia melakukannya dengan memuat histori dari store dan membangun kembali status internalnya sendiri.

Dengan kata lain, panah 2 & 3 (jika ada) biasanya akan terhubung ke toko peristiwa, bukan ke toko agregat.

letakkan id versi agregat bersama dengan acara di toko acara jadi jika ada ketidakcocokan versi saat modifikasi, tidak ada yang terjadi

Variasi dari ini adalah kasus yang biasa - daripada menambahkan ke aliran dalam aliran peristiwa, kami biasanya PUT ke lokasi tertentu di aliran; jika operasi itu tidak sesuai dengan keadaan toko, penulisan gagal, dan layanan dapat memilih mode kegagalan yang sesuai (gagal untuk klien, coba lagi, gabungkan ....). Menggunakan idempoten menulis memecahkan sejumlah masalah dalam pengiriman pesan terdistribusi, tetapi tentu saja itu memerlukan toko yang mendukung penulisan idempoten.

VoiceOfUnreason
sumber
Hmm saya pikir saya salah paham komponen event store itu. Saya pikir semuanya harus melaluinya dan mengalir. Bagaimana jika toko acara saya adalah kafka dan hanya bisa dibaca? Saya tidak mampu melakukan langkah 2 dan 3 untuk membaca semua pesan lagi. Tampaknya secara keseluruhan visi saya cocok dengan yang ini: medium.com/technology-learning/…
Louis F.