Cara menangani kesalahan pasca-validasi dalam perintah (DDD + CQRS)

18

Misalnya, ketika Anda mengirimkan formulir Daftar, Anda harus memeriksa Domain Model( WriteModeldalam CQRS) bahwa itu dalam keadaan valid (misalnya, sintaksis alamat email, umur, dll).

Kemudian Anda membuat Command, dan mengirimkannya ke Command Bus.

Saya mengerti bahwa Perintah tidak boleh mengembalikan apa pun.

Jadi, bagaimana Anda menangani kesalahan di luar Command Bus? (Misalnya, pengguna mendaftar 1 detik sebelumnya dengan yang sama username/email).

Bagaimana Anda tahu bahwa perintah itu gagal, dan bagaimana Anda tahu kesalahannya?

JorgeeFG
sumber
2
Anda tidak perlu bus acara. Mengapa semua orang berpikir Anda perlu bus acara ketika menerapkan CQRS, saya tidak tahu. CQRS berarti Anda benar-benar memisahkan tulisan dan bacaan, membuat kekhawatiran terpisah. Anda memiliki CQRS selama Anda melakukannya. Perintah Anda bahkan tidak perlu asinkron. Jika perintah sinkron dengan pelaporan kesalahan berfungsi untuk Anda, lakukan itu.
Andy
1
Perintah tidak boleh mengembalikan apa pun ketika berhasil . Idenya dimulai dari CQS , yang memiliki implikasi bahwa perintah masih bisa melempar pengecualian pada kesalahan. Apa yang Anda gambarkan adalah perintah satu arah. Lihat jawaban ini sehubungan dengan itu.
Kasey Speakman

Jawaban:

4

Saya mengerti bahwa Perintah tidak boleh mengembalikan apa pun.

Itu satu pandangan, tapi tidak sepenuhnya dibuat-buat. Pertimbangkan menulis (PUT, POST, HAPUS) dalam HTTP - semua pesan ini adalah perintah, dalam arti bahwa mereka adalah pesan dengan permintaan bahwa status sumber daya berubah, namun semuanya mengembalikan respons.

Jadi, bagaimana Anda menangani kesalahan di luar Command Bus? (Misalnya, pengguna mendaftar 1 detik sebelumnya dengan nama pengguna / email yang sama).

Bagaimana Anda tahu bahwa perintah itu gagal, dan bagaimana Anda tahu kesalahannya?

Jadi dalam kasus di mana Anda berkomunikasi secara langsung dengan pengendali perintah, pesan yang dikembalikan adalah cara yang masuk akal untuk mengakui bahwa perintah telah diterima dan diproses.

Jika Anda menggunakan sepotong middleware, seperti bus, yang mencegah Anda berkomunikasi langsung dengan target, maka saya akan menyarankan agar Anda melihat ke pola pesan asinkron - bagaimana Anda mendapatkan penangan perintah untuk mengirim pesan kembali ke penelepon?

Satu ide adalah berlangganan hasil dari perintah; ini meminjam dari beberapa gagasan dalam Pola Integrasi Perusahaan Hohpe. Gagasan dasarnya adalah bahwa, karena klien terbiasa dengan pesan perintah yang dikirim, ia berada pada posisi yang tepat untuk berlangganan pesan baru yang diterbitkan sebagai konsekuensi dari pesan perintah tersebut. Penangan perintah, setelah menyimpan data ke dalam buku catatan, menerbitkan peristiwa yang mengumumkan bahwa perubahan itu berhasil, dan klien berlangganan peristiwa-peristiwa tersebut - mengenali peristiwa yang benar dengan mempertimbangkan kebetulan berbagai pengidentifikasi dalam pesan (causation id, id korelasi , dan sebagainya).

Pendekatan alternatif sedikit lebih langsung. Satu akan memasukkan dalam pesan panggilan balik, yang dapat dipanggil oleh penangan perintah setelah pesan berhasil ditangani.

Alternatif yang sangat mirip adalah dengan menyimpan ruang dalam pesan perintah untuk penangan perintah untuk menulis pengakuan - karena klien sudah memiliki pesan perintah yang dipermasalahkan, sirkuit sudah lengkap. Pikirkan " janji " atau " masa depan yang bisa diselesaikan". Pesan itu memberi tahu penangan perintah di mana harus menulis pengakuan; melakukan hal tersebut memberi sinyal kepada klien (kait hitung mundur) bahwa pengakuan tersedia.

Dan tentu saja, Anda memiliki opsi tambahan untuk menghapus middleware yang tampaknya menghalangi cara melakukan hal yang benar.

Misalnya, pengguna mendaftar 1 detik sebelumnya dengan nama pengguna / email yang sama

