Praktik terbaik untuk serialisasi agregat DDD

23

Menurut logika domain DDD tidak boleh dicemari dengan masalah teknis seperti serialisasi, pemetaan objek-relasional, dll.

Jadi bagaimana Anda membuat cerita bersambung atau memetakan status agregat tanpa memaparkannya secara terbuka melalui getter dan setter? Saya telah melihat banyak contoh untuk implementasi repositori misalnya, tetapi praktis semuanya bergantung pada aksesor publik pada entitas dan objek nilai untuk pemetaan.

Kita dapat menggunakan refleksi untuk menghindari aksesor publik, tetapi IMO objek domain ini masih secara implisit akan tergantung pada masalah serialisasi. Misalnya Anda tidak dapat mengganti nama atau menghapus bidang pribadi tanpa mengubah konfigurasi serialisasi / pemetaan Anda. Jadi, Anda harus mempertimbangkan serialisasi di mana Anda seharusnya berfokus pada logika domain.

Jadi apa kompromi terbaik untuk diikuti di sini? Hidup dengan pengakses publik, tetapi hindari menggunakannya untuk hal lain selain memetakan kode? Atau apakah saya hanya melewatkan sesuatu yang jelas?

Saya secara eksplisit tertarik membuat serialisasi keadaan objek domain DDD (agregat yang terdiri dari entitas dan objek nilai). Ini BUKAN tentang serialisasi dalam skenario skrip transkasi umum atau di mana layanan stateless beroperasi pada objek wadah data sederhana.

EagleBeak
sumber

Jawaban:

12

Macam benda

Untuk keperluan diskusi kita, mari kita pisahkan objek kita menjadi tiga jenis:

Logika Domain Bisnis

Ini adalah objek yang selesai dikerjakan. Mereka memindahkan uang dari satu akun ke yang lain, memenuhi pesanan, dan semua tindakan lain yang kami harapkan dilakukan oleh perangkat lunak bisnis.

Objek logika domain biasanya tidak memerlukan aksesor (getter dan setter). Sebaliknya, Anda membuat objek dengan menyerahkan dependensi melalui konstruktor, dan kemudian memanipulasi objek melalui metode (katakan, jangan tanya).

Objek Transfer Data

Objek Transfer Data adalah keadaan murni; mereka tidak mengandung logika bisnis. Mereka akan selalu memiliki aksesor. Mereka mungkin atau mungkin tidak memiliki setter, tergantung pada apakah Anda sedang menulisnya atau tidak . Anda akan mengatur bidang Anda di konstruktor dan nilainya tidak akan berubah selama masa objek, atau pengakses Anda akan membaca / menulis. Dalam praktiknya, objek-objek ini biasanya bisa berubah, sehingga pengguna dapat mengeditnya.

Lihat objek Model

Lihat Model objek berisi representasi data yang dapat ditampilkan / diedit. Mereka mungkin berisi logika bisnis, biasanya terbatas pada validasi data. Contoh objek Model Tampilan mungkin adalah InvoiceViewModel, berisi objek Pelanggan, objek Header Faktur, dan Item Baris Faktur. Lihat objek Model selalu mengandung accessors.

Jadi satu-satunya jenis objek yang akan "murni" dalam arti bahwa itu tidak mengandung accessors bidang akan menjadi objek Domain Logic. Membuat serial objek seperti itu menyimpan "keadaan komputasi" saat ini, sehingga ia dapat diambil nanti untuk menyelesaikan pemrosesan. Lihat Model dan DTO dapat dengan bebas diserialisasi, tetapi dalam praktiknya data mereka biasanya disimpan ke database.

Serialisasi, dependensi dan sambungan

Memang benar bahwa serialisasi menciptakan dependensi, dalam arti bahwa Anda harus deserialize ke objek yang kompatibel, itu tidak selalu berarti bahwa Anda harus mengubah konfigurasi serialisasi Anda. Mekanisme serialisasi yang baik adalah tujuan umum; mereka tidak peduli jika Anda mengubah nama properti atau anggota, asalkan masih dapat memetakan nilai kepada anggota. Dalam praktiknya, ini hanya berarti bahwa Anda harus membuat ulang serialisasi objek contoh untuk membuat representasi serialisasi (xml, json, apa pun) yang kompatibel dengan objek baru Anda; tidak perlu ada perubahan konfigurasi pada serializer.

Memang benar bahwa objek tidak harus peduli dengan bagaimana mereka serial. Anda telah menjelaskan satu cara masalah tersebut dapat dipisahkan dari kelas domain: refleksi. Tapi serializer harus peduli tentang bagaimana serialisasi dan deserializes objek; itu, bagaimanapun, adalah fungsinya. Cara Anda menjaga objek Anda dipisahkan dari proses serialisasi Anda adalah membuat serialisasi fungsi tujuan umum , mampu bekerja di semua jenis objek.

Salah satu hal yang membuat orang bingung adalah decoupling harus terjadi di kedua arah. Itu tidak; itu hanya harus bekerja dalam satu arah. Dalam praktiknya, Anda tidak akan pernah bisa lepas sepenuhnya; selalu ada beberapa kopling. Tujuan dari kopling longgar adalah untuk membuat pemeliharaan kode lebih mudah, bukan untuk menghapus semua dependensi.

