Jadi saya sudah lama bermain-main dengan Event Sourcing dan CQRS, meskipun saya tidak pernah memiliki kesempatan untuk menerapkan pola pada proyek nyata.
Saya memahami manfaat memisahkan masalah baca dan tulis Anda, dan saya menghargai bagaimana Event Sourcing memudahkan untuk memproyeksikan perubahan status ke database "Baca Model" yang berbeda dari Toko Acara Anda.
Yang tidak saya ketahui dengan jelas adalah mengapa Anda akan merehidrasi Agregat Anda dari Event Store itu sendiri.
Jika memproyeksikan perubahan pada basis data "baca" begitu mudah, mengapa tidak selalu memproyeksikan perubahan pada basis data "tulis" yang skemanya sangat cocok dengan model domain Anda? Ini secara efektif akan menjadi basis data snapshot.
Saya membayangkan ini pasti sangat umum di aplikasi ES + CQRS di alam liar.
Jika ini masalahnya, apakah Event Store hanya berguna ketika membangun kembali database "write" Anda sebagai akibat dari perubahan skema? Atau apakah saya melewatkan sesuatu yang lebih besar?
sumber
Jawaban:
Karena "peristiwa" adalah buku catatan.
Iya; kadang-kadang optimasi kinerja yang berguna untuk menggunakan salinan cache dari status agregat, daripada membuat ulang keadaan itu dari awal setiap waktu. Ingat: aturan pertama pengoptimalan kinerja adalah "Jangan". Ini menambah kompleksitas ekstra untuk solusi, dan Anda lebih suka menghindarinya sampai Anda memiliki motivasi bisnis yang meyakinkan.
Anda melewatkan sesuatu yang lebih besar.
Poin pertama adalah bahwa jika Anda mempertimbangkan solusi yang berasal dari suatu peristiwa, itu karena Anda berharap akan ada nilai dalam melestarikan sejarah tentang apa yang telah terjadi, yang artinya Anda ingin membuat perubahan yang tidak merusak .
Jadi itu sebabnya kami menulis ke toko acara sama sekali.
Secara khusus, ini berarti bahwa setiap perubahan harus ditulis ke toko acara.
Penulis yang bersaing dapat berpotensi memusnahkan tulisan masing-masing, atau mendorong sistem ke kondisi yang tidak diinginkan, jika mereka tidak mengetahui adanya suntingan satu sama lain. Jadi pendekatan yang biasa digunakan ketika Anda membutuhkan konsistensi adalah menulis tulisan Anda pada posisi tertentu dalam jurnal (analog dengan PUT bersyarat dalam HTTP api). Tulisan yang gagal memberi tahu penulis bahwa pemahaman mereka tentang jurnal saat ini tidak sinkron, dan bahwa mereka harus pulih.
Kembali ke posisi yang dikenal baik kemudian memutar ulang peristiwa tambahan karena titik itu adalah strategi pemulihan umum. Posisi bagus yang diketahui itu bisa berupa salinan dari apa yang ada di cache lokal, atau representasi di toko snapshot Anda.
Di jalur bahagia, Anda dapat menyimpan snapshot agregat di memori; Anda hanya perlu menjangkau toko eksternal ketika tidak ada salinan lokal yang tersedia.
Selain itu, Anda tidak perlu sepenuhnya terperangkap, jika Anda memiliki akses ke buku catatan.
Jadi pendekatan yang biasa ( jika menggunakan repositori snapshot) adalah untuk mempertahankannya secara tidak sinkron . Dengan begitu, jika Anda perlu memulihkan, Anda dapat melakukannya tanpa memuat ulang dan memutar ulang seluruh riwayat agregat.
Ada banyak kasus di mana kompleksitas ini tidak menarik, karena agregat berbutir halus dengan masa hidup yang dicakup biasanya tidak mengumpulkan cukup peristiwa untuk manfaat melebihi biaya mempertahankan cache snapshot.
Tetapi ketika itu adalah alat yang tepat untuk masalah tersebut, memuat representasi basi dari agregat ke dalam model tulis, kemudian memperbaruinya dengan acara tambahan, adalah hal yang sangat masuk akal untuk dilakukan.
sumber
Karena Anda tidak menentukan apa yang akan menjadi tujuan dari database "tulis", saya akan berasumsi di sini bahwa yang Anda maksudkan adalah ini: ketika mendaftarkan pembaruan baru ke agregat, alih-alih membangun kembali agregat dari toko kejadian, Anda angkat dari database "tulis", validasi perubahan, dan keluarkan suatu peristiwa.
Jika ini yang Anda maksud, maka strategi ini akan menciptakan kondisi ketidakkonsistenan: jika pembaruan baru terjadi sebelum yang terakhir memiliki kesempatan untuk membuatnya menjadi database "tulis", pembaruan baru akan berakhir divalidasi terhadap data yang sudah usang, dengan demikian berpotensi mengeluarkan peristiwa "tidak mungkin" (yaitu "tidak diizinkan") dan merusak kondisi sistem.
Misalnya, perhatikan contoh berdiri pemesanan kursi di teater. Untuk mencegah pemesanan ganda, Anda perlu memastikan bahwa kursi yang dipesan belum diambil - inilah yang Anda sebut "validasi". Untuk melakukan itu, Anda menyimpan daftar kursi yang sudah dipesan di database "tulis". Kemudian, ketika permintaan pemesanan masuk, Anda memeriksa apakah kursi yang diminta ada dalam daftar, dan jika tidak, berikan acara "dipesan", jika tidak balas dengan pesan kesalahan. Kemudian Anda menjalankan proses proyeksi, di mana Anda mendengarkan acara "dipesan" dan menambahkan kursi dipesan ke daftar di database "tulis".
Biasanya, sistem akan berfungsi seperti ini:
Namun, bagaimana jika permintaan masuk terlalu cepat, dan langkah 5 terjadi sebelum langkah 4?
Sekarang Anda memiliki dua acara untuk memesan kursi yang sama. Status sistem rusak.
Untuk mencegah hal ini terjadi, Anda tidak boleh memvalidasi pembaruan terhadap suatu proyeksi. Untuk memvalidasi pembaruan, Anda membangun kembali agregat dari toko acara, lalu memvalidasi pembaruan yang menentangnya. Setelah itu, Anda mengeluarkan acara, tetapi gunakan penjaga stempel waktu untuk memastikan bahwa tidak ada acara baru yang dikeluarkan sejak Anda terakhir membaca dari toko. Jika gagal, Anda hanya perlu mencoba lagi.
Membangun kembali agregat dari toko acara mungkin membawa penalti kinerja. Untuk mengurangi ini, Anda dapat menyimpan snapshot agregat tepat di aliran acara, ditandai dengan ID peristiwa dari mana snapshot dibuat. Dengan cara ini, Anda dapat membangun kembali agregat dengan memuat snapshot terbaru dan hanya mengulang peristiwa yang datang setelahnya, sebagai lawan untuk selalu memutar ulang seluruh aliran peristiwa dari awal waktu.
sumber
Alasan utamanya adalah kinerja. Anda dapat menyimpan snapshot untuk setiap komit (komit = peristiwa yang dihasilkan oleh satu perintah, biasanya hanya satu peristiwa) tetapi ini mahal. Sepanjang snapshot Anda perlu menyimpan juga komit, jika tidak, itu bukan sumber acara. Dan semua ini harus dilakukan secara atomis, semuanya atau tidak sama sekali. Pertanyaan Anda hanya valid jika basis data / tabel / koleksi terpisah digunakan (jika tidak akan menjadi sumber acara) sehingga Anda terpaksa menggunakan transaksi untuk menjamin konsistensi. Transaksi tidak dapat diskalakan. Aliran acara yang hanya ditambahkan (toko acara) adalah ibu dari skalabilitas.
Alasan kedua adalah enkapsulasi Agregat. Anda harus melindunginya. Ini berarti bahwa Agregat harus bebas untuk mengubah perwakilan internalnya kapan saja. Jika Anda menyimpannya dan sangat bergantung padanya maka Anda akan mengalami kesulitan dengan versi, yang pasti akan terjadi. Dalam situasi ketika Anda menggunakan snapshot hanya sebagai optimisasi, ketika skema mengubah Anda cukup mengabaikan snapshot itu ( hanya ? Saya tidak benar-benar berpikir begitu; semoga berhasil menentukan bahwa skema Aggregate berubah - termasuk semua entitas bersarang dan objek nilai - dalam cara efisien dan mengelola itu).
sumber