Konstruktor argumen nol dan entitas Selalu Valid

8

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?

w0051977
sumber
2
Anda benar-benar tidak bisa menyamaratakan. Mungkin. Mungkin tidak. Apakah itu penting? IMHO tidak, terutama pembicaraan DDD awal lupa untuk berpikir bahwa kenyataan itu berbeda dan misalnya, ORM / bahasa / kerangka kerja menentukan beberapa aturan (dan harga untuk mengelaknya mungkin terlalu tinggi atau tidak layak). Ok, juga proyek yang Anda daftarkan tampaknya tidak mungkin. Kode NET terbaik ...
Adriano Repetti
@Adriano Repetti, apakah Anda tahu ada proyek open source cqrs yang bagus? Apakah Anda memasukkan validasi pada perintah Anda?
w0051977
2
Saya akan membedakan validasi parameter dari validasi domain (bila berlaku). Misalnya parameter nol tidak ada hubungannya dengan domain, itu berasal dari detail implementasi (bahasa yang Anda gunakan). Yang pertama selalu saya lakukan, tidak peduli apa dan di mana. Validasi domain lebih rumit, tetapi jika Anda benar-benar khawatir tentang invarian yang rusak, IMO, Anda harus mempertimbangkan objek yang tidak dapat diubah . Mereka (lagi-lagi hanya menurut saya) merupakan tradeoff yang lebih sering dan lebih baik daripada perang suci melawan bahasa yang benar-benar kita gunakan (dan itu adalah kritik bagi banyak DDD lama)
Adriano Repetti

Jawaban:

7

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 mendaftar
  • Customer harus di bawah 25 untuk memenuhi syarat untuk diskon pada saat pendaftaran
  • Customer harus lebih dari 25 tahun untuk membuat reservasi

Hal 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()dan customer.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 Customerke dalam Registrardan Reserverentitas 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/

slide sisi raja
sumber
3

Apakah objek domain dalam status yang valid jika diinisialisasi dengan nilai default menggunakan konstruktor argumen nol / diinisialisasi dengan nilai kosong / nol?

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.

Number n = new Number(...)
Optional<Prime> p = n.prime()
if (p.isPresent()) {
    Optional<MersennePrime> mp = p.mersennePrime()
    // ...
} 

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-> MersennePrimekonversi, memastikan bahwa pengetahuan tentang batasan Mersenne memiliki satu otoritas ("jangan ulangi diri Anda").

"Buat yang tersirat, eksplisit"

VoiceOfUnreason
sumber
Terima kasih. Bisakah Anda menjelaskan aljabar semantik apa dengan contohnya? Bisakah Anda juga menjelaskan bagaimana contoh kode itu relevan. +1 untuk "tidak ada yang menentang ....".
w0051977
Tentunya entitas tanpa ID kosong adalah hal yang buruk? Kecuali ada skenario di mana Anda menginisialisasi kelas domain dengan nilai default dan kemudian membangunnya secara bertahap?
w0051977