Robert Harvey
sumber
Saya setuju dengan pandangan Anda tentang decoupling. Serializer tergantung pada objek domain dan itu OK. Tapi tidak sebaliknya. Namun saya tidak setuju dengan pandangan Anda tentang aksesor publik pada objek domain. Dalam praktiknya mereka sering memilikinya, ya. Tapi IMO akan lebih disukai untuk mengimplementasikan logika domain dalam desain berorientasi objek yang bersih: Katakan, jangan tanya . Tetapi Anda masih membutuhkan pengakses untuk keperluan pemetaan (ORM, serialisasi, GUI ...). Dan itu yang ingin saya hindari, jika memungkinkan.
EagleBeak
Bagaimana Anda berencana mengakses bidang Anda, jika Anda tidak memiliki aksesor?
Robert Harvey
Sebenarnya saya mengacu pada tak satu pun dari tiga jenis objek yang Anda uraikan, tetapi untuk "agregat" dalam terminologi DDD dan sub objeknya (enities, objek nilai). Saya menyadari sekarang, bahwa pertanyaan saya tidak cukup eksplisit tentang ini. Maaf! Silakan lihat hasil edit saya di atas.
EagleBeak
1
Ini pada dasarnya masalah yang belum terpecahkan - Anda tidak dapat memiliki enkapsulasi, decoupling, dan serialisasi \ enkode sekaligus mengekspos DTO, adalah salah satu cara untuk mencapai kompromi. Namun, ada cara yang jauh lebih tidak mengganggu: yegor256.com/2016/07/06/data-transfer-object.html
Basilevs
1
Itu menjatuhkan enkapsulasi, siapa pun dapat menerapkan atau menggunakan kelas teman untuk membaca objek internal,.
Basilevs
-1

Tujuan mendasar dari serialisasi adalah untuk memastikan bahwa data yang dihasilkan oleh satu sistem dapat dikonsumsi oleh satu atau lebih sistem yang kompatibel.

Pendekatan serialisasi yang termudah dan paling kuat adalah menerjemahkan data ke dalam format agnostik tipe yang mempertahankan struktur dalam format yang sederhana dan mudah dikonsumsi. Misalnya format serialisasi yang paling umum di mana-mana (yaitu JSON, XML) menggunakan format berbasis teks yang terdefinisi dengan baik. Teks mudah diproduksi, dikirim, dan dikonsumsi.

Ada 2 alasan mengapa menggunakan salah satu format ini mungkin tidak ideal.

  1. Efisiensi

    Ada biaya yang melekat yang terlibat dalam menerjemahkan semua data ke dalam padanan berbasis teks mereka. Jenis data tidak akan ada jika teks adalah cara paling efisien untuk mengekspresikan semua bentuk data yang berbeda. Selain itu, struktur format ini tidak ideal untuk mengambil subset data secara asinkron atau sebagian.

    Misalnya, XML dan JSON mengasumsikan bahwa data yang digunakan akan ditulis dan dibaca dari awal hingga selesai. Untuk memproses set data yang sangat besar di mana memori langka, sistem yang mengonsumsi data mungkin memerlukan kemampuan untuk memproses data di bagian-bagian. Dalam hal itu, implementasi serialisasi / deserialisasi tujuan khusus mungkin diperlukan untuk menangani data.

  2. Presisi

    Casting yang dibutuhkan untuk membuat serial / deserialize data dari tipe yang dimaksudkan untuk tipe data agnostik mengakibatkan hilangnya presisi.

Orang bisa berpendapat bahwa menghasilkan representasi biner dari objek dan data jelas merupakan solusi yang paling efisien dan akurat. Kelemahan utama adalah bahwa implementasi semua sistem yang mengkonsumsi dan menghasilkan data harus tetap kompatibel. Ini adalah kendala sederhana dalam teori tetapi merupakan mimpi buruk untuk dipertahankan dalam praktik karena sistem produksi cenderung berubah / berkembang seiring waktu.

Dengan mengatakan itu. Memisahkan serialisasi / deserialisasi dari detail spesifik domain masuk akal sebagai aturan umum karena format tujuan umum lebih kuat, didukung lebih baik di berbagai sistem, dan memerlukan sedikit peningkatan biaya pemeliharaan untuk menggunakan.

Evan Plaice
sumber
Maaf, tetapi ini tidak menjawab pertanyaan saya. Ini tentang memisahkan objek domain dari serialisasi, bukan tentang alasan serialisasi atau pro dan kontra dari berbagai format. Bagaimana cara membuat serial objek objek tanpa mengungkap keadaan pribadi mereka secara publik?
EagleBeak
@EagleBeak Oh, saya tidak menyadari kekhawatiran Anda secara khusus tentang penanganan anggota pribadi. Dalam kasus Anda, Anda bisa membuat serial dalam biner (dengan asumsi sistem penerima mengikuti aturan / struktur yang sama dengan objek domain yang dibuat di bawah) atau menulis beberapa logika yang mengekstrak hanya data publik sebelum serialisasi.
Evan Plaice
Saya pikir asumsi 'biasa' adalah bahwa data yang diserialkan ke dalam format tujuan umum (ex xml, json) akan bersifat publik dan bahwa hak istimewa dikendalikan melalui API melalui ACL atau yang setara lainnya. Serialisasi / deserialisasi tujuan umum lebih sesuai dengan jalur decoupling data dari business logic yang berpindah dari satu sistem ke sistem lainnya.
Evan Plaice
Saya setuju tentang pengakses publik yang biasanya diasumsikan pada objek yang akan serial. Tapi saya masih ingin tahu lebih banyak tentang bagaimana ini berhubungan dengan DDD dan fokusnya yang kuat pada enkapsulasi logika domain. Apakah semua praktisi DDD hanya mengekspos keadaan model domain melalui aksesor publik untuk serialisasi (dan tidak pernah menyebutkannya dalam contoh mereka)? Aku meragukan itu. Tolong jangan salah paham. Saya sangat menghargai masukan Anda. Hanya saja saya tertarik pada aspek yang berbeda. (Sejauh ini saya tidak berpikir pertanyaan saya terlalu kabur, tetapi jawaban Robert Harvey dan pertanyaan Anda membuat saya berpikir tentang hal itu ..)
EagleBeak