Cara membuat kode OO yang lebih baik dalam aplikasi berbasis relasional database di mana database dirancang dengan buruk

19

Saya menulis aplikasi web Java yang sebagian besar terdiri dari sekelompok halaman serupa di mana setiap halaman memiliki beberapa tabel dan filter yang berlaku untuk tabel tersebut. Data pada tabel ini berasal dari database SQL.

Saya menggunakan myBatis sebagai ORM, yang mungkin bukan pilihan terbaik dalam kasus saya, karena database dirancang dengan buruk dan mybatis adalah alat yang lebih berorientasi database.

Saya menemukan bahwa saya menulis banyak kode duplikat karena, karena desain database yang buruk, saya harus menulis pertanyaan yang berbeda untuk hal-hal yang sama karena pertanyaan itu bisa sangat berbeda. Yaitu, saya tidak bisa dengan mudah melakukan parametrik pada kueri. Ini menyebar ke kode saya dan bukannya mengisi baris pada kolom di tabel saya dengan loop sederhana saya punya kode seperti:

dapatkan A Data (p1, ..., pi);

dapatkan B Data (p1, ..., pi);

dapatkan data C (p1, ..., pi);

dapatkan D Data (p1, ..., pi); ...

Dan ini segera meledak ketika kita memiliki tabel berbeda dengan kolom berbeda.

Ini juga menambah kompleksitas fakta bahwa saya menggunakan "gawang", yang pada dasarnya merupakan pemetaan objek ke elemen html di halaman. Jadi kode Java saya menjadi adaptor antara basis data dan ujung depan, yang membuat saya membuat banyak kabel, kode boilerplate dengan beberapa logika yang saling terkait di dalamnya.

Apakah solusi yang tepat adalah membungkus pembuat peta ORM dengan extralayer yang menghadirkan antarmuka yang lebih homogen ke db atau apakah ada cara yang lebih baik untuk menangani kode spaghetti yang saya tulis ini?

EDIT: Info lebih lanjut tentang database

Basis data menampung informasi panggilan telepon. Desain yang buruk terdiri dari:

Tabel dengan ID buatan sebagai kunci utama yang tidak ada hubungannya dengan pengetahuan domain.

Tidak ada yang unik, pemicu, cek atau kunci asing apa pun.

Bidang dengan nama generik yang cocok dengan konsep berbeda untuk catatan berbeda.

Catatan yang hanya dapat dikategorikan dengan menyeberang dengan tabel lain dengan kondisi yang berbeda.

Kolom yang harus berupa angka atau tanggal disimpan sebagai string.

Singkatnya, desain berantakan / malas di sekitar.

DPM
sumber
7
Apakah mengoreksi desain database merupakan opsi?
RMalke
1
Tolong jelaskan bagaimana database dirancang dengan buruk.
Tulains Córdova
@Renan Malke Stigliani Sayangnya tidak, karena ada perangkat lunak lama yang bergantung padanya, namun saya telah merefleksikan beberapa tabel dengan desain yang sedikit berbeda dan mengisinya, yang menyederhanakan kode. Namun saya tidak bangga dengan hal ini dan saya lebih suka tidak menduplikasi tabel tanpa pandang bulu
DPM
1
Buku ini mungkin memberi Anda beberapa cara tentang bagaimana Anda dapat mulai memperbaiki problesm basis data dan menjaga kode lama berfungsi: amazon.com/…
HLGEM
4
Sebagian besar masalah Anda daftar. . . tidak. Penggunaan kunci pengganti daripada kunci alami sebenarnya merupakan rekomendasi yang cukup standar saat ini; bukan "desain yang buruk" sama sekali. Kurangnya kendala dan penggunaan tipe kolom yang tidak sesuai adalah contoh yang lebih baik sejauh "desain yang buruk" berjalan, tetapi seharusnya tidak benar-benar mempengaruhi kode aplikasi Anda sama sekali (kecuali jika Anda berencana untuk menyalahgunakan masalah ini?).
ruakh