Jika Anda menangani pendaftaran pengguna dengan tenang, itu tidak akan selalu menjadi kesalahan - pengulangan pesan sampai respons diamati adalah cara yang umum untuk memastikan setidaknya sekali pengiriman.

VoiceOfUnreason
sumber
2

Misalnya, ketika Anda mengirimkan formulir Daftar, Anda harus memeriksa dalam Model Domain (WriteModel dalam CQRS) bahwa itu dalam keadaan yang valid (misalnya, sintaksis alamat email, usia, dll)

Ada banyak jenis validasi. Validasi ketika Anda memeriksa sintaksis alamat email dan format umur adalah jenis validasi yang dapat dilakukan oleh suatu Perintah. Ini sebenarnya bukan masalah Domain. Mungkin tampak seperti itu karena beberapa pakar domain akan memberi tahu Anda spesifikasi itu tetapi Anda harus melakukan validasi semacam ini dalam pembuatan Perintah. Sebenarnya, ide umum adalah melakukan validasi sesegera mungkin karena setelah perintah dibuat dan dikirim ke BUS, lebih rumit untuk mengambil tindakan.

Jadi, bagaimana Anda menangani kesalahan di luar Command Bus? (Misalnya, pengguna mendaftar 1 detik sebelumnya dengan nama pengguna / email yang sama).

Validasi semacam ini banyak dibahas di komunitas CQRS, sejak awal CQRS. Di mana melakukannya? Sangat diperdebatkan. Saya pribadi menggunakan pendekatan berikut: Sebelum perintah dikirim ke BUS, saya menandai nama pengguna / email yang diambil secara terpusat (yaitu batasan indeks unik pada nama pengguna / email). Setelah itu saya mengirim perintah. Kekurangannya adalah bahwa, jika perintah gagal, untuk waktu yang singkat nama pengguna diambil dan tidak digunakan; ini dapat diterima untuk bisnis saya, kemungkinan untuk bisnis Anda.

Bagaimana Anda tahu bahwa perintah itu gagal, dan bagaimana Anda tahu kesalahannya?

Jika perintah asinkron ditolak oleh Agregat karena invarian domain maka beberapa tindakan harus diambil dengan mengeluarkan beberapa perintah kompensasi yang entah bagaimana memberi tahu penerbit perintah (mis. Mengirim email penjelasan bahwa perintah gagal).

Masalah dengan duplikat email adalah Anda tidak dapat mengirim email karena alamat email itu milik orang lain, itu sebabnya saya memeriksanya sebelum mengirim perintah ke bus.

Constantin Galbenu
sumber
1

Validasi harus dilakukan di dekorator. Maka setiap perintah yang membutuhkan validasi dapat didekorasi seperti itu.

Validasi dapat ditangani dengan pengecualian jika Anda kembali batal dengan perintah Anda sehingga dapat diambil dengan panggilan sinkronisasi atau asinkron dengan hasil tugas yang dikembalikan.

Kemungkinan lain adalah menganggap validasi sebagai jenis "kueri" yang akan mengembalikan hasil validasi. Jalankan permintaan validasi, dan kemudian jika lulus, jalankan perintah. Ini akan menjadi alternatif dari pendekatan dekorator.

Jon Raynor
sumber
Saya suka pendekatan pengecualian karena ini adalah cara terbersih. Tapi bukankah pengecualian terlalu berat untuk ini?
EresDev
1
HI @EresDev - Ya mereka berat. Tapi, saya tidak tahu seberapa "valid" data Anda. Katakanlah dari 1000 catatan 2 tidak valid. Pengecualian bisa berlaku karena mereka benar-benar kondisi yang luar biasa. Sekarang pertimbangkan 1000 catatan dengan 200 tidak valid. Karena itu, ya pengecualian harus dihindari karena 20% dari data Anda tidak valid. Jadi, saya menyerahkan kepada Anda untuk memutuskan apakah akan memilih pendekatan ini berdasarkan validitas data Anda saat ini atau tidak. :)
Jon Raynor
Saya akan menambahkan ini dengan mengatakan bahwa jika kita menyimpan contoh formulir pendaftaran, di mana saya pikir 50% atau bahkan lebih banyak pengguna memasukkan data yang tidak valid terlebih dahulu, pengecualian masih oke. Di backend aplikasi web, Anda sebagian besar mendapatkan data yang valid karena frontend juga melakukan validasi. Anda hanya mendapatkan jika ada sesuatu yang melewatkan validasi frontend oleh beberapa kesempatan langka atau jika seseorang bermain-main. Jadi, pengecualian sangat baik dalam hal ini.
EresDev