Apakah isolasi model domain / kegigihan biasanya canggung ini?

12

Saya menyelami konsep-konsep Desain Domain-Driven (DDD) dan menemukan beberapa prinsip aneh, terutama mengenai isolasi domain dan model ketekunan. Inilah pemahaman dasar saya:

  1. Layanan pada lapisan aplikasi (menyediakan serangkaian fitur) meminta objek domain dari repositori yang diperlukan untuk menjalankan fungsinya.
  2. Implementasi konkret dari repositori ini mengambil data dari penyimpanan yang diimplementasikan untuknya
  3. Layanan memberi tahu objek domain, yang merangkum logika bisnis, untuk melakukan tugas-tugas tertentu yang mengubah keadaannya.
  4. Layanan memberi tahu repositori untuk tetap mempertahankan objek domain yang dimodifikasi.
  5. Repositori perlu memetakan objek domain kembali ke representasi terkait dalam penyimpanan.

Ilustrasi Aliran

Sekarang, dengan asumsi di atas, yang berikut ini tampak canggung:

Iklan 2 .:

Model domain tampaknya memuat seluruh objek domain (termasuk semua bidang dan referensi), bahkan jika mereka tidak diperlukan untuk fungsi yang memintanya. Memuat seluruhnya mungkin bahkan tidak mungkin jika objek domain lain direferensikan, kecuali jika Anda memuat objek domain tersebut juga dan semua objek yang dirujuk secara bergantian, dan seterusnya dan seterusnya. Pemuatan malas muncul di pikiran, yang berarti bahwa Anda mulai menanyakan objek domain Anda yang seharusnya menjadi tanggung jawab repositori.

Mengingat masalah ini, cara "benar" memuat objek domain tampaknya memiliki fungsi pemuatan khusus untuk setiap kasus penggunaan. Fungsi-fungsi khusus ini hanya akan memuat data yang diperlukan oleh use case mereka dirancang untuk. Di sinilah kejanggalan berperan: Pertama, saya harus mempertahankan sejumlah besar fungsi pemuatan untuk setiap implementasi repositori, dan objek domain akan berakhir di keadaan tidak lengkap yang dibawa nulldi bidangnya. Yang terakhir seharusnya secara teknis tidak menjadi masalah karena jika suatu nilai tidak dimuat, itu seharusnya tidak diperlukan oleh fungsi yang memintanya. Masih canggung dan potensi bahaya.

Iklan 3 .:

Bagaimana objek domain memverifikasi keunikan kendala pada konstruksi jika tidak memiliki gagasan tentang repositori? Misalnya, jika saya ingin membuat yang baru Userdengan nomor jaminan sosial yang unik (yang diberikan), konflik paling awal akan terjadi setelah meminta repositori untuk menyimpan objek, hanya jika ada batasan keunikan yang ditentukan pada database. Kalau tidak, saya bisa mencari Userdengan jaminan sosial yang diberikan dan melaporkan kesalahan jika ada, sebelum membuat yang baru. Tapi kemudian cek kendala akan tinggal di layanan dan bukan di objek domain tempat mereka berada. Saya baru menyadari bahwa objek domain sangat diperbolehkan menggunakan repositori (disuntikkan) untuk validasi.

Iklan 5 .:

Saya melihat pemetaan objek domain ke backend penyimpanan sebagai proses yang padat karya dibandingkan dengan objek domain yang memodifikasi data yang mendasari secara langsung. Tentu saja, ini merupakan prasyarat penting untuk memisahkan implementasi penyimpanan konkret dari kode domain. Namun, apakah ini memang mahal?

Anda tampaknya memiliki opsi untuk menggunakan alat ORM untuk melakukan pemetaan untuk Anda. Ini sering mengharuskan Anda untuk merancang model domain sesuai dengan batasan ORM, namun, atau bahkan memperkenalkan ketergantungan dari domain ke lapisan infrastruktur (misalnya, dengan menggunakan anotasi ORM dalam objek domain). Saya juga pernah membaca bahwa ORM memperkenalkan overhead komputasi yang cukup besar.

Dalam kasus database NoSQL, yang hampir tidak ada konsep seperti ORM, bagaimana Anda melacak properti yang diubah dalam model domain save()?

