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).
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
sumber
Jawaban:
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:
Application layer
.Aggregate
dan memuatnya dari repositori (dalam hal ini pemuatan dilakukan dengannew
-ing sebuahAggregate
instance, 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)Aggregate
, sukaAccount::withdrawMoney(100)
dan mengumpulkan peristiwa yang dihasilkan, yaituMoneyWithdrewEvent(AccountId, 100)
; jika tidak ada cukup uang dalam akun (saldo <100) maka Pengecualian dinaikkan dan semua dibatalkan; jika tidak, langkah selanjutnya dilakukan.Aggregate
ke repositori (dalam hal ini repositori adalahEvent Store
); itu melakukannya dengan menambahkan peristiwa baru keEvent stream
jika dan hanya jikaversion
dariAggregate
masih salah satu yang adalah ketikaAggregate
dimuat. Jika versinya tidak sama, maka perintah itu dicoba lagi - lanjutkan ke langkah 1 . Jikaversion
sama, maka acara ditambahkan keEvent stream
dan klien diberikanSuccess
status.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 stream
ini adalah abstraksi di sekitar semua peristiwa yang dipancarkan oleh Agregat yang sama.Anda harus memahami bahwa
Event store
ini hanyalah jenis kegigihan lainnya di mana disimpan semua perubahan pada Agregat, bukan hanya kondisi akhir.Toko acara selalu menjadi sumber kebenaran.
Dengan menggunakan penguncian yang optimis Anda tidak memiliki kunci, cukup perintah coba lagi.
Pokoknya, Kunci! = Deadlock
sumber
Aggregate
tempat di mana Anda tidak menerapkan semua peristiwa tetapi Anda menyimpan snapshotAggregate
hingga titik di masa lalu dan hanya menerapkan peristiwa yang terjadi setelah titik itu.Aggregate
, kapan snapshot harus diperbarui? Apakah snapshot store sama dengan event store atau apakah itu tampilan material yang diturunkan dari bus peristiwa?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.
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.
sumber