Dalam CQRS / ES, dapatkah sebuah perintah membuat perintah lain?

10

Dalam CQRS / ES, perintah dikirim dari klien ke server dan dialihkan ke penangan perintah yang sesuai. Handler perintah itu memuat agregat dari repositori, dan memanggil beberapa metode di atasnya, dan menyimpannya kembali ke repositori. Acara dihasilkan. Seorang manajer event handler / saga / proses dapat mendengarkan acara ini untuk mengeluarkan perintah.

Jadi, perintah (input) menghasilkan peristiwa (output), yang kemudian dapat mengumpan balik ke sistem lebih banyak perintah (input). Sekarang, apakah ini merupakan praktik yang umum bagi suatu perintah untuk tidak memancarkan peristiwa apa pun, melainkan untuk membuat perintah lain? Pendekatan semacam itu dapat digunakan untuk memaksa eksekusi dalam proses eksternal.

EDIT:

Kasus penggunaan spesifik yang ada dalam pikiran saya adalah pemrosesan rincian pembayaran. Klien mengirimkan PayInvoiceperintah, yang payload-nya meliputi rincian kartu kredit pengguna. Perintah ini PayInvoiceHandlerdikirimkan MakeInvoicePaymentke proses terpisah, yang bertanggung jawab untuk berinteraksi dengan gateway pembayaran. Jika pembayaran berhasil, suatu InvoicePaidperistiwa dihasilkan. Jika karena alasan tertentu sistem macet setelah PayInvoiceperintah tetap ada tetapi sebelum MakeInvoicePaymentperintah itu tetap ada, kita dapat melacak ini secara manual (tidak ada pembayaran yang akan dilakukan). Jika sistem crash setelah MakeInvoicePaymentperintah tetap tetapi sebelumInvoicePaidacara tetap, kami mungkin memiliki situasi di mana kartu kredit pengguna dibebankan tetapi faktur tidak ditandai sebagai telah dibayar. Dalam hal ini, situasinya harus diselidiki secara manual dan faktur secara manual ditandai sebagai berbayar.

Magnus
sumber

Jawaban:

11

Dalam retrospeksi, saya pikir saya mempersulit masalah ini.

Secara umum, perintah harus membuang pengecualian atau memunculkan satu atau lebih peristiwa.

Jika saya dapat merangkum arsitektur Event Sourcing, itu adalah sebagai berikut:

  • Perintah adalah input yang mewakili instruksi untuk melakukan sesuatu.
  • Peristiwa adalah keluaran yang mewakili fakta historis dari apa yang telah dilakukan.
  • Penangan acara dapat mendengarkan acara untuk mengeluarkan perintah, membantu mengoordinasikan berbagai bagian sistem.

Memiliki satu perintah membuat perintah lain menyebabkan ambiguitas dalam arti umum perintah dan peristiwa: jika satu perintah "memicu" perintah lain, itu menyiratkan bahwa perintah adalah "fakta sejarah dari apa yang dilakukan". Ini bertentangan dengan maksud dari dua jenis pesan ini, dan bisa menjadi jalan yang licin, karena pengembang lain dapat memicu peristiwa dari peristiwa, yang dapat menyebabkan data yang korup dan akhirnya inkonsistensi .

Sehubungan dengan skenario spesifik yang saya ajukan, faktor yang menyulitkan adalah saya tidak ingin utas utama berinteraksi dengan gateway pembayaran, karena (sebagai proses persisten, satu-utas), ini tidak akan memungkinkan perintah lain diproses . Solusi sederhana di sini adalah menelurkan utas / proses lain untuk menangani pembayaran.

Sebagai ilustrasi, klien mengirim PayInvoiceperintah. The PayInvoiceHandlermemulai proses baru dan dibagikan rincian pembayaran. Proses baru berkomunikasi dengan gateway pembayaran. Jika pembayaran berhasil, ia memanggil invoice.markAsPaid()dengan nomor tanda terima (yang menghasilkan InvoicePaidacara). Jika pembayaran tidak berhasil, ia memanggil invoice.paymentFailed()dengan melewati kode referensi untuk penyelidikan lebih lanjut (yang menghasilkan InvoicePaymentFailedacara). Jadi terlepas dari fakta bahwa proses / utas terpisah terlibat, polanya tetap ada Command -> Event.

Berkenaan dengan kegagalan, ada tiga skenario, sistem bisa macet setelah PayInvoiceperintah tetap tetapi sebelum InvoicePaidatau InvoicePaymentFailedacara tetap ada. Dalam hal ini, kami tidak tahu apakah kartu kredit pengguna ditagih. Dalam kasus seperti itu, pengguna akan melihat tagihan pada kartu kredit mereka dan mengajukan keluhan, dalam hal ini anggota staf dapat menyelidiki masalah ini dan secara manual menandai faktur yang telah dibayarkan.

Magnus
sumber
1
Terima kasih banyak atas pertanyaan dan jawabannya, makanan yang enak untuk dipikirkan. Secara keseluruhan masih ada beberapa poin yang saya anggap membingungkan: (1) Saya belum melihat banyak orang berbicara tentang pengertian event handler. (2) Penafsiran saya tentang pandangan Anda menggeser sebagian besar pekerjaan ke penangan acara, ke titik di mana penangan perintah menjadi fungsi terjemahan murni, menimbulkan pertanyaan apakah pemisahan menjadi perintah / peristiwa bahkan diperlukan. Lihat pertanyaan tindak lanjut ini jika Anda tertarik.
bluenote10
3

Anda akan mendapatkan sistem yang secara arsitektur lebih longgar digabungkan jika Anda hanya memancarkan peristiwa dari suatu perintah. Dengan kata lain, satu perintah seharusnya tidak perlu tahu apa perintah eksternal lainnya untuk mengeluarkan; yang seharusnya menjadi tanggung jawab pihak eksternal (siapa yang harus berlangganan ke acara tersebut, dan bisa, seperti yang Anda sebutkan, seorang manajer saga yang memiliki tanggung jawab koordinasi, atau hanya modul lain yang memiliki ketergantungan pada acara-acara tersebut).

Erik Eidt
sumber
2

Tampilan yang disarankan: Udi Dahan tentang Pesan Tepercaya - ini tidak persis seperti yang Anda gambarkan, tetapi terkait erat.

Sekarang, apakah ini merupakan praktik yang umum bagi suatu perintah untuk tidak memancarkan peristiwa apa pun, melainkan untuk membuat perintah lain?

Saya belum pernah melihat orang merekomendasikan latihan itu.

Jawaban singkat: jika Anda tidak menyimpan beberapa keadaan, maka Anda tidak dapat memulihkan perintah enqueued jika Anda gagal setelah mengakui bahwa Anda telah menerimanya.

Jika Anda memutuskan bahwa Anda perlu menyimpan keadaan, tidak jelas bahwa ada keuntungan besar untuk menjadwalkan perintah kedua dari yang pertama, daripada menggunakan event handler.

VoiceOfUnreason
sumber