Jawaban:

53

Orientasi objek berharga khususnya karena jenis skenario ini muncul, dan ini memberi Anda alat untuk merancang abstraksi yang memungkinkan Anda merangkum kerumitan.

Pertanyaan sebenarnya di sini adalah, di mana Anda merangkum kompleksitas itu?

Jadi izinkan saya mundur sejenak dan berbicara tentang 'kompleksitas' apa yang saya maksud di sini. Masalah Anda (seperti yang saya mengerti; koreksi saya jika saya salah) adalah model ketekunan yang bukan model yang dapat digunakan secara efektif untuk tugas-tugas yang Anda butuhkan untuk melengkapi dengan data. Ini mungkin efektif dan dapat digunakan untuk tugas lain, tetapi tidak untuk tugas Anda .

Jadi apa yang kita lakukan ketika kita memiliki data yang tidak menyajikan model yang baik untuk kemampuan kita?

Menterjemahkan. Itu satu-satunya yang bisa Anda lakukan. Terjemahan itu adalah 'kompleksitas' yang saya rujuk di atas. Jadi sekarang kita menerima bahwa kita akan menerjemahkan model, kita perlu memutuskan beberapa faktor.

Apakah kita perlu menerjemahkan kedua arah? Apakah kedua arah akan diterjemahkan sama, seperti pada:

(Tbl A, Tbl B) -> Obj X (baca)

Obj X -> (Tbl A, Tbl B) (tulis)

atau apakah aktivitas menyisipkan / memperbarui / menghapus mewakili tipe objek yang berbeda sehingga Anda membaca data sebagai Obj X, tetapi data dimasukkan / diperbarui dari Obj Y? Mana dari dua cara yang ingin Anda tuju, atau jika tidak ada pembaruan / masukkan / hapus adalah faktor penting di mana Anda ingin menempatkan terjemahan.


Di mana Anda menerjemahkan?

Kembali ke pernyataan pertama yang saya buat dalam jawaban ini; OO memungkinkan Anda untuk merangkum kerumitan dan apa yang saya rujuk di sini adalah fakta bahwa Anda tidak hanya harus melakukannya, tetapi Anda harus merangkum kerumitan itu jika Anda ingin memastikannya tidak bocor dan meresap ke dalam semua kode Anda. Pada saat yang sama, penting untuk mengenali Anda tidak dapat memiliki abstraksi yang sempurna, jadi jangan khawatir tentang hal itu daripada memiliki yang sangat efektif dan bermanfaat.

Lagi sekarang; masalah Anda adalah: Di mana Anda menempatkan kompleksitas ini? Yah, Anda punya pilihan.

Anda dapat melakukannya di database menggunakan prosedur tersimpan. Ini memiliki kelemahan sering tidak bermain sangat baik dengan ORM tetapi itu tidak selalu benar. Prosedur yang tersimpan memberikan beberapa manfaat, termasuk kinerja yang sering. Namun prosedur yang tersimpan dapat membutuhkan banyak perawatan, tetapi terserah Anda untuk menganalisis skenario khusus Anda dan mengatakan apakah pemeliharaannya akan lebih atau kurang dari pilihan lain. Saya pribadi sangat terampil dengan prosedur tersimpan, dan dengan demikian fakta bakat yang tersedia ini mengurangi biaya overhead; tidak pernah meremehkan nilai membuat keputusan berdasarkan apa yang Anda lakukan tahu. Terkadang solusi suboptimal bisa lebih optimal daripada solusi yang benar karena Anda atau tim Anda tahu cara membuat dan memeliharanya lebih baik daripada solusi optimal.

