Arsitektur bersih Paman Bob - Kelas entitas / model untuk setiap lapisan?

44

LATAR BELAKANG :

Saya mencoba menggunakan arsitektur bersih Paman Bob di aplikasi android saya. Saya mempelajari banyak proyek open source yang mencoba menunjukkan cara yang tepat untuk melakukannya, dan saya menemukan implementasi yang menarik berdasarkan RxAndroid.

Apa yang saya perhatikan:

Di setiap lapisan (presentasi, domain, dan data), ada kelas model untuk entitas yang sama (UML yang berbicara). Plus, ada kelas mapper yang menangani transformasi objek setiap kali data melewati batas (dari layer ke yang lain).

PERTANYAAN :

Apakah harus memiliki kelas model di setiap lapisan ketika saya tahu bahwa mereka semua akan berakhir dengan atribut yang sama jika semua operasi CRUD diperlukan? Atau, apakah itu aturan atau praktik terbaik saat menggunakan arsitektur bersih?

Rami Jemli
sumber

Jawaban:

52

Menurut pendapat saya, itu sama sekali tidak seperti yang dimaksudkan. Dan itu pelanggaran KERING.

Idenya adalah bahwa entitas / objek domain di tengah dimodelkan untuk mewakili domain sebaik dan senyaman mungkin. Ini adalah pusat dari segalanya dan semuanya dapat bergantung padanya karena domain itu sendiri tidak berubah sebagian besar waktu.

Jika basis data Anda di luar dapat menyimpan objek-objek itu secara langsung, maka memetakannya ke format lain demi memisahkan lapisan tidak hanya sia-sia tetapi membuat duplikat model dan itu bukan maksudnya.

Untuk memulainya, arsitektur bersih dibuat dengan lingkungan / skenario khas yang berbeda dalam pikiran. Aplikasi server bisnis dengan lapisan luar raksasa yang membutuhkan jenis objek khusus mereka sendiri. Misalnya, database yang menghasilkan SQLRowobjek dan perlu SQLTransactionsuntuk memperbarui item. Jika Anda menggunakan yang ada di tengah, Anda melanggar arah ketergantungan karena inti Anda akan bergantung pada database.

Dengan ORM ringan yang memuat dan menyimpan objek entitas, bukan itu masalahnya. Mereka melakukan pemetaan antara internal SQLRowdan domain Anda. Bahkan jika Anda perlu memasukkan @Entitiyanotasi ORM ke objek domain Anda, saya berpendapat bahwa ini tidak membuat "penyebutan" lapisan luar. Karena anotasi hanyalah metadata, tidak ada kode yang tidak secara khusus mencari mereka yang akan melihatnya. Dan yang lebih penting, tidak ada yang perlu diubah jika Anda menghapusnya atau menggantinya dengan anotasi database yang berbeda.

Sebaliknya, jika Anda mengubah domain Anda dan membuat semua pemetaan itu, Anda harus banyak berubah.


Amandemen: Di atas sedikit terlalu disederhanakan dan bahkan bisa salah. Karena ada bagian dalam arsitektur bersih yang ingin Anda membuat representasi per lapisan. Tapi itu harus dilihat dalam konteks aplikasi.

Yaitu berikut ini di sini https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

Yang penting adalah bahwa struktur data yang terisolasi, sederhana, dilewatkan melintasi batas. Kami tidak ingin menipu dan melewati baris Entitas atau Database. Kami tidak ingin struktur data memiliki segala jenis ketergantungan yang melanggar Aturan Ketergantungan.

Melewati entitas dari pusat ke arah lapisan luar tidak melanggar aturan dependensi, namun disebutkan. Tetapi ini memiliki alasan dalam konteks aplikasi yang dibayangkan. Melewati entitas di sekitar akan memindahkan logika aplikasi ke arah luar. Lapisan luar perlu tahu bagaimana menafsirkan objek dalam, mereka harus melakukan apa yang seharusnya dilakukan lapisan dalam seperti "use case".

