Ini adalah transkripsi yang lebih baik dari komentar awal saya di bawah pertanyaan Anda. Jawaban atas pertanyaan yang diajukan oleh OP dapat ditemukan di bagian bawah jawaban ini. Periksa juga catatan penting yang terletak di tempat yang sama.
Apa yang saat ini Anda gambarkan, Sipo, adalah pola desain yang disebut Rekaman aktif . Seperti halnya segala sesuatu, bahkan yang ini telah menemukan tempatnya di antara programmer, tetapi telah dibuang demi repositori dan pola data mapper karena satu alasan sederhana, skalabilitas.
Singkatnya, catatan aktif adalah objek, yang:
- mewakili objek dalam domain Anda (termasuk aturan bisnis, tahu cara menangani operasi tertentu pada objek, seperti jika Anda dapat atau tidak dapat mengubah nama pengguna dan sebagainya),
- tahu cara mengambil, memperbarui, menyimpan, dan menghapus entitas.
Anda mengatasi beberapa masalah dengan desain Anda saat ini dan masalah utama desain Anda dibahas pada poin terakhir, ke-6 (terakhir tapi tidak kalah penting, saya kira). Ketika Anda memiliki kelas yang Anda merancang konstruktor dan Anda bahkan tidak tahu apa yang harus dilakukan konstruktor, kelas mungkin melakukan sesuatu yang salah. Itu terjadi dalam kasus Anda.
Tetapi memperbaiki desain sebenarnya cukup sederhana dengan memecah representasi entitas dan logika CRUD menjadi dua (atau lebih) kelas.
Seperti inilah desain Anda sekarang:
Employee
- berisi informasi tentang struktur karyawan (atributnya) dan metode bagaimana memodifikasi entitas (jika Anda memutuskan untuk pergi dengan cara yang bisa berubah), berisi logika CRUD untuk Employee
entitas, dapat mengembalikan daftar Employee
objek, menerima Employee
objek saat Anda ingin memperbarui karyawan, dapat mengembalikan satu Employee
melalui metode sepertigetSingleById(id : string) : Employee
Wow, kelasnya tampak besar.
Ini akan menjadi solusi yang diusulkan:
Employee
- berisi informasi tentang struktur karyawan (atributnya) dan metode bagaimana memodifikasi entitas (jika Anda memutuskan untuk pergi dengan cara yang bisa berubah)
EmployeeRepository
- berisi logika CRUD untuk Employee
entitas, dapat mengembalikan daftar Employee
objek, menerima Employee
objek saat Anda ingin memperbarui karyawan, dapat mengembalikan satu Employee
melalui metode sepertigetSingleById(id : string) : Employee
Pernahkah Anda mendengar tentang pemisahan kekhawatiran ? Tidak, sekarang akan. Ini adalah versi Prinsip Tanggung Jawab Tunggal yang tidak terlalu ketat, yang mengatakan bahwa kelas seharusnya hanya memiliki satu tanggung jawab, atau seperti yang dikatakan Paman Bob:
Modul harus memiliki satu dan hanya satu alasan untuk berubah.
Cukup jelas bahwa jika saya dapat dengan jelas membagi kelas awal Anda menjadi dua yang masih memiliki antarmuka yang baik, kelas awal mungkin melakukan terlalu banyak, dan memang begitu.
Apa yang hebat tentang pola repositori, itu tidak hanya bertindak sebagai abstraksi untuk menyediakan lapisan tengah antara database (yang bisa berupa apa saja, file, noSQL, SQL, berorientasi objek), tetapi bahkan tidak perlu menjadi beton kelas. Dalam banyak bahasa OO, Anda dapat mendefinisikan antarmuka sebagai aktual interface
(atau kelas dengan metode virtual murni jika Anda menggunakan C ++) dan kemudian memiliki beberapa implementasi.
Ini sepenuhnya mengangkat keputusan apakah repositori adalah implementasi aktual Anda hanya mengandalkan antarmuka dengan benar-benar mengandalkan struktur dengan interface
kata kunci. Dan repositori persis seperti itu, itu adalah istilah mewah untuk abstraksi lapisan data, yaitu memetakan data ke domain Anda dan sebaliknya.
Hal hebat lainnya tentang memisahkannya ke dalam (setidaknya) dua kelas adalah bahwa sekarang Employee
kelas dapat dengan jelas mengelola datanya sendiri dan melakukannya dengan sangat baik, karena ia tidak perlu mengurus hal-hal sulit lainnya.
Pertanyaan 6: Jadi apa yang harus dilakukan oleh konstruktor di kelas yang baru dibuat Employee
? Sederhana saja. Seharusnya mengambil argumen, memeriksa apakah mereka valid (seperti usia seharusnya tidak boleh negatif atau nama tidak boleh kosong), menimbulkan kesalahan ketika data tidak valid dan jika validasi yang dilewatkan menetapkan argumen ke variabel pribadi entitas. Sekarang tidak dapat berkomunikasi dengan database, karena ia tidak tahu bagaimana melakukannya.
Pertanyaan 4: Tidak dapat dijawab sama sekali, tidak secara umum, karena jawabannya sangat tergantung pada apa yang sebenarnya Anda butuhkan.
Pertanyaan 5: Sekarang bahwa Anda telah memisahkan kelas membengkak menjadi dua, Anda dapat memiliki beberapa metode pembaruan langsung pada Employee
kelas, seperti changeUsername
, markAsDeceased
, yang akan memanipulasi data dari Employee
kelas hanya dalam RAM dan kemudian Anda bisa memperkenalkan metode seperti registerDirty
dari Pola Unit Kerja ke kelas repositori, di mana Anda akan membiarkan repositori tahu bahwa objek ini telah mengubah properti dan perlu diperbarui setelah Anda memanggil commit
metode.
Jelas, untuk pembaruan suatu objek harus memiliki id dan karenanya sudah disimpan, dan itu adalah tanggung jawab repositori untuk mendeteksi ini dan meningkatkan kesalahan ketika kriteria tidak terpenuhi.
Pertanyaan 3: Jika Anda memutuskan untuk mengikuti pola Unit Kerja, create
metode sekarang akan menjadi registerNew
. Jika tidak, saya mungkin akan menyebutnya sebagai save
gantinya. Tujuan dari repositori adalah untuk memberikan abstraksi antara domain dan lapisan data, karena ini saya akan merekomendasikan Anda bahwa metode ini (baik itu registerNew
atau save
) menerima Employee
obyek dan terserah kepada kelas menerapkan antarmuka repositori, yang atribut mereka memutuskan untuk mengeluarkan entitas tersebut. Melewati seluruh objek lebih baik sehingga Anda tidak perlu memiliki banyak parameter opsional.
Pertanyaan 2: Kedua metode sekarang akan menjadi bagian dari antarmuka repositori dan mereka tidak melanggar prinsip tanggung jawab tunggal. Tanggung jawab repositori adalah untuk menyediakan operasi CRUD untuk Employee
objek, itulah yang dilakukannya (selain Baca dan Hapus, CRUD diterjemahkan menjadi Buat dan Perbarui). Jelas, Anda dapat membagi repositori lebih jauh dengan memiliki EmployeeUpdateRepository
dan sebagainya, tetapi itu jarang diperlukan dan implementasi tunggal biasanya dapat berisi semua operasi CRUD.
Pertanyaan 1: Anda berakhir dengan Employee
kelas sederhana yang sekarang (di antara atribut lainnya) memiliki id. Apakah id diisi atau kosong (atau null
) tergantung pada apakah objek telah disimpan. Meskipun demikian, id masih merupakan atribut yang dimiliki entitas dan tanggung jawab Employee
entitas adalah untuk menjaga atributnya, maka dari itu menjaga idnya.
Entah suatu entitas memiliki atau tidak memiliki id, biasanya tidak penting sebelum Anda mencoba melakukan beberapa logika kegigihan padanya. Seperti disebutkan dalam jawaban untuk pertanyaan 5, itu adalah tanggung jawab repositori untuk mendeteksi Anda tidak mencoba menyelamatkan entitas yang sudah disimpan atau mencoba memperbarui entitas tanpa id.
Catatan penting
Perlu diketahui bahwa meskipun pemisahan kekhawatiran itu hebat, sebenarnya merancang lapisan repositori fungsional merupakan pekerjaan yang sangat membosankan dan menurut pengalaman saya sedikit lebih sulit untuk dilakukan dengan benar daripada pendekatan rekaman aktif. Tetapi Anda akan berakhir dengan desain yang jauh lebih fleksibel dan terukur, yang mungkin merupakan hal yang baik.
Employee
objek untuk memberikan abstraksi, pertanyaan 4. dan 5. umumnya tidak dapat dijawab, tergantung pada kebutuhan Anda, dan jika Anda memisahkan struktur dan operasi CRUD menjadi dua kelas, maka itu cukup jelas, konstruktorEmployee
tidak dapat mengambil data dari db lagi, jadi itu jawaban 6.