Opsi lain dalam basis data adalah tampilan. Tergantung pada server database Anda, ini mungkin sangat optimal atau kurang optimal atau bahkan tidak efektif sama sekali, salah satu kelemahannya adalah waktu kueri tergantung pada opsi pengindeksan apa yang tersedia di database Anda. Tampilan menjadi pilihan yang lebih baik jika Anda tidak perlu melakukan modifikasi data apa pun (masukkan / perbarui / hapus).

Melangkah melewati database Anda memiliki siaga lama menggunakan pola repositori. Ini adalah pendekatan teruji waktu yang bisa sangat efektif. Kerugiannya cenderung mencakup pelat ketel, tetapi repositori yang diperhitungkan dengan baik dapat menghindari sejumlah ini, dan bahkan ketika ini menghasilkan pelat ketel yang tidak menguntungkan, repositori cenderung menjadi kode sederhana yang mudah dipahami dan dipelihara serta menyajikan API yang baik. /abstraksi. Repositori juga bagus untuk unit-testability mereka yang Anda kehilangan dengan opsi dalam-database.

Ada alat-alat seperti auto-mapper di luar sana yang mungkin membuat menggunakan ORM masuk akal di mana mereka dapat melakukan terjemahan antara model-basis data dari orm ke model yang dapat digunakan, tetapi beberapa alat ini bisa sulit untuk mempertahankan / memahami berperilaku lebih seperti sihir; meskipun mereka membuat kode overhead minimum sehingga biaya pemeliharaan lebih sedikit bila dipahami dengan baik.

Selanjutnya Anda melangkah lebih jauh dan lebih jauh dari database , yang berarti akan ada jumlah kode yang lebih besar yang akan berurusan dengan model kegigihan yang tidak diterjemahkan, yang akan menjadi benar-benar tidak menyenangkan. Dalam skenario ini Anda berbicara tentang meletakkan lapisan terjemahan di UI Anda yang sepertinya Anda lakukan sekarang. Ini umumnya ide yang sangat buruk, dan meluruh sangat lama.


Sekarang mari kita mulai bicara gila .

The Objectbukan satu-satunya akhir-semua akan-semua abstraksi yang ada. Telah ada kedalaman abstraksi yang dikembangkan selama bertahun-tahun bahwa ilmu komputer telah dipelajari dan bahkan sebelum itu dari studi matematika. Jika kita akan mulai menjadi kreatif, mari kita mulai berbicara tentang abstraksi yang diketahui tersedia yang telah dipelajari.

Ada model aktor.Ini adalah pendekatan yang menarik karena dikatakan semua yang Anda lakukan adalah mengirim pesan ke kode lain yang secara efektif mendelegasikan semua pekerjaan ke kode lain itu, yang sangat efektif dalam merangkum kompleksitas dari semua kode Anda. Ini dapat bekerja sejauh Anda mengirim pesan ke aktor yang mengatakan "Saya perlu Obj X dikirim ke Y" dan Anda memiliki wadah menunggu respons di lokasi Y yang kemudian memproses Obj X. Anda bahkan dapat mengirim pesan yang menginstruksikan "Aku perlu Obj X dan perhitungan Y, Z dilakukan untuk itu" dan kemudian kamu bahkan tidak perlu menunggu; terjemahan terjadi di sisi lain dari pesan yang lulus dan Anda bisa melanjutkan jika Anda tidak perlu membaca hasilnya. Ini bisa menjadi sedikit penyalahgunaan model aktor untuk tujuan Anda, tetapi semuanya tergantung;

