Jadi situasi yang sering saya temui adalah situasi di mana model saya mulai:
- Tumbuh menjadi monster dengan berton-ton metode
ATAU
- Memungkinkan Anda mengirimkan potongan-potongan SQL kepada mereka, sehingga mereka cukup fleksibel untuk tidak memerlukan jutaan metode berbeda
Misalnya, kita memiliki model "widget". Kami mulai dengan beberapa metode dasar:
- dapatkan ($ id)
- masukkan ($ record)
- perbarui ($ id, $ record)
- hapus ($ id)
- getList () // dapatkan daftar Widget
Semua baik-baik saja dan keren, tetapi kemudian kita perlu pelaporan:
- listCreatedBetween ($ start_date, $ end_date)
- listPurchasedBetween ($ start_date, $ end_date)
- listOfPending ()
Dan kemudian pelaporan mulai menjadi rumit:
- listPendingCreatedBetween ($ start_date, $ end_date)
- listForCustomer ($ customer_id)
- listPendingCreatedBetweenForCustomer ($ customer_id, $ start_date, $ end_date)
Anda dapat melihat di mana ini berkembang ... pada akhirnya kami memiliki begitu banyak persyaratan kueri khusus sehingga saya perlu menerapkan berton-ton metode, atau semacam objek "kueri" yang dapat saya sampaikan ke satu -> kueri (kueri) metode $ query) ...
... atau cukup gigit peluru, dan mulailah melakukan sesuatu seperti ini:
- list = MyModel-> query ("start_date> X AND end_date <Y AND pending = 1 AND customer_id = Z")
Ada daya tarik tertentu untuk hanya memiliki satu metode seperti itu daripada 50 juta metode lain yang lebih spesifik ... tapi kadang-kadang terasa "salah" untuk memasukkan setumpuk apa yang pada dasarnya SQL ke dalam pengontrol.
Apakah ada cara "benar" untuk menangani situasi seperti ini? Apakah dapat diterima untuk memasukkan pertanyaan seperti itu ke dalam metode generik -> kueri ()?
Apakah ada strategi yang lebih baik?
sumber
Jawaban:
Pola Arsitektur Aplikasi Enterprise Martin Fowler menjelaskan sejumlah pola ORM terkait, termasuk penggunaan Obyek Kueri, yang merupakan apa yang saya sarankan.
Objek kueri memungkinkan Anda mengikuti prinsip Tanggung Jawab Tunggal, dengan memisahkan logika untuk setiap kueri menjadi objek strategi yang dikelola dan dikelola secara individual. Pengontrol Anda dapat mengatur penggunaannya secara langsung, atau mendelegasikannya ke pengontrol sekunder atau objek bantuan.
Apakah Anda akan memiliki banyak dari mereka? Pasti. Bisakah beberapa dikelompokkan ke dalam pertanyaan umum? Ya lagi
Bisakah Anda menggunakan injeksi ketergantungan untuk membuat objek dari metadata? Itulah yang dilakukan sebagian besar alat ORM.
sumber
Tidak ada cara yang benar untuk melakukan ini. Banyak orang menggunakan ORM untuk mengabstraksi semua kerumitan. Beberapa ORM yang lebih maju menerjemahkan ekspresi kode menjadi pernyataan SQL yang rumit. ORM memiliki kerugian juga, namun untuk banyak aplikasi manfaatnya lebih besar daripada biaya.
Jika Anda tidak bekerja dengan dataset besar, hal paling sederhana yang harus dilakukan adalah memilih seluruh tabel ke dalam memori dan memfilter dalam kode.
Untuk aplikasi pelaporan internal, pendekatan ini mungkin baik. Jika dataset sangat besar, Anda akan mulai membutuhkan banyak metode khusus serta indeks yang sesuai di tabel Anda.
sumber
Beberapa ORM memungkinkan Anda untuk membangun kueri kompleks mulai dari metode dasar. Contohnya
adalah kueri yang benar-benar valid dalam Django ORM .
Idenya adalah bahwa Anda memiliki beberapa pembuat kueri (dalam hal ini
Purchase.objects
) yang status internnya mewakili informasi tentang kueri. Metode sepertiget
,filter
,exclude
,order_by
adalah valid dan kembali pembangun query yang baru dengan status update. Objek-objek ini mengimplementasikan antarmuka iterable, sehingga ketika Anda mengulanginya, query dilakukan dan Anda mendapatkan hasil dari query yang dibangun sejauh ini. Meskipun contoh ini diambil dari Django, Anda akan melihat struktur yang sama di banyak ORM lainnya.sumber
Ada pendekatan ketiga.
Contoh spesifik Anda menunjukkan pertumbuhan eksponensial dalam jumlah metode yang diperlukan ketika jumlah fitur yang diperlukan bertambah: kami ingin kemampuan untuk menawarkan kueri tingkat lanjut, menggabungkan setiap fitur permintaan ... jika kami melakukannya dengan menambahkan metode, kami memiliki satu metode untuk permintaan dasar, dua jika kita menambahkan satu fitur opsional, empat jika kita menambahkan dua, delapan jika kita menambahkan tiga, 2 ^ n jika kita menambahkan fitur n.
Itu jelas tidak dapat dipertahankan di luar tiga atau empat fitur, dan ada bau buruk dari banyak kode terkait erat yang hampir disalin-disisipkan antara metode.
Anda bisa menghindarinya dengan menambahkan objek data untuk menyimpan parameter, dan memiliki satu metode yang membangun kueri berdasarkan set parameter yang disediakan (atau tidak disediakan). Dalam hal itu, menambahkan fitur baru seperti rentang tanggal semudah menambahkan setter dan getter untuk rentang tanggal ke objek data Anda, dan kemudian menambahkan sedikit kode tempat kueri parameterisasi dibuat:
... dan tempat parameter ditambahkan ke kueri:
Pendekatan ini memungkinkan untuk pertumbuhan kode linier ketika fitur ditambahkan, tanpa harus mengizinkan permintaan sewenang-wenang dan tidak diparameterisasi.
sumber
Saya pikir konsensus umum adalah untuk menjaga akses data sebanyak mungkin dalam model Anda di MVC. Salah satu prinsip desain lainnya adalah memindahkan beberapa pertanyaan Anda yang lebih umum (Pertanyaan yang tidak terkait langsung dengan model Anda) ke tingkat yang lebih tinggi dan lebih abstrak di mana Anda dapat mengizinkannya digunakan oleh model lain juga. (Dalam RoR, kami memiliki sesuatu yang disebut framework). Ada juga hal lain yang harus Anda pertimbangkan dan itu adalah pemeliharaan kode Anda. Seiring proyek Anda tumbuh, jika Anda memiliki akses data di pengontrol, akan menjadi semakin sulit untuk melacaknya (Kami saat ini menghadapi masalah ini dalam proyek besar) Model, meskipun berantakan dengan metode memberikan satu titik kontak untuk setiap pengontrol yang mungkin berakhir quering dari tabel. (Ini juga dapat menyebabkan penggunaan kembali kode yang pada gilirannya bermanfaat)
sumber
Antarmuka lapisan layanan Anda mungkin memiliki banyak metode, tetapi panggilan ke database hanya dapat memiliki satu metode.
Database memiliki 4 operasi besar
Metode opsional lain mungkin untuk menjalankan beberapa operasi database yang tidak termasuk dalam operasi DB dasar. Mari kita sebut Jalankan itu.
Sisipkan dan Pembaruan dapat digabungkan menjadi satu operasi, yang disebut Simpan.
Banyak metode Anda adalah kueri. Jadi, Anda dapat membuat antarmuka umum untuk memenuhi sebagian besar kebutuhan mendesak. Berikut ini contoh antarmuka umum:
Objek transfer data bersifat umum dan akan memiliki semua filter, parameter, penyortiran, dll. Yang terkandung di dalamnya. Lapisan data akan bertanggung jawab untuk mem-parsing dan mengekstraksi ini dan mengatur operasi ke database melalui prosedur yang tersimpan, parameter sql, LINQ dll. Jadi, SQL tidak melewati antar lapisan. Ini biasanya apa yang dilakukan ORM, tetapi Anda dapat memutar sendiri dan memiliki pemetaan sendiri.
Jadi, dalam kasus Anda, Anda memiliki Widget. Widget akan mengimplementasikan antarmuka IPOCO.
Jadi, dalam model lapisan layanan Anda akan melakukannya
getList().
Butuh lapisan pemetaan untuk menangani tranforming
getList
kedan sebaliknya. Seperti yang telah disebutkan, kadang-kadang hal ini dilakukan melalui ORM, tetapi pada akhirnya Anda berakhir dengan banyak jenis kode boilerplate, terutama jika Anda memiliki 100-an tabel. ORM secara ajaib membuat parameter SQL dan menjalankannya terhadap basis data. Jika menggulirkan milik Anda sendiri, juga di lapisan data itu sendiri, pemetaan akan diperlukan untuk mengatur SP, LINQ dll. (Pada dasarnya sql pergi ke database).
Seperti yang disebutkan sebelumnya, DTO adalah objek yang dibuat oleh komposisi. Mungkin salah satu objek yang terkandung di dalamnya adalah objek yang disebut QueryParameters. Ini akan menjadi semua parameter untuk kueri yang akan diatur dan digunakan oleh kueri. Objek lain akan menjadi Daftar objek yang dikembalikan dari querys, pembaruan, ext. Ini payloadnya. Jika hal ini payload akan menjadi Daftar Daftar widget.
Jadi, strategi dasarnya adalah:
Dalam kasus Anda, saya pikir model dapat memiliki banyak metode, tetapi secara optimal Anda ingin panggilan database menjadi generik. Anda masih berakhir dengan banyak kode pemetaan boilerplate (terutama dengan SP) atau kode ORM ajaib yang secara dinamis membuat SQL parametisasi untuk Anda.
sumber