Sunting : Juga, agar repositori dapat mengakses keadaan objek domain (yaitu nilai dari setiap bidang), objek domain perlu mengungkapkan status internalnya yang memecah enkapsulasi.

Secara umum:

  • Ke mana logika transaksional pergi? Ini tentu saja merupakan kegigihan yang spesifik. Beberapa infrastruktur penyimpanan mungkin bahkan tidak mendukung transaksi sama sekali (seperti repositori tiruan dalam memori).
  • Untuk operasi massal yang memodifikasi banyak objek, apakah saya harus memuat, memodifikasi, dan menyimpan setiap objek secara terpisah untuk dapat melalui logika validasi yang dienkapsulasi objek? Itu bertentangan dengan mengeksekusi permintaan tunggal langsung ke database.

Saya akan sangat menghargai beberapa klarifikasi tentang topik ini. Apakah asumsi saya benar? Jika tidak, apa cara yang benar untuk mengatasi masalah ini?

Ganda M
sumber
1
Poin dan pertanyaan yang bagus, saya juga tertarik pada itu. Satu catatan sisi - jika Anda memodelkan agregat dengan benar yang berarti bahwa pada waktu keberadaan tertentu, contoh agregat harus dalam keadaan valid - itu adalah titik utama agregat (dan tidak menggunakan agregat sebagai wadah komposisi). Itu juga berarti bahwa untuk mengembalikan agregat dari data DB, repositori itu sendiri biasanya harus menggunakan konstruktor dan rangkaian operasi mutasi tertentu, dan saya tidak melihat bagaimana ORM dapat secara otomatis mengetahui cara melakukan operasi tersebut secara ajaib. .
Dusan
2
Yang bahkan lebih mengecewakan adalah bahwa pertanyaan-pertanyaan seperti Anda sering ditanyakan, tetapi, sepengetahuan saya - ada NOL contoh penerapan agregat dan repositori yang ada di buku
Dusan

Jawaban:

5

Pemahaman dasar Anda benar dan arsitektur yang Anda sketsa bagus dan berfungsi dengan baik.

Membaca yang tersirat sepertinya Anda berasal dari gaya pemrograman catatan aktif yang lebih berbasis database? Untuk mencapai implementasi yang berjalan saya akan mengatakan Anda perlu

1: Objek domain tidak harus menyertakan seluruh grafik objek. Misalnya saya dapat memiliki:

public class Customer
{
    public string AddressId {get;set;}
    public string Name {get;set;}
}

public class Address
{
    public string Id {get;set;}
    public string HouseNumber {get;set;
}

Alamat dan Pelanggan hanya perlu menjadi bagian dari agregat yang sama jika Anda memiliki logika seperti, "nama pelanggan hanya dapat dimulai dengan huruf yang sama dengan nama rumah". Anda benar untuk menghindari pemuatan yang malas dan versi objek Lite.

2: Kendala keunikan umumnya adalah bidang repositori, bukan objek domain. Jangan menyuntikkan repositori ke Objek Domain, itu adalah langkah kembali ke catatan aktif, cukup kesalahan ketika layanan mencoba untuk menyimpan.

Aturan bisnis bukan "Tidak ada dua contoh Pengguna dengan SocialSecurityNumber yang sama dapat ada pada saat yang sama"

Itu karena mereka tidak bisa ada di repositori yang sama.

3: Tidaklah sulit untuk menulis repositori daripada metode pembaruan properti individual. Bahkan, Anda akan menemukan bahwa Anda memiliki kode yang hampir sama. Hanya kelas mana Anda memasukkannya.

ORM hari ini mudah dan tidak memiliki kendala tambahan pada kode Anda. Karena itu, secara pribadi saya lebih suka hanya tangan engkol SQL. Tidak sulit, Anda tidak pernah mengalami masalah dengan fitur ORM dan Anda dapat mengoptimalkan jika diperlukan.

Benar-benar tidak perlu untuk melacak properti mana yang berubah saat Anda menyimpan. Pertahankan Objek Domain Anda kecil dan cukup timpa versi lama.

Pertanyaan Umum