Batas enkapsulasi lainnya adalah batas proses. Ini dapat digunakan untuk memisahkan kompleksitas dengan sangat efektif. Anda dapat membuat kode terjemahan sebagai layanan web di mana komunikasi HTTP sederhana, menggunakan SOAP, REST, atau jika Anda benar-benar menginginkan protokol Anda sendiri (tidak disarankan). STOMP sama sekali bukan protokol baru yang buruk. Atau gunakan layanan daemon normal dengan pipa memori sistem-lokal yang dipublikasikan untuk berkomunikasi dengan sangat cepat lagi menggunakan protokol apa pun yang Anda pilih. Ini sebenarnya memiliki beberapa manfaat yang cukup baik:

  • Anda dapat memiliki beberapa proses yang berjalan yang melakukan terjemahan untuk dukungan versi yang lebih lama dan yang lebih baru pada saat yang sama memungkinkan Anda untuk memperbarui layanan terjemahan untuk mempublikasikan model objek V2, dan kemudian secara terpisah di kemudian hari memperbarui kode yang digunakan untuk bekerja dengan objek baru model.
  • Anda dapat melakukan hal-hal menarik seperti menyematkan proses ke inti untuk kinerja, Anda juga mendapatkan sejumlah keamanan keamanan dalam pendekatan ini dengan menjadikan bahwa satu-satunya proses berjalan dengan hak istimewa keamanan untuk menyentuh data itu.
  • Anda akan mendapatkan batas yang sangat kuat ketika Anda berbicara tentang batas proses yang akan tetap memastikan kebocoran minimum abstraksi Anda untuk waktu yang lama karena menulis kode di ruang terjemahan tidak akan dapat dipanggil di luar ruang terjemahan karena mereka tidak akan berbagi ruang lingkup proses, memastikan serangkaian skenario penggunaan tetap berdasarkan kontrak.
  • Kemampuan untuk pembaruan asinkron / non-pemblokiran menjadi lebih sederhana.

Kekurangannya jelas lebih banyak perawatan daripada yang biasanya diperlukan, overhead komunikasi mempengaruhi kinerja dan pemeliharaan.


Ada banyak cara untuk merangkum kerumitan yang memungkinkan kerumitan itu ditempatkan di tempat-tempat yang lebih aneh dan aneh di sistem Anda. Menggunakan bentuk-bentuk fungsi tingkat tinggi (sering kali dipalsukan menggunakan pola strategi atau berbagai bentuk pola objek aneh lainnya), Anda dapat melakukan beberapa hal yang sangat menarik.

Itu benar, mari kita mulai berbicara tentang monad. Anda dapat membuat lapisan terjemahan ini dengan cara yang sangat independen dari fungsi spesifik kecil yang diperlukan untuk melakukan terjemahan independen, tetapi menyembunyikan semua fungsi terjemahan itu tidak terlihat sehingga sulit diakses oleh kode luar. Ini memiliki manfaat mengurangi ketergantungan pada mereka yang memungkinkan mereka untuk berubah dengan mudah tanpa memengaruhi banyak kode eksternal. Anda kemudian membuat kelas yang menerima fungsi urutan lebih tinggi (fungsi anonim, fungsi lambda, objek strategi, namun Anda perlu menyusunnya) yang bekerja pada objek tipe model OO yang bagus. Anda kemudian membiarkan kode yang mendasari yang menerima fungsi-fungsi tersebut melakukan eksekusi literal menggunakan metode terjemahan yang sesuai.

Ini menciptakan batas di mana semua terjemahan tidak hanya ada di sisi lain dari batas jauh dari semua kode Anda; itu hanya digunakan pada sisi itu memungkinkan sisa kode Anda bahkan tidak tahu apa-apa tentang itu selain di mana titik masuk untuk batas itu.

Ok, ya itu benar-benar gila, tapi siapa yang tahu; Anda mungkin gila (serius, jangan melakukan monad dengan peringkat kegilaan di bawah 88%, ada risiko nyata cedera tubuh).

Jimmy Hoffa
sumber
4
Wow, jawaban yang sangat komprehensif. Saya akan memperbaiki ini lebih dari sekali jika hanya SE yang akan membiarkan saya.
Marjan Venema
11
Kapan versi filmnya keluar?
yannis
3
@JimmyHoffa Bravo Pak !!! Saya akan menandai jawaban ini dan menunjukkan kepada putri saya ketika dia semakin besar.
Tombatron
4

Saran saya:

