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 Car
menggunakan dua komponen:
Transmission
,Engine
.
Baik Transmission
dan Engine
objek nilai direpresentasikan sebagai tipe super dan memiliki sub tipe sesuai, Automatic
dan Manual
transmisi, atau Petrol
dan Electric
mesin masing-masing.
Dalam domain ini, tinggal sendiri yang berhasil dibuat Transmission
, baik itu Automatic
atau Manual
, atau salah satu dari jenis Engine
itu sepenuhnya baik-baik saja. Tetapi Car
agregat memperkenalkan beberapa aturan baru, hanya berlaku ketika Transmission
dan Engine
objek digunakan dalam konteks yang sama. Yaitu:
- Ketika mobil menggunakan
Electric
mesin, satu-satunya jenis transmisi yang diizinkan adalahAutomatic
. - Ketika mobil menggunakan
Petrol
mesin, 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 CreateCar
perintah 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 CreateUser
perintah.
Perintah berisi Id
dari 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 UserRepository
dependensi 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 User
agregat dan bahwa validator perintah hanya harus memeriksa tentang keunikan dan jika validasi berhasil saya harus melanjutkan untuk membuat User
agregat dalam CreateUserCommandHandler
dan 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 UserRepository
dengan User
objek 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
.