Saya telah mengadaptasi CQRS 1 orang miskin untuk beberapa waktu sekarang karena saya suka fleksibilitasnya untuk memiliki data granular di satu toko data, memberikan kemungkinan besar untuk analisis dan dengan demikian meningkatkan nilai bisnis dan ketika diperlukan yang lain untuk bacaan yang berisi data yang didenormalkan untuk meningkatkan kinerja .
Tetapi sayangnya sejak awal saya telah bergumul dengan masalah di mana tepatnya saya harus menempatkan logika bisnis dalam arsitektur jenis ini.
Dari apa yang saya mengerti, perintah adalah sarana untuk mengomunikasikan niat dan tidak memiliki ikatan dengan domain dengan sendirinya. Mereka pada dasarnya adalah data (bodoh - jika Anda mau) objek transfer. Ini untuk membuat perintah mudah ditransfer antara berbagai teknologi. Hal yang sama berlaku untuk acara sebagai tanggapan terhadap acara yang berhasil diselesaikan.
Dalam aplikasi DDD yang khas, logika bisnis berada di dalam entitas, objek nilai, akar agregat, mereka kaya akan data maupun perilaku. Tetapi perintah bukanlah objek domain sehingga tidak boleh terbatas pada representasi data domain, karena itu terlalu banyak membebani mereka.
Jadi pertanyaan sebenarnya adalah: Di mana tepatnya logikanya?
Saya menemukan bahwa saya cenderung menghadapi perjuangan ini paling sering ketika mencoba untuk membangun agregat yang cukup rumit yang menetapkan beberapa aturan tentang kombinasi nilai-nilainya. Juga, ketika memodelkan objek domain saya suka mengikuti paradigma gagal-cepat , tahu kapan objek mencapai metode itu dalam keadaan valid.
Katakanlah agregat Carmenggunakan dua komponen:
Transmission,Engine.
Baik Transmissiondan Engineobjek nilai direpresentasikan sebagai tipe super dan memiliki sub tipe sesuai, Automaticdan Manualtransmisi, atau Petroldan Electricmesin masing-masing.
Dalam domain ini, tinggal sendiri yang berhasil dibuat Transmission, baik itu Automaticatau Manual, atau salah satu dari jenis Engineitu sepenuhnya baik-baik saja. Tetapi Caragregat memperkenalkan beberapa aturan baru, hanya berlaku ketika Transmissiondan Engineobjek digunakan dalam konteks yang sama. Yaitu:
- Ketika mobil menggunakan
Electricmesin, satu-satunya jenis transmisi yang diizinkan adalahAutomatic. - Ketika mobil menggunakan
Petrolmesin, ia mungkin memiliki salah satu tipeTransmission.
Saya bisa menangkap pelanggaran kombinasi komponen ini pada tingkat membuat perintah, tetapi seperti yang telah saya nyatakan sebelumnya, dari apa yang saya pahami yang tidak boleh dilakukan karena perintah itu kemudian akan berisi logika bisnis yang harus dibatasi pada lapisan domain.
Salah satu opsi adalah untuk memindahkan validasi logika bisnis ini ke perintah validator itu sendiri, tetapi ini tampaknya juga tidak benar. Rasanya seperti saya akan mendekonstruksi perintah, memeriksa propertinya diambil menggunakan getter dan membandingkannya dengan validator dan memeriksa hasilnya. Itu menjerit seperti pelanggaran terhadap hukum Demeter bagi saya.
Membuang opsi validasi yang disebutkan karena sepertinya tidak layak, sepertinya seseorang harus menggunakan perintah dan membangun agregat darinya. Tetapi di mana logika ini seharusnya ada? Haruskah itu berada di dalam penangan perintah yang bertanggung jawab untuk menangani perintah konkret? Atau haruskah itu mungkin dalam validator perintah (saya juga tidak suka pendekatan ini)?
Saat ini saya menggunakan perintah dan membuat agregat darinya di dalam penangan perintah yang bertanggung jawab. Tetapi ketika saya melakukan ini, haruskah saya memiliki validator perintah itu tidak akan mengandung apa-apa, karena jika CreateCarperintah itu ada maka akan berisi komponen yang saya tahu valid pada kasus-kasus terpisah tetapi agregat mungkin mengatakan berbeda.
Mari kita bayangkan skenario berbeda yang memadukan proses validasi yang berbeda - membuat pengguna baru menggunakan CreateUserperintah.
Perintah berisi Iddari pengguna yang akan dibuat dan mereka Email.
Sistem menyatakan aturan berikut untuk alamat email pengguna:
- harus unik,
- tidak boleh kosong,
- harus memiliki paling banyak 100 karakter (panjang maksimal kolom db).
Dalam hal ini, walaupun memiliki email unik adalah aturan bisnis, mengeceknya secara agregat sangat tidak masuk akal, karena saya perlu memuat seluruh rangkaian email saat ini di sistem ke memori dan memeriksa email di perintah terhadap agregat ( Eeeek! Sesuatu, sesuatu, kinerja.). Karena itu, saya akan memindahkan pemeriksaan ini ke validator perintah, yang akan menggunakan UserRepositorydependensi dan menggunakan repositori untuk memeriksa apakah pengguna dengan email yang ada dalam perintah sudah ada.
Ketika sampai pada hal ini, tiba-tiba masuk akal untuk memasukkan dua aturan email lainnya ke dalam validator perintah juga. Tapi saya merasa aturan harus benar-benar hadir dalam Useragregat dan bahwa validator perintah hanya harus memeriksa tentang keunikan dan jika validasi berhasil saya harus melanjutkan untuk membuat Useragregat dalam CreateUserCommandHandlerdan meneruskannya ke repositori untuk disimpan.
Saya merasa seperti ini karena metode penyimpanan repositori cenderung menerima agregat yang memastikan bahwa setelah agregat disahkan semua invarian dipenuhi. Ketika logika (mis. Ketidak-kekosongan) hanya hadir dalam validasi perintah itu sendiri programmer lain dapat sepenuhnya melewatkan validasi ini dan memanggil metode save UserRepositorydengan Userobjek secara langsung yang dapat menyebabkan kesalahan database fatal, karena email mungkin memiliki sudah terlalu lama.
Bagaimana Anda secara pribadi menangani validasi dan transformasi yang kompleks ini? Saya sebagian besar senang dengan solusi saya, tetapi saya merasa saya perlu penegasan bahwa ide dan pendekatan saya tidak sepenuhnya bodoh untuk cukup senang dengan pilihan. Saya sepenuhnya terbuka untuk pendekatan yang sama sekali berbeda. Jika Anda memiliki sesuatu yang secara pribadi telah Anda coba dan bekerja dengan sangat baik untuk Anda, saya akan senang melihat solusi Anda.
1 Bekerja sebagai pengembang PHP yang bertanggung jawab untuk menciptakan sistem RESTful interpretasi saya terhadap CQRS sedikit menyimpang dari pendekatan pemrosesan async-command standar , seperti kadang-kadang mengembalikan hasil dari perintah karena kebutuhan memproses perintah secara serempak.
CommandDispatcher.