Selain itu, ia juga memisahkan lapisan sehingga perubahan ke inti tidak perlu memerlukan perubahan di lapisan luar (lihat komentar SteveCallender). Dalam konteks itu, mudah untuk melihat bagaimana objek harus mewakili secara spesifik tujuan mereka digunakan. Lapisan itu juga harus berbicara satu sama lain dalam hal objek yang dibuat khusus untuk tujuan komunikasi ini. Ini bahkan dapat berarti bahwa ada 3 representasi, 1 di setiap lapisan, 1 untuk transportasi antar lapisan.

Dan ada https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html yang membahas di atas:

Orang lain khawatir bahwa hasil bersih dari saran saya akan menjadi banyak kode duplikat, dan banyak menyalin data secara hafal dari satu struktur data ke yang lain di seluruh lapisan sistem. Tentu saya juga tidak menginginkan ini; dan tidak ada yang saya sarankan akan mengarah pada pengulangan struktur data dan banyak sekali penyalinan di lapangan.

Itu IMO menyiratkan bahwa menyalin objek 1: 1 polos adalah bau dalam arsitektur karena Anda tidak benar-benar menggunakan lapisan yang tepat dan / atau abstraksi.

Dia kemudian menjelaskan bagaimana dia membayangkan semua "penyalinan"

Anda memisahkan UI dari aturan bisnis dengan mengirimkan struktur data sederhana di antara keduanya. Anda tidak membiarkan pengendali Anda tahu apa-apa tentang aturan bisnis. Sebagai gantinya, pengendali membongkar objek HttpRequest ke dalam struktur data vanilla sederhana, dan kemudian meneruskan struktur data itu ke objek interaksor yang mengimplementasikan use case dengan menggunakan objek bisnis. Interaksi kemudian mengumpulkan data respons ke dalam struktur data vanili lain dan meneruskannya kembali ke UI. Pandangan tidak tahu tentang objek bisnis. Mereka hanya melihat struktur data itu dan menyajikan responsnya.

Dalam aplikasi ini, ada perbedaan besar antara representasi. Data yang mengalir bukan hanya entitas. Dan ini menjamin dan menuntut kelas yang berbeda.

Namun, diterapkan pada aplikasi Android sederhana seperti penampil foto di mana Photoentitas memiliki sekitar 0 aturan bisnis dan "kasus penggunaan" yang berkaitan dengannya hampir tidak ada dan sebenarnya lebih peduli tentang caching & mengunduh (proses yang seharusnya IMO dilakukan diwakili secara lebih eksplisit), titik untuk membuat representasi terpisah dari suatu foto mulai menghilang. Saya bahkan merasa bahwa foto itu sendiri adalah objek transfer data sementara lapisan bisnis-logika-inti-nyata tidak ada.

Ada perbedaan antara "pisahkan UI dari aturan bisnis dengan melewatkan struktur data sederhana antara keduanya" dan "saat Anda ingin menampilkan foto, ganti nama itu 3 kali dalam perjalanan" .

Selain itu, titik di mana saya melihat aplikasi demo gagal mewakili arsitektur bersih adalah bahwa mereka menambahkan penekanan besar pada pemisahan lapisan demi pemisahan lapisan tetapi secara efektif menyembunyikan apa yang dilakukan aplikasi. Itu berbeda dengan apa yang dikatakan di https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html - yaitu

arsitektur aplikasi perangkat lunak berteriak tentang kasus penggunaan aplikasi

Saya tidak melihat penekanan pada pemisahan lapisan dalam arsitektur bersih. Ini tentang arah ketergantungan dan fokus pada mewakili inti dari aplikasi - entitas dan kasus penggunaan - di Jawa polos idealnya tanpa ketergantungan ke luar. Ini bukan soal ketergantungan pada inti itu.