Buat tampilan basis data yang:

  1. Berikan nama yang bermakna ke kolom
  2. Lakukan "persimpangan dengan tabel lain dengan kondisi yang berbeda" sehingga Anda dapat menyembunyikan kompleksitas itu.
  3. Konversi angka atau tanggal yang disimpan sebagai string ke angka dan tanggal masing-masing.
  4. Buat keunikan di mana tidak ada, sesuai dengan beberapa kriteria.

Idenya adalah membuat fasad yang meniru desain yang lebih baik di atas yang buruk.

Kemudian buat ORM terkait dengan façade itu dan bukan tabel sebenarnya.

Ini tidak menyederhanakan penyisipan.

Tulains Córdova
sumber
Menggunakan tampilan basis data tampak seperti ide bagus dan tindakan paling elegan yang mengabstraksi keburukan di tingkat terendah, untuk beberapa alasan saya tidak mempertimbangkannya. Terima kasih.
DPM
3

Saya dapat melihat bagaimana skema database Anda yang ada menyebabkan Anda menulis kode dan pertanyaan yang lebih spesifik untuk tugas-tugas yang mungkin diabstraksikan dengan skema yang dirancang lebih baik, tetapi seharusnya tidak menghambat kemampuan Anda untuk menulis kode berorientasi objek yang baik.

  • Ingat prinsip-prinsip SOLID .
  • Tulis kode yang dapat dengan mudah diuji unit (yang sering datang melalui prinsip SOLID berikut).
  • Pisahkan logika bisnis Anda dari logika tampilan Anda.
  • Baca dokumentasi dan contoh Apache Wicket - kerangka kerja ini mungkin dapat menghemat lebih banyak kode boilerplate daripada yang Anda pikirkan, jadi pelajari cara menggunakannya secara efektif.
  • Pertahankan logika yang harus berurusan dengan basis data di lapisan terpisah yang menyediakan antarmuka yang bersih yang dapat digunakan oleh logika bisnis Anda. Dengan cara ini, jika Anda (atau pengelola masa depan) pernah mendapatkan kesempatan untuk meningkatkan skema, mereka dapat melakukannya tanpa terlalu banyak perubahan pada logika bisnis.

Ketika Anda menemukan diri Anda bekerja dengan skema database yang tidak sempurna, mudah untuk mengeluh tentang semua cara yang membuat pekerjaan Anda lebih sulit, tetapi pada titik tertentu Anda harus mengesampingkan keluhan tersebut dan memanfaatkan yang terbaik.

Anggap saja sebagai kesempatan untuk menggunakan kreativitas Anda untuk menulis kode yang bersih, dapat digunakan kembali, dan mudah dikelola terlepas dari skema yang tidak sempurna.

Mike Partridge
sumber
1

Menjawab pertanyaan awal Anda tentang kode berorientasi objek yang lebih baik, saya sarankan menggunakan objek yang berbahasa SQL . ORM secara intrinsik bertentangan dengan prinsip-prinsip berorientasi objek, karena beroperasi pada suatu objek, dan objek dalam OOP adalah entitas mandiri, yang memiliki semua sumber daya untuk mencapai tujuannya. Saya yakin pendekatan ini dapat membuat kode Anda lebih sederhana.

Berbicara tentang ruang masalah, yaitu domain Anda, saya akan mencoba mengidentifikasi akar agregat . Ini adalah batas konsistensi domain Anda. Batas-batas yang mutlak harus memegang konsistensi setiap saat. Agregat berkomunikasi melalui peristiwa domain. Jika Anda memiliki sistem yang cukup besar, mungkin Anda harus mulai membaginya pada subsistem (sebut saja SOA, Microservice, sistem mandiri, dll)

Saya juga akan mempertimbangkan menggunakan CQRS - ini dapat sangat menyederhanakan sisi penulisan dan baca Anda. Pastikan untuk membaca artikel Udi Dahan tentang topik ini.

Zapadlo
sumber