Saya telah melakukan banyak membaca baru-baru ini tentang entitas domain Always Valid. Saya menjadi percaya bahwa untuk memastikan entitas selalu valid saya perlu:
1) Hapus obsesi primitif dan masukkan aturan validasi / domain dalam konstruktor objek nilai seperti yang dijelaskan di sini: https://enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/ . 2) Masukkan aturan validasi / domain dalam konstuktor entitas atau setter properti seperti dijelaskan di sini: http://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/ .
Namun, saya kemudian melihat beberapa proyek Open Source seperti ini: https://github.com/gregoryyoung/mr . Dari apa yang saya pahami, penulis proyek ini adalah penganjur model domain yang selalu valid, namun saya melihat di sini di kelas InventoryItem: https://github.com/gregoryyoung/mr/blob/master/SimpleCQRS/Domain.cs . Saya perhatikan bahwa saya dapat melakukan ini:
InventoryItem inventoryItem = new InventoryItem();
atau ini:
InventoryItem inventoryItem2 = new InventoryItem(Guid.Empty,null);
Dalam pikiran saya ini berarti entitas diinisialisasi dalam keadaan tidak valid. Ini tampaknya menjadi kasus di semua proyek Open Source lain yang telah saya lihat baru-baru ini juga misalnya yang ini: https://github.com/dcomartin/DDD-CQRS-ES-Example/blob/master/src/Domain /Customer.cs .
Saya menyadari ada validasi kontekstual dalam proyek sumber terbuka ini ( https://martinfowler.com/bliki/ContextualValidation.html ). Saya juga menyadari bahwa ORM memerlukan konstruktor kosong default jika dipetakan ke model domain.
Apakah objek domain dalam status yang valid jika diinisialisasi dengan nilai default menggunakan konstruktor argumen nol / diinisialisasi dengan nilai kosong / nol?
sumber
Jawaban:
Sebelum saya membahas jenis pertimbangan yang masuk ke penciptaan objek, mari kita membahas motivasi di balik pertanyaan Anda: frasa "Selalu Valid Entitas Domain". Frasa ini paling tidak menyesatkan dan tidak masuk akal dalam konteks DDD. Ini harus jelas karena dua alasan terkait:
Yang pertama adalah bahwa itu secara implisit menggeser fokus Anda menjauh dari perilaku sistem, alih-alih, meminta Anda untuk mempertimbangkan validasi hanya dalam hal keadaan. Walaupun secara dangkal ini mungkin masuk akal (tentu saja validasi sesuai dengan keadaan!), Anda harus ingat bahwa prinsip dasar DDD adalah bahwa sistem dimodelkan sesuai dengan perilaku . Motivasi untuk ini cukup sederhana yaitu konteksnya, atau proses bisnis itu sendiri, sering kali menjadi pertimbangan penting ketika menentukan apakah suatu bagian dari negara itu sah atau tidak. Pemodelan sistem dengan cara ini dapat sangat mengurangi kompleksitasnya.
Ini membawa kita pada alasan kedua, yang berkenaan dengan persyaratan praktis yang diperlukan oleh sistem seperti itu. Untuk membuat sistem "Selalu Valid Domain Entitas", diperlukan satu untuk memodelkan setiap permutasi tunggal negara sesuai dengan proses bisnis di mana negara digunakan. Contoh sederhana dapat menggambarkan keterbatasan ini:
Aturan:
Customer
harus lebih dari 18 tahun untuk mendaftarCustomer
harus di bawah 25 untuk memenuhi syarat untuk diskon pada saat pendaftaranCustomer
harus lebih dari 25 tahun untuk membuat reservasiHal pertama yang harus Anda perhatikan adalah bahwa semua aturan ini (seperti hampir semua aturan) berlaku untuk beberapa proses bisnis. Mereka tidak ada dalam ruang hampa. Aturan-aturan ini akan divalidasi pada
customer.Register()
dancustomer.Reserve()
. Ini menghasilkan paradigma yang jauh lebih deskriptif dan deklaratif karena jelas di mana aturan dijalankan.Jika kita ingin memodelkan aturan-aturan ini sedemikian rupa sehingga menghasilkan sistem "Entitas Domain Selalu Valid" kita perlu mempartisi
Customer
ke dalamRegistrar
danReserver
entitas kita. Dan sementara itu mungkin tidak tampak terlalu buruk untuk contoh ini, karena aturan menjadi lebih kompleks dan berlimpah Anda akan berakhir dengan kelas ledakan seperti ini yang mewakili negara "dalam" beberapa konteks atau proses. Ini sama sekali tidak perlu dan pasti akan menciptakan masalah ketika dua objek tersebut bergantung pada potongan negara yang sama.Selain itu, sesuatu seperti
Customer c = new Customer()
tempat yang buruk untuk melempar pengecualian karena tidak jelas apa aturan bisnis yang berlaku. Yang membawa kita ke diskusi akhir kita.Saya hanya akan keluar dan pergi sejauh mengatakan bahwa harus ada nol validasi aturan bisnis terjadi di konstruktor. Konstruksi objek tidak ada hubungannya sama sekali dengan domain bisnis Anda, dan untuk alasan itu, selain alasan di atas mengenai konteks dan koherensi, semua aturan bisnis harus ditegakkan dalam badan metode entitas (kemungkinan metode tersebut dinamai dengan proses bisnis yang benar ?).
Lebih jauh, "mem-newing" sebuah objek tidak sama dengan membuat entitas baru di domain Anda. Benda tidak datang entah dari mana. Jika ada aturan bisnis tentang bagaimana entitas baru dapat masuk ke sistem Anda, maka itu harus dimodelkan dalam domain Anda . Berikut adalah beberapa diskusi lebih lanjut tentang topik tersebut oleh master sejati http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
sumber
Itu bisa saja.
Tidak ada yang melanggar aturan tentang membuat objek domain yang valid menggunakan konstruktor default.
Tidak ada yang melanggar aturan tentang membuat objek domain yang valid dengan nilai kosong.
Tidak ada yang melanggar aturan tentang membuat objek domain yang valid yang tidak memiliki elemen opsional.
Tidak ada yang melanggar aturan tentang membuat objek domain yang valid menggunakan nulls.
Di mana masalah terjadi: membuat objek domain yang tidak mematuhi aljabar semantik mereka sendiri.
Implementasi yang benar tergantung pada interpretasi semantik yang benar.
Itu normal untuk mengambil representasi pemaaf dari konsep domain dan dengan langkah-langkah menerapkan kendala tambahan. Ini adalah salah satu area di mana bahasa implementasi yang membuatnya mudah untuk menambahkan tipe (mis: F #) memiliki keunggulan dibandingkan bahasa clumsier seperti Java.
Misalnya, jika kita memiliki domain yang peduli tentang angka, dan di dalam domain itu ada beberapa kemampuan yang khusus untuk bilangan prima, atau bilangan prima Mersenne, maka mekanisme alami untuk digunakan adalah membuat tiga jenis yang berbeda; buat secara eksplisit gagasan bahwa ada berbagai rangkaian kendala untuk diterapkan pada input di berbagai bagian solusi Anda.
Dalam desain semacam ini, "validasi" bahwa bilangan itu benar-benar Prime akan ada di dalam konstruktor Prime itu sendiri. Dalam konteks di mana kita memerlukan batasan tambahan bahwa parameternya adalah prime Mersenne , kami menggunakan
Prime
->MersennePrime
konversi, memastikan bahwa pengetahuan tentang batasan Mersenne memiliki satu otoritas ("jangan ulangi diri Anda")."Buat yang tersirat, eksplisit"
sumber