Jadi jika aplikasi Anda benar-benar memiliki inti yang mewakili aturan bisnis dan kasus penggunaan, dan / atau orang yang berbeda bekerja pada lapisan yang berbeda, harap pisahkan dengan cara yang dimaksud. Jika Anda di sisi lain hanya menulis aplikasi sederhana sendiri jangan berlebihan. 2 lapisan dengan batas lancar mungkin lebih dari cukup. Dan lapisan dapat ditambahkan nanti juga.

zapl
sumber
1
@ RamiJemli Idealnya, entitas sama di semua aplikasi. Itulah perbedaan antara "aturan bisnis perusahaan" dan "aturan bisnis aplikasi" (terkadang logika bisnis vs aplikasi). Inti adalah representasi yang sangat abstrak dari entitas Anda yang cukup umum sehingga Anda dapat menggunakannya di mana saja. Bayangkan sebuah bank yang memiliki banyak aplikasi, satu untuk dukungan pelanggan, satu berjalan di mesin uang, satu sebagai web ui untuk pelanggan sendiri. Semua itu bisa menggunakan yang sama BankAccounttetapi dengan aturan khusus aplikasi apa yang dapat Anda lakukan dengan akun itu.
4
Saya pikir poin penting dalam arsitektur bersih adalah bahwa dengan menggunakan lapisan adaptor antarmuka untuk mengkonversi (atau seperti yang Anda katakan peta) antara representasi lapisan yang berbeda dari entitas Anda mengurangi ketergantungan pada entitas tersebut. Jika ada perubahan dalam lapisan Usecase atau Entity (mudah-mudahan tidak mungkin tetapi karena persyaratan mengubah lapisan ini akan), maka dampak perubahan terkandung dalam lapisan adaptor. Jika Anda memilih untuk menggunakan representasi entitas yang sama di seluruh arsitektur Anda, dampak dari perubahan ini akan jauh lebih besar.
SteveCallender
1
@ RamiJemli ada baiknya menggunakan kerangka kerja yang membuat hidup lebih sederhana, intinya adalah bahwa Anda harus berhati-hati ketika arsitektur Anda bergantung pada mereka dan Anda mulai menempatkan mereka di pusat segalanya. Ini bahkan ada artikel tentang blog RxJava.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html - itu tidak mengatakan Anda tidak boleh menggunakannya. Ini lebih seperti: Saya pernah melihat ini, akan berbeda dalam satu tahun dan ketika aplikasi Anda masih ada Anda terjebak dengannya. Jadikan detail dan lakukan hal-hal paling penting di java tua polos sambil menerapkan prinsip-prinsip SOLID tua polos.
zapl
1
@ zapl Apakah Anda merasakan hal yang sama tentang lapisan layanan web? Dengan kata lain, apakah Anda akan menempatkan @SerializedNameanotasi Gson pada model domain? Atau apakah Anda akan membuat objek baru yang bertanggung jawab untuk memetakan respons web terhadap model domain?
tir38
2
@ tir38 Pemisahan itu sendiri tidak memberikan manfaat, itu adalah biaya dari perubahan di masa depan yang turun dengannya. => Tergantung pada aplikasi. 1) Anda perlu waktu untuk membuat & memelihara tahap tambahan yang mengubah antara representasi yang berbeda. Misalnya menambahkan bidang ke domain dan lupa menambahkannya di tempat lain bukanlah hal yang tidak pernah terjadi. Tidak dapat terjadi dengan pendekatan sederhana. 2) Biayanya untuk transisi ke pengaturan yang lebih kompleks nantinya jika Anda membutuhkannya. Menambahkan lapisan tidak mudah, karena itu lebih mudah dalam aplikasi besar untuk membenarkan lebih banyak lapisan yang tidak diperlukan segera
zapl
7

Anda benar melakukannya dengan benar. Dan tidak ada pelanggaran KERING karena Anda menerima SRP.