  1. Logika transaksi berlaku di repositori. Tetapi Anda tidak harus memiliki banyak jika ada. Tentu Anda memerlukan beberapa jika Anda memiliki tabel anak di mana Anda meletakkan objek anak-anak dari agregat, tapi itu akan sepenuhnya dienkapsulasi dalam metode repositori SaveMyObject.

  2. Pembaruan massal. Ya, Anda harus mengubah masing-masing objek secara individual lalu tambahkan saja metode SaveMyObjects (Daftar objek) ke repositori Anda, untuk melakukan pembaruan massal.

    Anda ingin Objek Domain atau Layanan Domain mengandung logika. Bukan basis data. Itu berarti Anda tidak bisa begitu saja "memperbarui nama set pelanggan = x di mana y", karena untuk semua yang Anda tahu objek Pelanggan, atau CustomerUpdateService melakukan 20 hal aneh ketika Anda mengubah nama.

Ewan
sumber
Jawaban yang bagus Anda benar sekali, saya terbiasa dengan gaya penulisan kode aktif, yang mengapa pola repositori tampak aneh pada pandangan pertama. Namun, bukankah objek domain "lean" ( AddressIdbukan Address) bertentangan dengan prinsip OO?
Dobel M
tidak, Anda masih memiliki objek Alamat, hanya saja bukan anak Pelanggan
Ewan
pemetaan objek iklan tanpa perubahan pelacakan, perangkat
Double M
2

Jawaban singkat: Pemahaman Anda benar dan pertanyaan-pertanyaan yang Anda miliki menunjukkan masalah-masalah valid yang solusinya tidak langsung atau diterima secara universal.

Butir 2 .: (memuat grafik objek lengkap)

Saya bukan yang pertama menunjukkan bahwa ORM tidak selalu merupakan solusi yang baik. Masalah utama adalah bahwa ORM tidak tahu apa-apa tentang kasus penggunaan yang sebenarnya, sehingga mereka tidak tahu harus memuat apa atau bagaimana mengoptimalkan. Ini adalah sebuah masalah.

Seperti yang Anda katakan, solusi yang jelas adalah memiliki metode kegigihan untuk setiap kasus penggunaan. Tetapi jika Anda masih menggunakan ORM untuk itu, ORM akan memaksa Anda untuk mengemas semuanya menjadi objek data. Yang terlepas dari tidak benar-benar berorientasi objek, sekali lagi bukan desain terbaik untuk beberapa kasus penggunaan.

Bagaimana jika saya hanya ingin memperbarui sebagian catatan? Mengapa saya membutuhkan representasi objek untuk semua catatan? Dll

Jadi solusi untuk itu adalah tidak menggunakan ORM untuk kasus penggunaan yang tidak cocok. Terapkan kasus penggunaan "secara alami" sebagaimana adanya, yang kadang-kadang tidak memerlukan "abstraksi" tambahan dari data itu sendiri (objek data) atau abstraksi atas "tabel" (repositori).

Memiliki objek data yang setengah terisi atau mengganti referensi objek dengan "id" adalah solusi terbaik, bukan desain yang baik, seperti yang Anda tunjukkan.

Angka 3 .: (memeriksa batasan)

Jika kegigihan tidak diabstraksikan, setiap use-case jelas dapat memeriksa kendala apa pun yang diinginkan dengan mudah. Persyaratan bahwa objek tidak tahu "repositori" sepenuhnya buatan dan bukan masalah teknologi.

Butir 5 .: (ORM)

Tentu saja, ini merupakan prasyarat penting untuk memisahkan implementasi penyimpanan konkret dari kode domain. Namun, apakah ini memang mahal?

Tidak, tidak. Ada banyak cara lain untuk bertahan. Masalahnya adalah bahwa ORM dipandang sebagai "solusi" untuk digunakan, selalu (setidaknya untuk database relasional). Mencoba menyarankan untuk tidak menggunakannya untuk beberapa kasus penggunaan dalam suatu proyek adalah sia-sia dan tergantung pada ORM itu sendiri kadang-kadang bahkan tidak mungkin, karena cache dan eksekusi yang terlambat kadang-kadang digunakan oleh alat-alat ini.

Pertanyaan umum 1 .: (transaksi)

Saya kira tidak ada solusi tunggal. Jika desain Anda berorientasi objek, akan ada metode "top" untuk setiap use case. Transaksi harus ada di sana.

Pembatasan lain sepenuhnya buatan.

Pertanyaan umum 2 .: (operasi massal)

Dengan ORM, Anda (untuk sebagian besar ORM yang saya tahu) dipaksa untuk melalui objek individual. Ini sama sekali tidak perlu dan mungkin bukan desain Anda jika tangan Anda tidak akan diikat oleh ORM.

Persyaratan untuk memisahkan "logika" dari SQL berasal dari ORM. Mereka harus mengatakan itu, karena mereka tidak dapat mendukungnya. Itu tidak secara inheren "buruk".

Ringkasan

Saya kira maksud saya adalah ORM tidak selalu merupakan alat terbaik untuk suatu proyek, dan bahkan jika itu, ORM sangat tidak mungkin menjadi yang terbaik untuk semua kasus penggunaan dalam suatu proyek.

Demikian pula, abstraksi dataobject-repositori dari DDD tidak selalu yang terbaik juga. Saya bahkan akan mengatakan lebih jauh, mereka jarang desain yang optimal.

Itu membuat kita tidak memiliki solusi satu ukuran untuk semua, jadi kita harus memikirkan solusi untuk setiap kasus penggunaan secara individual, yang bukan berita baik dan jelas membuat pekerjaan kita lebih sulit :)

