Pengecualian dalam DDD


11

Saya belajar DDD dan saya berpikir untuk melempar pengecualian dalam situasi tertentu. Saya mengerti bahwa suatu objek tidak dapat masuk ke keadaan buruk jadi di sini pengecualiannya baik-baik saja, tetapi dalam banyak contoh pengecualian melempar juga misalnya jika kita mencoba menambahkan pengguna baru dengan email yang ada dalam database.

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

Jadi, jika pengguna dengan email ini ada, maka kita dapat dalam layanan aplikasi menangkap pengecualian, tetapi kita tidak seharusnya mengontrol operasi aplikasi menggunakan blok try-catch.

Bagaimana cara terbaik untuk ini?


1
Memiliki layanan domain (penangan) melempar pengecualian tidak masalah.
Andy

Jawaban:


20

Mari kita mulai dengan tinjauan singkat tentang ruang masalah: Salah satu prinsip dasar DDD adalah menempatkan aturan bisnis sedekat mungkin ke tempat-tempat di mana mereka perlu ditegakkan. Ini adalah konsep yang sangat penting karena membuat sistem Anda lebih "kohesif". Memindahkan aturan "ke atas" umumnya merupakan tanda model anemia; di mana objek hanya kantong data dan aturan disuntikkan dengan data yang akan ditegakkan.

Model anemia dapat membuat banyak akal bagi pengembang baru memulai dengan DDD. Anda membuat Usermodel dan EmailMustBeUnqiueRuleyang disuntikkan dengan informasi yang diperlukan untuk memvalidasi email. Sederhana. Anggun. Masalahnya adalah bahwa "pemikiran" semacam ini pada dasarnya bersifat prosedural. Bukan DDD. Apa yang akhirnya terjadi adalah Anda dibiarkan dengan modul dengan puluhan Rulesdibungkus rapi dan dikemas, tetapi mereka benar-benar tanpa konteks ke titik di mana mereka tidak lagi dapat diubah karena tidak jelas kapan / di mana mereka ditegakkan. Apakah itu masuk akal? Ini mungkin menjadi jelas bahwa EmailMustBeUnqiueRuleakan diterapkan pada penciptaan User, tapi bagaimana UserIsInGoodStandingRule?. Perlahan tapi pasti, grainularisasi mengekstraksiRulesdi luar konteksnya membuat Anda memiliki sistem yang sulit dipahami (dan karenanya tidak dapat diubah). Aturan harus dienkapsulasi hanya ketika crunching / eksekusi aktual begitu bertele-tele sehingga model Anda mulai kehilangan fokus.

Sekarang ke pertanyaan spesifik Anda: Masalah dengan memiliki Service/ CommandHandlermembuang Exceptionadalah bahwa logika bisnis Anda mulai bocor ("ke atas") dari domain Anda. Mengapa Anda Service/ CommandHandlerkebutuhan untuk mengetahui email harus unik? Lapisan layanan aplikasi umumnya digunakan untuk koordinasi daripada implementasi. Alasan untuk ini dapat diilustrasikan hanya jika kita menambahkan ChangeEmailmetode / perintah ke sistem Anda. Sekarang KEDUA metode / penangan perintah perlu menyertakan cek unik Anda. Di sinilah pengembang mungkin tergoda untuk "mengekstrak" suatu EmailMustBeUniqueRule. Seperti yang dijelaskan di atas, kami tidak ingin menempuh rute itu.

Beberapa tambahan pengetahuan bisa membawa kita ke jawaban DDD yang lebih banyak. Keunikan email adalah invarian yang harus diberlakukan di kumpulan Userobjek. Apakah ada konsep di domain Anda yang mewakili "kumpulan Userobjek"? Saya pikir Anda mungkin bisa melihat ke mana saya pergi ke sini.

Untuk kasus khusus ini (dan banyak lagi melibatkan penegakan invarian di koleksi) tempat terbaik untuk mengimplementasikan logika ini ada di Anda Repository. Ini sangat nyaman karena Anda Repositoryjuga "tahu" infrastruktur tambahan yang diperlukan untuk menjalankan validasi semacam ini (penyimpanan data). Dalam kasus Anda, saya akan menempatkan cek ini di addmetode. Ini masuk akal bukan? Secara konseptual, metode inilah yang benar-benar menambah Usersistem Anda. Menyimpan data adalah detail implementasi.


Bisakah saya bertanya apa maksud Anda Moving rules "upwards" is generally a sign of an anemic model? Ke atas artinya ke lapisan aplikasi? atau ke model domain?
Anyname Donotcare

1
"Ke atas" berarti ke arah lapisan aplikasi Anda (pikirkan arsitektur berlapis). Saya menyinggung ini di atas, tetapi alasan memindahkan aturan ke atas adalah tanda model anemia adalah bahwa hal itu mewakili pemisahan data dan perilaku dengan cara yang mengalahkan seluruh tujuan memiliki model domain inti. Jika memvalidasi email dalam modul terpisah daripada mengirim email, Emailmodel Anda harus berupa sekumpulan data yang diperiksa untuk invarian sebelum mengirim. Lihat: softwareengineering.stackexchange.com/questions/372338/…
slide sisi-raja

2
Memindahkan Domain Logic ke Repository salah. Anda harus menegakkan aturan domain dengan root agregat di lapisan domain.
Arash

1
@Arash Set validasi rumit, dan sering tidak cocok dengan DDD. Anda dibiarkan memilih untuk memvalidasi bahwa beberapa proses akan berfungsi sebelum mencoba proses itu (menambahkan a User), atau menerima bahwa jenis invarian khusus ini tidak akan dienkapsulasi dengan rapi di domain Anda. Yang pertama merupakan pemisahan data dan perilaku yang menghasilkan paradigma prosedural. Ini kurang ideal. Validasi harus ada dengan data, bukan di sekitar data. Selain itu, apa manfaatnya membuat layanan domain yang hanya berfungsi untuk memeriksa aturan yang satu ini? Seolah-olah, layanan ini ...
slide sisi-raja

1
@Arash memasangkan pasangan Anda Repository,, Userdan yang lainnya ini dan karenanya tidak mencapai visi murni yang Anda dorong. Implementasi repositori adalah bagian dari domain, dan karenanya dapat diberikan tanggung jawab tertentu.
slide sisi-raja

0

Anda dapat memaksakan validasi di setiap lapisan aplikasi Anda. Di mana untuk menentukan aturan mana yang tergantung pada aplikasi Anda.

Misalnya, entitas menerapkan metode yang mewakili aturan bisnis sementara kasus penggunaan menerapkan aturan khusus untuk aplikasi Anda.

Jika Anda membangun layanan e-mail seperti Gmail, orang dapat berargumentasi bahwa aturan "pengguna harus memiliki alamat e-mail unik" adalah aturan bisnis.

Jika Anda membangun proses pendaftaran untuk sistem CRM baru, aturan ini mungkin paling baik diterapkan dalam kasus penggunaan karena aturan ini juga cenderung menjadi bagian dari kasus penggunaan Anda. Selain itu, mungkin juga lebih mudah untuk menguji karena Anda dapat dengan mudah mematikan panggilan repositori dalam pengujian use case Anda.

Untuk keamanan tambahan, Anda juga bisa menerapkan aturan ini di repositori Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.