Saya pikir saya akan menjawab pertanyaan saya sendiri. Berikut ini hanyalah satu cara untuk menyelesaikan masalah 1-3 dalam pertanyaan awal saya.
Penafian: Saya mungkin tidak selalu menggunakan istilah yang tepat saat menjelaskan pola atau teknik. Maaf untuk itu.
Tujuan:
- Buat contoh lengkap dari pengontrol dasar untuk melihat dan mengedit
Users
.
- Semua kode harus sepenuhnya dapat diuji dan dapat dipermainkan.
- Pengontrol seharusnya tidak tahu di mana data disimpan (artinya dapat diubah).
- Contoh untuk menunjukkan implementasi SQL (paling umum).
- Untuk kinerja maksimum, pengontrol hanya akan menerima data yang mereka butuhkan — tidak ada bidang tambahan.
- Implementasi harus memanfaatkan beberapa jenis data mapper untuk kemudahan pengembangan.
- Implementasi harus memiliki kemampuan untuk melakukan pencarian data yang kompleks.
Solusinya
Saya membagi interaksi penyimpanan (basis data) saya menjadi dua kategori: R (Baca) dan CUD (Buat, Perbarui, Hapus). Pengalaman saya adalah bahwa membaca benar-benar menyebabkan aplikasi melambat. Dan sementara manipulasi data (CUD) sebenarnya lebih lambat, itu terjadi jauh lebih jarang, dan karena itu jauh lebih sedikit dari masalah.
CUD (Buat, Perbarui, Hapus) mudah. Ini akan melibatkan bekerja dengan model yang sebenarnya , yang kemudian diteruskan ke saya Repositories
untuk kegigihan. Catatan, repositori saya masih akan menyediakan metode Baca, tetapi hanya untuk pembuatan objek, bukan tampilan. Lebih lanjut tentang itu nanti.
R (Baca) tidak mudah. Tidak ada model di sini, hanya nilai objek . Gunakan array jika Anda mau . Objek-objek ini dapat mewakili model tunggal atau campuran dari banyak model, apa pun sebenarnya. Ini tidak terlalu menarik pada mereka sendiri, tetapi bagaimana mereka dihasilkan. Saya menggunakan apa yang saya panggil Query Objects
.
Kode:
Model Pengguna
Mari kita mulai dengan model pengguna dasar kami. Perhatikan bahwa tidak ada perluasan ORM atau basis data sama sekali. Hanya kemuliaan model murni. Tambahkan getter, setter, validasi, apa pun.
class User
{
public $id;
public $first_name;
public $last_name;
public $gender;
public $email;
public $password;
}
Antarmuka Repositori
Sebelum saya membuat repositori pengguna saya, saya ingin membuat antarmuka repositori saya. Ini akan menentukan "kontrak" yang harus diikuti oleh repositori agar dapat digunakan oleh pengontrol saya. Ingat, pengontrol saya tidak akan tahu di mana data sebenarnya disimpan.
Perhatikan bahwa repositori saya hanya akan berisi setiap tiga metode ini. The save()
Metode bertanggung jawab untuk kedua menciptakan dan memperbarui pengguna, hanya tergantung pada apakah atau tidak objek pengguna memiliki set id.
interface UserRepositoryInterface
{
public function find($id);
public function save(User $user);
public function remove(User $user);
}
Implementasi Repositori SQL
Sekarang untuk membuat implementasi antarmuka saya. Seperti yang disebutkan, contoh saya adalah dengan database SQL. Catat penggunaan data mapper untuk mencegah keharusan menulis query SQL berulang.
class SQLUserRepository implements UserRepositoryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function find($id)
{
// Find a record with the id = $id
// from the 'users' table
// and return it as a User object
return $this->db->find($id, 'users', 'User');
}
public function save(User $user)
{
// Insert or update the $user
// in the 'users' table
$this->db->save($user, 'users');
}
public function remove(User $user)
{
// Remove the $user
// from the 'users' table
$this->db->remove($user, 'users');
}
}
Antarmuka Objek Kueri
Sekarang dengan CUD (Buat, Perbarui, Hapus) diurus oleh repositori kami, kami dapat fokus pada R (Baca). Objek kueri hanyalah enkapsulasi dari beberapa jenis logika pencarian data. Mereka bukan pembangun permintaan. Dengan mengabstraksikannya seperti repositori kami, kami dapat mengubah implementasinya dan mengujinya lebih mudah. Contoh Obyek Kueri mungkin berupa AllUsersQuery
atau AllActiveUsersQuery
, atau bahkan MostCommonUserFirstNames
.
Anda mungkin berpikir "tidak bisakah saya membuat metode di repositori saya untuk pertanyaan itu?" Ya, tetapi inilah mengapa saya tidak melakukan ini:
- Repositori saya dimaksudkan untuk bekerja dengan objek model. Dalam aplikasi dunia nyata, mengapa saya harus mendapatkan
password
bidang jika saya ingin mendaftar semua pengguna saya?
- Repositori sering model spesifik, namun permintaan sering melibatkan lebih dari satu model. Jadi repositori apa yang Anda masukkan dalam metode Anda?
- Ini membuat repositori saya sangat sederhana — bukan kelas metode yang membengkak.
- Semua pertanyaan sekarang diatur ke dalam kelas mereka sendiri.
- Sungguh, pada titik ini, repositori ada hanya untuk abstrak lapisan database saya.
Sebagai contoh saya, saya akan membuat objek permintaan untuk mencari "AllUsers". Inilah antarmuka:
interface AllUsersQueryInterface
{
public function fetch($fields);
}
Implementasi Objek Kueri
Di sinilah kita bisa menggunakan data mapper lagi untuk membantu mempercepat pengembangan. Perhatikan bahwa saya mengizinkan satu tweak untuk dataset yang dikembalikan - bidang. Sejauh ini saya ingin memanipulasi kueri yang dilakukan. Ingat, objek kueri saya bukan pembuat kueri. Mereka hanya melakukan kueri tertentu. Namun, karena saya tahu bahwa saya mungkin akan sering menggunakan ini, dalam sejumlah situasi yang berbeda, saya memberikan diri saya kemampuan untuk menentukan bidang. Saya tidak pernah ingin mengembalikan bidang yang tidak saya butuhkan!
class AllUsersQuery implements AllUsersQueryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fetch($fields)
{
return $this->db->select($fields)->from('users')->orderBy('last_name, first_name')->rows();
}
}
Sebelum beralih ke controller, saya ingin menunjukkan contoh lain untuk menggambarkan betapa kuatnya ini. Mungkin saya memiliki mesin pelaporan dan perlu membuat laporan untuk AllOverdueAccounts
. Ini bisa rumit dengan mapper data saya, dan saya mungkin ingin menulis beberapa aktual SQL
dalam situasi ini. Tidak masalah, di sini terlihat seperti apa objek query ini:
class AllOverdueAccountsQuery implements AllOverdueAccountsQueryInterface
{
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fetch()
{
return $this->db->query($this->sql())->rows();
}
public function sql()
{
return "SELECT...";
}
}
Ini dengan baik menyimpan semua logika saya untuk laporan ini dalam satu kelas, dan mudah untuk diuji. Saya dapat mengejeknya dengan sepenuh hati, atau bahkan menggunakan implementasi yang berbeda sama sekali.
Pengendali
Sekarang bagian yang menyenangkan — kumpulkan semuanya. Perhatikan bahwa saya menggunakan injeksi ketergantungan. Biasanya dependensi disuntikkan ke konstruktor, tetapi saya sebenarnya lebih suka menyuntikkannya langsung ke metode controller saya (rute). Ini meminimalkan grafik objek pengontrol, dan saya benar-benar merasa lebih terbaca. Catatan, jika Anda tidak menyukai pendekatan ini, cukup gunakan metode konstruktor tradisional.
class UsersController
{
public function index(AllUsersQueryInterface $query)
{
// Fetch user data
$users = $query->fetch(['first_name', 'last_name', 'email']);
// Return view
return Response::view('all_users.php', ['users' => $users]);
}
public function add()
{
return Response::view('add_user.php');
}
public function insert(UserRepositoryInterface $repository)
{
// Create new user model
$user = new User;
$user->first_name = $_POST['first_name'];
$user->last_name = $_POST['last_name'];
$user->gender = $_POST['gender'];
$user->email = $_POST['email'];
// Save the new user
$repository->save($user);
// Return the id
return Response::json(['id' => $user->id]);
}
public function view(SpecificUserQueryInterface $query, $id)
{
// Load user data
if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
return Response::notFound();
}
// Return view
return Response::view('view_user.php', ['user' => $user]);
}
public function edit(SpecificUserQueryInterface $query, $id)
{
// Load user data
if (!$user = $query->fetch($id, ['first_name', 'last_name', 'gender', 'email'])) {
return Response::notFound();
}
// Return view
return Response::view('edit_user.php', ['user' => $user]);
}
public function update(UserRepositoryInterface $repository)
{
// Load user model
if (!$user = $repository->find($id)) {
return Response::notFound();
}
// Update the user
$user->first_name = $_POST['first_name'];
$user->last_name = $_POST['last_name'];
$user->gender = $_POST['gender'];
$user->email = $_POST['email'];
// Save the user
$repository->save($user);
// Return success
return true;
}
public function delete(UserRepositoryInterface $repository)
{
// Load user model
if (!$user = $repository->find($id)) {
return Response::notFound();
}
// Delete the user
$repository->delete($user);
// Return success
return true;
}
}
Pikiran terakhir:
Hal penting yang perlu diperhatikan di sini adalah ketika saya memodifikasi (membuat, memperbarui atau menghapus) entitas, saya bekerja dengan objek model nyata, dan melakukan persistensi melalui repositori saya.
Namun, ketika saya menampilkan (memilih data dan mengirimkannya ke tampilan), saya tidak bekerja dengan objek model, melainkan objek nilai lama yang polos. Saya hanya memilih bidang yang saya butuhkan, dan itu dirancang agar saya dapat memaksimalkan kinerja pencarian data saya.
Repositori saya tetap sangat bersih, dan sebagai gantinya "kekacauan" ini diatur dalam pertanyaan model saya.
Saya menggunakan data mapper untuk membantu pengembangan, karena itu konyol untuk menulis SQL berulang untuk tugas-tugas umum. Namun, Anda benar-benar dapat menulis SQL di mana diperlukan (pertanyaan rumit, pelaporan, dll.). Dan ketika Anda melakukannya, itu terselip dengan baik ke dalam kelas yang diberi nama dengan benar.
Saya ingin mendengar pendapat Anda tentang pendekatan saya!
Pembaruan Juli 2015:
Saya telah ditanya dalam komentar di mana saya berakhir dengan semua ini. Sebenarnya tidak terlalu jauh. Sejujurnya, saya masih tidak terlalu menyukai repositori. Saya menemukan mereka berlebihan untuk pencarian dasar (terutama jika Anda sudah menggunakan ORM), dan berantakan ketika bekerja dengan pertanyaan yang lebih rumit.
Saya biasanya bekerja dengan ORM gaya ActiveRecord, jadi paling sering saya hanya akan merujuk model-model itu langsung di seluruh aplikasi saya. Namun, dalam situasi di mana saya memiliki kueri yang lebih kompleks, saya akan menggunakan objek kueri untuk membuatnya lebih dapat digunakan kembali. Saya juga harus mencatat bahwa saya selalu menyuntikkan model saya ke dalam metode saya, membuatnya lebih mudah untuk mengejek dalam tes saya.
new Query\ComplexUserLookup($username, $anotherCondition)
. Atau, lakukan ini melalui metode penyetel$query->setUsername($username);
. Anda benar-benar dapat merancang ini namun masuk akal untuk aplikasi khusus Anda, dan saya pikir objek permintaan meninggalkan banyak fleksibilitas di sini.Berdasarkan pengalaman saya, berikut adalah beberapa jawaban untuk pertanyaan Anda:
T: Bagaimana cara kita menangani pengembalian bidang yang tidak kita butuhkan?
A: Dari pengalaman saya, ini benar-benar bermuara pada berurusan dengan entitas lengkap versus permintaan ad-hoc.
Entitas yang lengkap adalah sesuatu seperti
User
objek. Ini memiliki properti dan metode, dll. Ini adalah warga negara kelas satu di basis kode Anda.Kueri ad-hoc mengembalikan beberapa data, tetapi kami tidak tahu apa-apa selain itu. Ketika data dilewatkan di sekitar aplikasi, itu dilakukan tanpa konteks. Apakah ini
User
? AUser
dengan beberapaOrder
informasi terlampir? Kami tidak benar-benar tahu.Saya lebih suka bekerja dengan entitas penuh.
Anda benar bahwa Anda akan sering mengembalikan data yang tidak akan Anda gunakan, tetapi Anda dapat mengatasinya dengan berbagai cara:
User
untuk bagian belakang dan mungkinUserSmall
untuk panggilan AJAX. Seseorang mungkin memiliki 10 properti dan satu memiliki 3 properti.Kelemahan bekerja dengan permintaan ad-hoc:
User
, Anda pada dasarnya akan menulis hal yang samaselect *
untuk banyak panggilan. Satu panggilan akan mendapatkan 8 dari 10 bidang, satu akan mendapatkan 5 dari 10, satu akan mendapatkan 7 dari 10. Mengapa tidak mengganti semua dengan satu panggilan yang mendapat 10 dari 10? Alasan ini buruk adalah karena pembunuhan adalah untuk re-factor / test / ejekan.User
begitu lambat?" Anda akhirnya melacak kueri satu kali dan karenanya perbaikan bug cenderung kecil dan terlokalisasi.T: Saya akan memiliki terlalu banyak metode di repositori saya.
A: Saya belum benar-benar melihat jalan keluar selain dari konsolidasi panggilan. Metode panggilan dalam repositori Anda benar-benar memetakan ke fitur-fitur dalam aplikasi Anda. Semakin banyak fitur, semakin banyak data panggilan khusus. Anda dapat menekan kembali fitur dan mencoba menggabungkan panggilan serupa menjadi satu.
Kompleksitas pada akhirnya harus ada di suatu tempat. Dengan pola repositori, kami telah mendorongnya ke antarmuka repositori alih-alih mungkin membuat banyak prosedur tersimpan.
Kadang-kadang saya harus mengatakan pada diri sendiri, "Yah, itu harus memberi di suatu tempat! Tidak ada peluru perak."
sumber
SELECT *
, bukan hanya memilih bidang yang Anda butuhkan. Sebagai contoh, lihat pertanyaan ini . Adapun semua pertanyaan ad-hock yang Anda bicarakan, saya tentu mengerti dari mana Anda berasal. Saya memiliki aplikasi yang sangat besar sekarang yang memiliki banyak dari mereka. Itu adalah "Yah, itu harus memberi di suatu tempat!" Saat ini, saya memilih kinerja maksimum. Namun, sekarang saya berurusan dengan BANYAK pertanyaan yang berbeda.reads
sering kali muncul masalah kinerja, Anda dapat menggunakan pendekatan kueri yang lebih khusus untuk mereka, yang tidak diterjemahkan ke dalam objek bisnis nyata. Kemudian, untukcreate
,update
dandelete
, gunakan ORM, yang bekerja dengan seluruh objek. Adakah pemikiran tentang pendekatan itu?Saya menggunakan antarmuka berikut:
Repository
- memuat, memasukkan, memperbarui, dan menghapus entitasSelector
- Menemukan entitas berdasarkan filter, dalam repositoriFilter
- merangkum logika penyaringanRepository
Database saya agnostik; pada kenyataannya itu tidak menentukan kegigihan; itu bisa apa saja: database SQL, file xml, layanan jarak jauh, alien dari luar angkasa dll. Untuk kemampuan pencarian,Repository
konstrukSelector
yang dapat disaring,LIMIT
-ed, diurutkan dan dihitung. Pada akhirnya, pemilih mengambil satu atau lebihEntities
dari ketekunan.Berikut ini beberapa contoh kode:
Kemudian, satu implementasi:
Idenya adalah bahwa generik
Selector
menggunakanFilter
tetapi implementasinyaSqlSelector
menggunakanSqlFilter
; yangSqlSelectorFilterAdapter
menyesuaikan generikFilter
dengan betonSqlFilter
.Kode klien membuat
Filter
objek (yang merupakan filter umum) tetapi dalam implementasi konkret dari pemilih filter tersebut ditransformasikan dalam filter SQL.Implementasi pemilih lainnya, seperti
InMemorySelector
, mengubah dariFilter
keInMemoryFilter
menggunakan khusus merekaInMemorySelectorFilterAdapter
; jadi, setiap implementasi pemilih dilengkapi dengan adaptor filternya sendiri.Menggunakan strategi ini, kode klien saya (di lapisan bisnis) tidak peduli dengan repositori atau implementasi pemilih tertentu.
PS Ini adalah penyederhanaan kode saya yang sebenarnya
sumber
Saya akan menambahkan sedikit tentang ini karena saya sedang mencoba untuk memahami semua ini sendiri.
# 1 dan 2
Ini adalah tempat yang sempurna bagi ORM Anda untuk melakukan pekerjaan berat. Jika Anda menggunakan model yang mengimplementasikan semacam ORM, Anda bisa menggunakan metode itu untuk menangani hal-hal ini. Buat fungsi orderBy Anda sendiri yang menerapkan metode Eloquent jika perlu. Menggunakan Eloquent misalnya:
Apa yang Anda cari adalah ORM. Tidak ada alasan Repositori Anda tidak dapat didasarkan pada satu. Ini akan mengharuskan Pengguna memperluas fasih, tapi saya pribadi tidak melihatnya sebagai masalah.
Namun, jika Anda ingin menghindari ORM, Anda harus "memutar sendiri" untuk mendapatkan apa yang Anda cari.
# 3
Antarmuka tidak seharusnya menjadi persyaratan yang sulit dan cepat. Sesuatu dapat mengimplementasikan antarmuka dan menambahkannya. Apa yang tidak bisa dilakukan adalah gagal untuk mengimplementasikan fungsi yang diperlukan dari antarmuka itu. Anda juga dapat memperluas antarmuka seperti kelas untuk menjaga hal-hal KERING.
Yang mengatakan, saya baru mulai memahami, tetapi realisasi ini telah membantu saya.
sumber
Saya hanya bisa berkomentar tentang cara kami (di perusahaan saya) menangani ini. Pertama-tama kinerja tidak terlalu menjadi masalah bagi kami, tetapi memiliki kode bersih / tepat adalah.
Pertama-tama kita mendefinisikan Model seperti
UserModel
yang menggunakan ORM untuk membuatUserEntity
objek. Ketika aUserEntity
dimuat dari model, semua bidang dimuat. Untuk bidang yang mereferensikan entitas asing, kami menggunakan model asing yang sesuai untuk membuat entitas terkait. Untuk entitas tersebut, data akan dimuat pada permintaan. Sekarang reaksi awal Anda mungkin ... ??? ... !!! izinkan saya memberi Anda sedikit contoh:Dalam kasus kami
$db
adalah ORM yang dapat memuat entitas. Model menginstruksikan ORM untuk memuat sekumpulan entitas dari tipe tertentu. ORM berisi pemetaan dan menggunakannya untuk menyuntikkan semua bidang untuk entitas tersebut ke entitas. Namun untuk bidang asing hanya id dari objek-objek yang dimuat. Dalam hal ini,OrderModel
menciptakanOrderEntity
hanya dengan id dari pesanan yang direferensikan. KetikaPersistentEntity::getField
dipanggil olehOrderEntity
entitas menginstruksikan itu model untuk malas memuat semua bidang ke dalamOrderEntity
s. Semua yangOrderEntity
terkait dengan satu UserEntity diperlakukan sebagai satu set hasil dan akan dimuat sekaligus.Keajaiban di sini adalah bahwa model dan ORM kami menyuntikkan semua data ke entitas dan entitas hanya menyediakan fungsi wrapper untuk
getField
metode generik yang disediakan olehPersistentEntity
. Untuk meringkas, kami selalu memuat semua bidang, tetapi bidang referensi entitas asing dimuat saat diperlukan. Hanya memuat sekelompok bidang sebenarnya bukan masalah kinerja. Namun memuat semua entitas asing yang mungkin akan menjadi penurunan kinerja BESAR.Sekarang untuk memuat kumpulan pengguna tertentu, berdasarkan klausa tempat. Kami menyediakan paket kelas berorientasi objek yang memungkinkan Anda untuk menentukan ekspresi sederhana yang dapat direkatkan bersama. Dalam kode contoh saya menamainya
GetOptions
. Ini adalah pembungkus untuk semua opsi yang memungkinkan untuk kueri pemilihan. Ini berisi kumpulan klausa mana, grup dengan klausa dan yang lainnya. Klausa tempat kami cukup rumit tetapi Anda jelas dapat membuat versi yang lebih mudah.Versi paling sederhana dari sistem ini adalah untuk melewatkan bagian WHERE dari kueri sebagai string langsung ke model.
Maaf atas tanggapan yang cukup rumit ini. Saya mencoba merangkum kerangka kerja kami secepat dan sejelas mungkin. Jika Anda memiliki pertanyaan tambahan, jangan ragu untuk bertanya dan saya akan memperbarui jawaban saya.
EDIT: Selain itu jika Anda benar-benar tidak ingin memuat beberapa bidang Anda bisa menentukan opsi pemuatan malas dalam pemetaan ORM Anda. Karena semua bidang pada akhirnya dimuat melalui
getField
metode Anda dapat memuat beberapa bidang menit terakhir ketika metode itu dipanggil. Ini bukan masalah yang sangat besar di PHP, tetapi saya tidak akan merekomendasikan untuk sistem lain.sumber
Ini adalah beberapa solusi berbeda yang pernah saya lihat. Ada pro dan kontra untuk masing-masing, tetapi bagi Anda untuk memutuskan.
Masalah # 1: Terlalu banyak bidang
Ini adalah aspek penting terutama ketika Anda memperhitungkan Pemindaian Hanya Indeks . Saya melihat dua solusi untuk mengatasi masalah ini. Anda dapat memperbarui fungsi Anda untuk mengambil parameter array opsional yang akan berisi daftar kolom untuk dikembalikan. Jika parameter ini kosong, Anda akan mengembalikan semua kolom dalam kueri. Ini bisa sedikit aneh; berdasarkan parameter Anda bisa mengambil objek atau array. Anda juga bisa menduplikasi semua fungsi Anda sehingga Anda memiliki dua fungsi berbeda yang menjalankan kueri yang sama, tetapi satu mengembalikan array kolom dan yang lainnya mengembalikan objek.
Masalah # 2: Terlalu banyak metode
Saya sempat bekerja dengan Propel ORM setahun yang lalu dan ini didasarkan pada apa yang saya ingat dari pengalaman itu. Propel memiliki opsi untuk menghasilkan struktur kelasnya berdasarkan skema database yang ada. Ini menciptakan dua objek untuk setiap tabel. Objek pertama adalah daftar panjang fungsi akses yang mirip dengan apa yang saat ini Anda daftarkan;
findByAttribute($attribute_value)
. Objek berikutnya mewarisi dari objek pertama ini. Anda dapat memperbarui objek anak ini untuk membangun fungsi pengambil yang lebih kompleks.Solusi lain akan digunakan
__call()
untuk memetakan fungsi yang tidak didefinisikan untuk sesuatu yang dapat ditindaklanjuti.__call
Metode Anda akan dapat mem-parsing findById dan findByName ke dalam kueri yang berbeda.Saya harap ini membantu setidaknya beberapa hal.
sumber
Saya menyarankan https://packagist.org/packages/prettus/l5-repository sebagai vendor untuk menerapkan Repositori / Kriteria dll ... di Laravel5: D
sumber
Saya setuju dengan @ ryan1234 bahwa Anda harus membagikan objek lengkap dalam kode dan harus menggunakan metode kueri umum untuk mendapatkan objek tersebut.
Untuk penggunaan eksternal / endpoint saya sangat suka metode GraphQL.
sumber
Naluri saya memberi tahu saya ini mungkin memerlukan antarmuka yang mengimplementasikan metode yang dioptimalkan query bersama metode generik. Kueri sensitif kinerja harus memiliki metode yang ditargetkan, sementara kueri yang jarang atau ringan ditangani oleh penangan generik, mungkin biaya pengontrol melakukan sedikit juggling.
Metode generik akan memungkinkan setiap kueri diterapkan, dan juga akan mencegah pemutusan perubahan selama periode transisi. Metode yang ditargetkan memungkinkan Anda untuk mengoptimalkan panggilan saat masuk akal, dan itu dapat diterapkan ke beberapa penyedia layanan.
Pendekatan ini akan mirip dengan implementasi perangkat keras yang melakukan tugas-tugas khusus yang dioptimalkan, sementara implementasi perangkat lunak melakukan pekerjaan yang ringan atau implementasi yang fleksibel.
sumber
Saya pikir graphQL adalah kandidat yang baik dalam kasus seperti itu untuk menyediakan bahasa permintaan skala besar tanpa meningkatkan kompleksitas repositori data.
Namun, ada solusi lain jika Anda tidak ingin menggunakan graphQL untuk saat ini. Dengan menggunakan DTO di mana objek digunakan untuk mengarungi data antara proses, dalam hal ini antara layanan / pengontrol dan repositori.
Sebuah jawaban elegan sudah disediakan di atas, namun saya akan mencoba memberikan contoh lain yang menurut saya lebih sederhana dan dapat berfungsi sebagai titik awal untuk proyek baru.
Seperti yang ditunjukkan dalam kode, kita hanya membutuhkan 4 metode untuk operasi CRUD. yang
find
metode akan digunakan untuk daftar dan membaca dengan melewati argumen objek. Layanan Backend dapat membangun objek kueri yang ditentukan berdasarkan string kueri URL atau berdasarkan parameter tertentu.Objek permintaan (
SomeQueryDto
) juga dapat mengimplementasikan antarmuka spesifik jika diperlukan. dan mudah untuk diperpanjang kemudian tanpa menambah kerumitan.Contoh penggunaan:
sumber