Robert Bräutigam
sumber
Poin yang sangat menarik di sana, terima kasih telah mengkonfirmasi asumsi saya. Anda bilang ada banyak cara lain untuk memiliki kegigihan. Bisakah Anda merekomendasikan pola desain pemain untuk digunakan dengan basis data grafik (tanpa ORM), yang masih akan memberikan PI?
Double M
1
Saya sebenarnya akan mempertanyakan apakah Anda memerlukan isolasi (dan apa jenisnya) di tempat pertama. Mengisolasi per teknologi (mis. Basis data, ui, dll.) Membawa secara otomatis "kecanggungan" yang Anda coba hindari, untuk keuntungan penggantian teknologi basis data yang agak lebih mudah. Namun biaya adalah perubahan yang lebih sulit dari logika bisnis karena itu menyebar melalui lapisan. Atau Anda dapat membagi fungsi bisnis, yang akan membuat perubahan basis data lebih sulit, tetapi mengubah logika lebih mudah. Yang mana yang benar-benar Anda inginkan?
Robert Bräutigam
1
Anda bisa mendapatkan kinerja terbaik jika Anda hanya memodelkan domain (yaitu fungsi bisnis), dan tidak abstrak database (apakah relasional atau grafik tidak masalah). Karena database tidak disarikan dari use-case, use-case dapat mengimplementasikan query / pembaruan yang paling optimal yang diinginkannya, dan tidak perlu melalui beberapa model objek yang canggung untuk mencapai apa yang diinginkannya.
Robert Bräutigam
Nah, tujuan utama adalah untuk menjaga keprihatinan dari kegigihan dari logika bisnis, agar memiliki kode bersih yang mudah dipahami, diperluas dan diuji. Mampu menukar teknologi DB hanyalah bonus. Saya bisa melihat bahwa jelas ada gesekan antara efisiensi dan ketidaktahuan, yang tampaknya lebih kuat dengan DB grafik karena pertanyaan kuat yang Anda dapat (tetapi tidak diizinkan) gunakan.
Dobel M
1
Sebagai pengembang Java Enterprise saya dapat memberitahu Anda bahwa kami telah mencoba memisahkan kegigihan dari logika selama dua dekade terakhir. Itu tidak bekerja. Pertama, pemisahan tidak pernah benar-benar tercapai. Bahkan hari ini, ada segala macam hal yang berhubungan dengan basis data di objek yang dianggap "bisnis", yang utama adalah id basis data (dan banyak anotasi basis data). Kedua, seperti yang Anda katakan, kadang-kadang logika bisnis dijalankan pada basis data. Ketiga, itulah alasan kami memiliki basis data khusus, untuk dapat memuat beberapa logika yang sebaiknya dilakukan di tempat data berada.
Robert Bräutigam