Skenario:
Pelanggan memesan, kemudian, setelah menerima produk, memberikan umpan balik pada proses pemesanan.
Asumsikan akar agregat berikut:
- Pelanggan
- Memesan
- Umpan balik
Berikut adalah aturan bisnisnya:
- Seorang pelanggan hanya dapat memberikan umpan balik atas pesanan mereka sendiri, bukan milik orang lain.
Pelanggan hanya dapat memberikan umpan balik jika pesanan telah dibayar.
class Feedback { public function __construct($feedbackId, Customer $customer, Order $order, $content) { if ($customer->customerId() != $order->customerId()) { // Error } if (!$order->isPaid()) { // Error } $this->feedbackId = $feedbackId; $this->customerId = $customerId; $this->orderId = $orderId; $this->content = $content; } }
Sekarang, anggap bisnis menginginkan aturan baru:
Pelanggan hanya dapat memberikan umpan balik jika
Supplier
barang pesanan masih beroperasi.class Feedback { public function __construct($feedbackId, Customer $customer, Order $order, Supplier $supplier, $content) { if ($customer->customerId() != $order->customerId()) { // Error } if (!$order->isPaid()) { // Error } // NEW RULE HERE if (!$supplier->isOperating()) { // Error } $this->feedbackId = $feedbackId; $this->customerId = $customerId; $this->orderId = $orderId; $this->content = $content; } }
Saya telah menempatkan implementasi dari dua aturan pertama dalam Feedback
agregat itu sendiri. Saya merasa nyaman melakukan ini, terutama mengingat bahwa
Feedback
agregat merujuk semua agregat lainnya berdasarkan identitas. Misalnya, sifat-sifat Feedback
komponen menunjukkan bahwa ia mengetahui
keberadaan agregat lain, jadi saya merasa nyaman mengetahui tentang status read only dari agregat ini juga.
Namun, berdasarkan sifat itu, yang Feedback
agregat tidak memiliki pengetahuan tentang keberadaan dari
Supplier
agregat, sehingga seharusnya ia memiliki pengetahuan tentang membaca satunya negara dari agregat ini?
Solusi alternatif untuk menerapkan aturan 3 adalah dengan memindahkan logika ini ke yang sesuai CommandHandler
. Namun, ini terasa seperti memindahkan logika domain dari "pusat" arsitektur berbasis bawang saya.
Supplier
status operasi agregat tidak akan ditanyai melaluiOrder
repositori;Supplier
danOrder
dua agregat terpisah. Kedua, ada pertanyaan di milis DDD / CQRS tentang meneruskan akar agregat dan repositori ke metode akar agregat lainnya (termasuk konstruktor). Ada berbagai pendapat, tetapi Greg Young menyebutkan bahwa melewati akar agregat sebagai parameter adalah umum, sementara orang lain mengatakan bahwa repositori lebih erat terkait dengan infrastruktur daripada domain. Misalnya, repositori "abstrak dalam koleksi memori" dan tidak memiliki logika.Customer
kaleng hanya memberikan umpan balik pada salah satu pesanan mereka sendiri ($order->customerId() == $customer->customerId()
), kami juga harus membandingkan ID pemasok ($order->supplierId() == $supplier->supplierId()
). Aturan pertama melindungi pengguna yang memberikan nilai yang salah. Aturan kedua menjaga terhadap programmer yang memasok nilai yang salah. Namun demikian, pemeriksaan apakah pemasok beroperasi harus dalamFeedback
entitas, atau dalam pengendali perintah. Dimana pertanyaannya?Jawaban:
Jika kebenaran transaksional membutuhkan satu agregat mengetahui tentang kondisi saat ini dari agregat lain, maka model Anda salah.
Dalam kebanyakan kasus, kebenaran transaksional tidak diperlukan . Bisnis cenderung memiliki toleransi terhadap data latensi dan basi. Ini terutama berlaku untuk inkonsistensi yang mudah dideteksi dan mudah diperbaiki.
Jadi perintah akan dijalankan oleh agregat yang mengubah status. Untuk melakukan pemeriksaan yang belum tentu benar, diperlukan belum tentu salinan terbaru dari keadaan agregat lainnya.
Untuk perintah pada agregat yang ada, pola yang biasa adalah meneruskan Repositori ke agregat, dan agregat akan meneruskan statusnya ke repositori, yang menyediakan kueri yang mengembalikan keadaan / proyeksi kekal dari agregat lainnya
Tetapi pola konstruksi aneh - ketika Anda membuat objek, penelepon sudah mengetahui keadaan internal, karena menyediakannya. Pola yang sama berfungsi, hanya terlihat tidak berguna
Kami mengikuti aturan dengan menjaga semua logika domain di objek domain, tetapi kami tidak benar-benar melindungi invarian bisnis dengan cara yang bermanfaat dengan melakukannya (karena semua informasi yang sama tersedia untuk komponen aplikasi). Untuk pola pembuatan, akan sama baiknya untuk menulis
sumber
SupplierOperatingQuery
kueri model yang dibaca, atau "Kueri" pada nama menyesatkan? 2. Konsistensi transaksional tidak diperlukan. Tidak masalah jika pemasok menghentikan operasi satu detik sebelum pelanggan meninggalkan umpan balik, tetapi apakah itu berarti kita tidak seharusnya memeriksanya? 3. Dalam contoh Anda, apakah menyediakan "layanan permintaan" daripada objek itu sendiri menegakkan konsistensi transaksional? Jika ya, bagaimana caranya? 4. Bagaimana penggunaan layanan kueri tersebut memengaruhi pengujian unit?Saya tahu ini adalah pertanyaan lama, tetapi saya ingin menunjukkan bahwa masalah ini secara langsung berasal dari premis yang salah. Artinya, akar agregat yang seharusnya kita asumsikan ada sama sekali tidak benar.
Hanya ada satu agregat root dalam sistem yang telah Anda jelaskan: Pelanggan. Baik Pesanan dan Umpan Balik, meskipun mereka mungkin merupakan agregat dalam hak mereka sendiri, bergantung pada Pelanggan untuk keberadaannya sehingga bukan merupakan akar agregat itu sendiri. Logika yang Anda berikan dalam konstruktor umpan balik Anda tampaknya menunjukkan bahwa Pesanan HARUS memiliki pelanggan dan Umpan Balik HARUS juga terkait dengan Pelanggan. Ini masuk akal. Bagaimana Pesanan atau Umpan Balik tidak dapat dikaitkan dengan Pelanggan? Selain itu, Pemasok tampaknya secara logis terkait dengan Pesanan (demikian juga dengan agregat ini).
Dengan pemikiran di atas, semua informasi yang Anda inginkan sudah tersedia di akar agregat Pelanggan dan menjadi jelas Anda menegakkan aturan Anda di tempat yang salah. Konstruktor adalah tempat yang mengerikan untuk menegakkan aturan bisnis dan harus dihindari dengan cara apa pun. Seharusnya terlihat seperti ini (Catatan: Saya tidak akan menyertakan konstruktor untuk Pelanggan dan Pesanan karena Pabrik mungkin harus digunakan. Juga tidak menampilkan semua metode antarmuka).
Baik. Mari kita uraikan ini sedikit. Hal pertama yang Anda perhatikan adalah seberapa jauh deklaratif model ini. Semuanya adalah tindakan, menjadi jelas DI MANA aturan bisnis harus berlaku. Desain di atas tidak hanya "melakukan" hal yang benar, tetapi "mengatakan" hal yang benar.
Apa yang akan membuat orang menganggap aturan sedang dijalankan di baris berikut?
Kedua, Anda dapat melihat bahwa semua logika yang berkaitan dengan memvalidasi aturan bisnis dilakukan sedekat mungkin dengan model yang terkait. Dalam contoh Anda, konstruktor (metode tunggal) sedang melakukan beberapa validasi terhadap model yang berbeda. Itu merusak desain SOLID. Di mana kami akan menambahkan cek untuk memastikan bahwa konten Umpan Balik tidak mengandung kata-kata buruk? Periksa lagi di konstruktor? Bagaimana jika berbagai jenis Umpan Balik memerlukan pemeriksaan konten yang berbeda? Jelek.
Ketiga, melihat antarmuka, Anda dapat melihat ada tempat alami untuk memperluas / memodifikasi aturan melalui komposisi. Misalnya, berbagai jenis pesanan dapat memiliki aturan yang berbeda mengenai kapan umpan balik dapat diberikan. Pesanan juga dapat memberikan berbagai jenis umpan balik, yang pada gilirannya dapat memiliki aturan yang berbeda untuk validasi.
Anda juga dapat melihat banyak antarmuka ICustomer *. Ini digunakan untuk menyusun agregat Pelanggan yang kami butuhkan di sini (mungkin tidak hanya disebut Pelanggan). Alasannya sederhana. Sangat mungkin bahwa Pelanggan adalah akar agregat BESAR yang tersebar di seluruh domain / DB Anda. Dengan menggunakan antarmuka, kita dapat menguraikan satu agregat (yang kemungkinan terlalu besar untuk dimuat) menjadi beberapa agregat root yang hanya menyediakan tindakan tertentu (seperti memesan atau memberikan umpan balik). Anda dapat melihat agregat dalam implementasi saya dapat KEDUA memesan dan memberikan umpan balik, tetapi tidak dapat digunakan untuk mengatur ulang kata sandi atau mengubah nama pengguna.
Jadi jawaban untuk pertanyaan Anda adalah bahwa agregat harus memvalidasi diri mereka sendiri. Jika mereka tidak dapat Anda kemungkinan memiliki model yang kurang.
sumber