Sebagai contoh: Anda memiliki metode-bisnis createX (nama string) maka Anda dapat memiliki metode createX (nama string) di DAO-Layer, disebut dalam metode bisnis. Mereka mungkin memiliki tanda tangan yang sama dan mungkin hanya ada delegasi tetapi mereka memiliki tujuan yang berbeda. Anda juga dapat memiliki createX (nama String) pada UseCase. Bahkan saat itu tidak mubazir. Yang saya maksud dengan ini adalah: Tanda tangan yang sama tidak berarti semantik yang sama. Pilih nama lain agar semantik jelas. Memberi nama sendiri ini tidak mempengaruhi SRP sama sekali.

UseCase bertanggung jawab atas logika khusus aplikasi, objek bisnis bertanggung jawab atas logika independen aplikasi dan DAO bertanggung jawab untuk menyimpan.

Karena perbedaan semantik semua lapisan dapat memiliki representasi dan model komunikasi mereka sendiri. Seringkali Anda melihat "entitas" sebagai "objek bisnis" dan sering kali Anda tidak melihat keharusan untuk memisahkannya. Tetapi dalam proyek-proyek "besar", upaya harus diambil untuk memisahkan lapisan-lapisan tersebut dengan benar. Semakin besar proyek semakin besar kemungkinan Anda membutuhkan semantik yang berbeda diwakili dalam berbagai lapisan dan kelas.

Anda dapat memikirkan berbagai aspek dari semantik yang sama. Objek-Pengguna harus ditampilkan di layar, ia memiliki beberapa aturan konsistensi batin dan harus disimpan di suatu tempat. Setiap aspek harus diwakili dalam kelas yang berbeda (SRP). Membuat pembuat peta bisa sangat menyebalkan, jadi di sebagian besar proyek yang saya kerjakan, aspek-aspek ini dilebur menjadi satu kelas. Ini jelas merupakan pelanggaran terhadap SRP tetapi tidak ada yang benar-benar peduli.

Saya menyebut aplikasi arsitektur bersih dan SOLID "tidak dapat diterima secara sosial". Saya akan bekerja dengannya jika saya diizinkan. Saat ini saya tidak diperbolehkan melakukannya. Saya menunggu saat kita harus berpikir tentang mengambil SOLID dengan serius.

pengguna205407
sumber
Saya pikir tidak ada metode di lapisan Data yang harus memiliki tanda tangan yang sama seperti metode di lapisan Domain. Di lapisan Domain, Anda menggunakan konvensi penamaan yang terkait dengan bisnis seperti pendaftaran atau masuk, dan di lapisan Data, Anda menggunakan save (jika pola DAO), atau menambahkan (jika Repositori karena pola ini menggunakan Koleksi sebagai metafora). Akhirnya, saya tidak berbicara tentang entitas (Data) dan model (Domain), saya menyoroti kegunaan dari UserModel dan Mappernya (lapisan presentasi). Anda bisa memanggil kelas pengguna dari domain di dalam presentasi dan ini tidak melanggar aturan dependensi.
Rami Jemli
Saya setuju dengan Rami, mapper tidak perlu karena Anda dapat melakukan pemetaan langsung dalam implementasi interaktor.
Christopher Perry
5

Tidak, Anda tidak perlu membuat kelas model di setiap layer.

Entity ( DATA_LAYER) - adalah representasi penuh atau sebagian dari objek Database.DATA_LAYER

Mapper ( DOMAIN_LAYER) - sebenarnya adalah kelas yang mengonversi Entity ke ModelClass, yang akan digunakan padaDOMAIN_LAYER

Lihatlah: https://github.com/lifedemons/photoviewer

sekarat
sumber
1
Tentu saja, saya tidak menentang entitas di lapisan data, tetapi, dalam contoh Anda, kelas PhotoModel di lapisan presentasi memiliki atribut yang sama dari kelas Foto di lapisan domain. Secara teknis kelasnya sama. apakah itu perlu?
Saya pikir ada sesuatu yang salah dalam contoh Anda karena lapisan domain tidak boleh bergantung pada lapisan lain seperti dalam contoh Anda pemetaan Anda bergantung pada entitas dalam lapisan data Anda yang IMO, itu harus sebaliknya
navid_gh