Di hampir semua keadaan, kunci utama bukan bagian dari domain bisnis Anda. Tentu, Anda mungkin memiliki beberapa objek penting yang menghadap pengguna dengan indeks unik ( UserName
untuk pengguna atau OrderNumber
untuk pesanan) tetapi dalam kebanyakan kasus, tidak perlu berbisnis mengidentifikasi objek domain secara terbuka dengan satu nilai atau serangkaian nilai, kepada siapa pun, tetapi mungkin pengguna administratif. Bahkan dalam kasus luar biasa itu, terutama jika Anda menggunakan pengidentifikasi unik global (GUID) , Anda akan lebih suka atau ingin menggunakan kunci alternatif daripada mengekspos kunci primer itu sendiri.
Jadi, jika pemahaman saya tentang desain yang digerakkan oleh domain akurat, kunci primer tidak perlu dan karenanya tidak boleh diekspos, dan baguslah. Mereka jelek dan kram gaya saya. Tetapi jika kita memilih untuk tidak memasukkan kunci utama dalam model domain, ada konsekuensi:
- Secara naif, objek transfer data (DTO) yang berasal secara eksklusif dari kombinasi model domain tidak akan memiliki kunci utama
- DTO masuk tidak akan memiliki kunci utama
Jadi, apakah aman untuk mengatakan bahwa jika Anda benar-benar akan tetap murni dan menghilangkan kunci utama dalam model domain Anda, Anda harus siap untuk dapat menangani setiap permintaan dalam hal indeks unik pada kunci utama itu?
Dengan kata lain, solusi manakah di bawah ini yang merupakan pendekatan yang tepat untuk berurusan dengan mengidentifikasi objek tertentu setelah menghapus PK dalam model domain?
- Mampu mengidentifikasi objek yang perlu Anda tangani dengan atribut lain
- Mendapatkan kunci utama kembali di DTO; yaitu, menghilangkan PK saat memetakan dari kegigihan ke domain, lalu menggabungkan kembali PK saat memetakan dari domain ke DTO?
EDIT: Mari kita buat beton ini.
Mengatakan model domain saya VoIPProvider
yang meliputi bidang-bidang seperti Name
, Description
, URL
, serta referensi suka ProviderType
, PhysicalAddress
dan Transactions
.
Sekarang katakanlah saya ingin membangun layanan web yang akan memungkinkan pengguna istimewa untuk mengelola VoIPProvider
.
Mungkin ID ramah pengguna tidak berguna dalam kasus ini; setelah semua, penyedia VoIP adalah perusahaan yang namanya cenderung berbeda dalam pengertian komputer dan bahkan cukup berbeda dalam pengertian manusia untuk alasan bisnis. Jadi mungkin cukup untuk mengatakan bahwa yang unik VoIPProvider
sepenuhnya ditentukan oleh (Name, URL)
. Jadi sekarang katakanlah saya memerlukan metode PUT api/providers/voip
agar pengguna yang istimewa dapat memperbarui VoIP
penyedia. Mereka mengirim VoIPProviderDTO
, yang mencakup banyak tetapi tidak semua bidang dari VoIPProvider
, termasuk beberapa perataan berpotensi. Namun, saya tidak bisa membaca pikiran mereka, dan mereka masih perlu memberi tahu saya penyedia mana yang sedang kita bicarakan.
Sepertinya saya memiliki 2 (mungkin 3) opsi:
- Sertakan kunci utama atau kunci alternatif dalam model domain saya dan kirimkan ke DTO, dan sebaliknya
- Identifikasi penyedia yang kami pedulikan melalui indeks unik, seperti
(Name, Url)
- Memperkenalkan semacam objek perantara yang selalu dapat memetakan antara lapisan kegigihan, domain, dan DTO dengan cara yang tidak mengekspos detail implementasi tentang lapisan kegigihan - katakanlah dengan memperkenalkan pengidentifikasi sementara di dalam memori ketika pergi dari domain ke DTO dan kembali,
sumber
Jawaban:
Inilah cara kami memecahkan masalah ini (sejak lebih dari 15 tahun, bahkan ketika istilah "desain yang didorong oleh domain" tidak ditemukan):
Jadi, setiap kali Anda membutuhkan kunci utama untuk tujuan teknis (seperti memetakan hubungan ke database), Anda memiliki satu tersedia, tetapi selama Anda tidak ingin "melihatnya", ubah tingkat abstraksi Anda ke "model pakar domain" ". Dan Anda tidak harus mempertahankan "dua model" (satu dengan PK dan satu tanpa); alih-alih, pertahankan hanya model tanpa PK dan gunakan pembuat kode untuk membuat DDL untuk DB Anda, yang menambahkan PK secara otomatis sesuai dengan aturan pemetaan.
Perhatikan bahwa ini tidak melarang untuk menambahkan "kunci bisnis" seperti "OrderNumber" tambahan, selain pengganti
OrderID
. Secara teknis, kunci bisnis ini menjadi kunci alternatif saat memetakan ke basis data Anda. Hanya menghindari menggunakan ini untuk membuat referensi ke tabel lain, selalu lebih suka menggunakan kunci pengganti jika memungkinkan, ini akan membuat segalanya jauh lebih mudah.Untuk komentar Anda: menggunakan kunci pengganti untuk mengidentifikasi catatan bukanlah operasi yang terkait dengan bisnis, ini adalah operasi yang murni teknis. Untuk memperjelas ini, lihat contoh Anda: selama Anda tidak mendefinisikan batasan unik lainnya, akan mungkin untuk memiliki dua objek VoIPProvider dengan kombinasi yang sama (nama, url), tetapi berbeda VoIPProviderIDs.
sumber
Anda harus dapat mengidentifikasi banyak objek dengan beberapa indeks unik, dan bukankah itu yang menjadi kunci utama (atau setidaknya menyiratkan ada satu).
Indeks unik tersedia sehingga Anda dapat lebih lanjut membatasi skema DB Anda, bukan sebagai pengganti grosir untuk PK. Jika Anda tidak mengekspos PK karena mereka jelek, tetapi mengekspos kunci yang unik sebagai gantinya ... Anda sebenarnya tidak melakukan sesuatu yang berbeda. (Saya menganggap Anda tidak mendapatkan PK dan kolom identitas dicampur di sini?)
sumber
Tanpa kunci primer di frontend, tidak ada cara mudah bagi backend untuk mengetahui apa yang Anda kirim. Untuk memperbaikinya, Anda akan membutuhkan satu ton pekerjaan tambahan untuk mengurai data, yang akan merusak kinerja dan mungkin membutuhkan waktu lebih lama dan lebih buruk daripada melampirkan kunci ke setiap item.
Sebagai contoh, katakanlah saya ingin mengedit pesan di aplikasi; bagaimana aplikasi mengetahui pesan mana yang ingin saya edit tanpa kunci primer terpasang? Mengedit objek terjadi setiap saat dan melakukan itu tanpa kunci hampir tidak mungkin. Tetapi jika Anda memiliki objek yang tidak seharusnya diedit kemudian lewati tombol jika Anda pikir itu mengganggu, tetapi memiliki kunci primer dapat meningkatkan kinerja di sini.
sumber
MessageSender
,MessageRecipient
,TimeSent
- yang harus unik.Alasan kami menggunakan PK terkait non-bisnis adalah untuk memastikan bahwa sistem kami memiliki metode yang mudah dan konsisten untuk menentukan apa yang diinginkan pengguna.
Saya melihat Anda menjawab dengan komentar: MessageSender, MessageRecipient, TimeSent (untuk sebuah pesan). Anda dapat MASIH memiliki ambiguitas dengan cara ini (misalnya, dengan pesan yang dihasilkan sistem memicu sesuatu yang sering terjadi). Dan bagaimana Anda akan memvalidasi MessageSender dan MessageRecipient di sini? Misalkan Anda memvalidasi mereka menggunakan FirstName, Lastname, DateOfBirth, Anda pada akhirnya akan mengalami situasi di mana Anda memiliki 2 orang yang lahir pada hari yang sama dengan nama yang sama persis. Belum lagi Anda akan mengalami situasi di mana Anda memiliki pesan bernama
tacostacostacos-America-1980-Doc Brown-France-1965-23/5/2014-11:43:54.003UTC+200
. Itu monster dari sebuah nama, dan Anda masih tidak memiliki jaminan Anda hanya akan memiliki 1 dari itu.alasan kami menggunakan kunci utama adalah karena kami TAHU itu akan menjadi unik selama masa pakai perangkat lunak, tidak peduli data apa yang dimasukkan, dan kami TAHU itu akan menjadi format yang dapat diprediksi (apa yang terjadi jika kunci Anda di atas memiliki tanda hubung dalam nama pengguna? seluruh sistem Anda digunakan)
Anda tidak perlu menunjukkan ID Anda kepada pengguna Anda. Anda dapat menyembunyikannya (terlihat jelas jika perlu, melalui URL).
Alasan lain mengapa PK sangat berguna adalah sesuatu yang dapat Anda simpulkan dari hal di atas: PK membuatnya sehingga Anda tidak perlu memaksa komputer untuk menafsirkan kode yang dibuat pengguna. Jika pengguna China menggunakan kode Anda dan memasukkan banyak karakter Cina, kode Anda tiba-tiba tidak perlu dapat bekerja dengan ini secara internal, tetapi hanya dapat menggunakan Panduan yang dihasilkan sistem. Jika Anda memiliki pengguna Arab yang memasuki tulisan Arab, sistem Anda tidak harus mengatasinya secara internal, tetapi pada dasarnya dapat mengabaikan bahwa mereka ada di sana.
Seperti yang dikatakan orang lain, Guid adalah sesuatu yang dapat disimpan secara internal dalam ukuran tetap. Anda tahu apa yang Anda kerjakan dan itu adalah sesuatu yang dapat digunakan secara universal. Anda tidak perlu membuat aturan desain tentang cara membuat pengenal tertentu dan menyimpannya. Jika sistem Anda hanya menggunakan 10 huruf pertama dari sebuah nama, itu tidak melihat perbedaan antara Michael Guggenheimer dan Michael Gugstein, dan itu akan membingungkan 2 ini. Jika Anda memotongnya pada panjang yang sewenang-wenang, Anda dapat mengalami kebingungan. Jika Anda membatasi input pengguna, Anda dapat mengalami masalah dengan keterbatasan pengguna.
Ketika saya melihat sistem yang ada seperti Dynamics CRM, mereka juga menggunakan kunci internal (PK) bagi pengguna untuk memanggil satu catatan. Jika pengguna memiliki kueri yang tidak melibatkan ID, mereka mengembalikan serangkaian jawaban yang mungkin dan membiarkan pengguna memilihnya. Jika ada kemungkinan ambiguitas, mereka akan memberikan pilihan kepada pengguna.
Akhirnya, itu juga keamanan melalui ketidakjelasan. Jika Anda tidak tahu ID rekaman, satu-satunya pilihan adalah menebaknya. jika ID mudah ditebak (karena informasi yang dibuatnya tersedia untuk umum), siapa pun dapat mengubahnya. Anda bahkan dapat mendorong pengguna untuk mengubahnya melalui metode CSRF atau XSS klasik. Sekarang, jelas keamanan Anda seharusnya sudah diperhitungkan dan dimitigasi sebelum pernah menerbitkan versi live, tetapi Anda harus tetap mempersulit potensi penyalahgunaan terjadi.
sumber
Saat mengeluarkan pengidentifikasi untuk sistem eksternal, Anda hanya harus memberikan URI, atau alternatif kunci atau seperangkat kunci yang memiliki sifat yang sama dengan URI, daripada membuka kunci primer basis data secara langsung (dari sini dan seterusnya saya akan merujuk ke baik URI atau kunci atau serangkaian kunci yang memiliki properti yang sama dengan URI dengan hanya URI, dengan kata lain URI di bawah ini tidak selalu berarti RFC 3986 URI).
URI mungkin atau mungkin tidak mengandung kunci utama objek dan itu mungkin atau mungkin tidak benar-benar terdiri dari kunci alternatif. Itu tidak masalah. Yang penting adalah bahwa hanya sistem yang menghasilkan URI diizinkan untuk membagi atau menggabungkan URI untuk membentuk pemahaman tentang apa objek yang dimaksud. Sistem eksternal harus selalu menggunakan URI sebagai pengidentifikasi buram. Tidak masalah jika pengguna manusia dapat mengidentifikasi bahwa salah satu bagian dari URI sebenarnya adalah kunci pengganti basis data atau bahwa itu terdiri dari beberapa kunci bisnis yang dikelompokkan bersama, atau bahwa itu sebenarnya adalah basis64 dari nilai-nilai itu. Ini tidak relevan. Yang penting adalah bahwa sistem eksternal tidak perlu diminta untuk memahami apa arti pengidentifikasi untuk menggunakan pengidentifikasi. Sistem eksternal tidak boleh diminta untuk mengurai komponen dalam pengidentifikasi atau menggabungkan pengidentifikasi dengan pengidentifikasi lain untuk merujuk pada sesuatu di sistem Anda.
Menggunakan GUID memenuhi beberapa kriteria ini, namun pengidentifikasi seperti GUID bisa sulit untuk kembali ke objek bahkan di dalam sistem Anda, jadi kunci yang buram bahkan sistem Anda seperti GUID hanya boleh digunakan jika klien menguraikan URI / pengidentifikasi sebenarnya menimbulkan risiko keamanan.
Kembali ke contoh VoIP Anda, katakan bahwa penyedia VoIP dapat ditentukan secara unik baik oleh (VoIPProviderID) atau dengan (Nama, URL) atau dengan (GUID). Ketika sistem eksternal perlu memperbarui penyedia VoIP, itu hanya dapat melewati PUT / provider / by-id / 1234 atau
PUT /provider/foo-voip/bar-domain.com
atauPUT /3F2504E0-4F89-41D3-9A0C-0305E82C3301
dan sistem Anda akan memahami bahwa sistem eksternal ingin memperbarui VoIPProvider. URI ini dihasilkan oleh sistem Anda dan hanya sistem Anda yang perlu memahami bahwa semua itu berarti hal yang sama. Sistem eksternal harus memperlakukan apa saja yang ada di URI pada dasarnya aPUT <whatever>
.Misalkan Anda memiliki data untuk penyedia VoIP yang berbeda yang disimpan dalam tabel yang berbeda dengan skema yang berbeda (dengan demikian rangkaian kunci yang berbeda mengidentifikasi setiap penyedia VoIP berdasarkan tabel tempat penyimpanan mereka). Ketika Anda memiliki URI, mereka dapat diakses secara seragam oleh sistem eksternal, tanpa memperhatikan bagaimana sistem Anda mengidentifikasi penyedia VoIP tertentu. Untuk sistem eksternal itu semua hanya pointer buram.
Ketika sistem Anda menggunakan URI untuk merujuk ke objek sedemikian rupa, Anda tidak membocorkan apa pun tentang bagaimana Anda menerapkan sistem Anda. Anda menghasilkan URI dan klien hanya memberikannya kembali kepada Anda.
sumber
Saya harus menargetkan pernyataan yang sangat tidak akurat dan naif ini:
Nama-nama mengerikan seperti kunci, karena sering berubah. Perusahaan mungkin memiliki banyak nama selama masa hidupnya, perusahaan dapat menggabungkan, membagi, menggabungkan lagi, membuat anak perusahaan yang berbeda untuk keperluan pajak khusus yang memiliki 0 karyawan tetapi semua pelanggan yang kemudian mempekerjakan karyawan dari anak perusahaan yang sama sekali berbeda.
Kemudian kita masuk ke fakta bahwa nama perusahaan bahkan tidak unik, seperti yang ditunjukkan oleh tengara Apple vs Apple .
Mapper relasional atau kerangka kerja objek yang baik harus mengabstraksi kunci primer dan membuatnya tidak terlihat, tetapi mereka ada di sana, dan mereka biasanya akan menjadi satu - satunya cara untuk secara unik mengidentifikasi objek dalam database Anda.
Untuk referensi, saya lebih suka bagaimana Django menangani ini:
Dengan cara ini, detail penyedia pelanggan dapat diakses dalam kode dengan menggunakan:
Meskipun kunci Utama / Asing tidak terlihat, mereka ada di sana, dan dapat digunakan untuk mengakses item, tetapi disarikan.
sumber
Saya pikir kita sering masih salah melihat masalah ini dari perspektif DB: oh, tidak ada kunci alami, jadi kita perlu membuat kunci pengganti. Oh tidak, kami tidak dapat membuka kunci pengganti kembali ke objek domain, itu bocor dll.
Namun, terkadang sikap yang lebih baik adalah ini: jika objek bisnis (domain) tidak memiliki kunci alami, maka mungkin harus diberikan satu. Ini adalah masalah domain bisnis dua kali lipat: Pertama, hal-hal memerlukan identitas, bahkan tanpa adanya database. Kedua, walaupun kami mencoba berpura-pura kegigihan adalah ide abstrak yang tidak terlihat oleh domain, kenyataannya adalah bahwa kegigihan masih merupakan konsep bisnis. Jelas, ada masalah di mana kunci alami yang dipilih tidak didukung oleh DB sebagai kunci utama (misalnya GUID pada beberapa sistem) - dalam hal ini Anda perlu menambahkan kunci pengganti.
Dengan demikian, Anda berakhir di tempat yang sangat mirip, misalnya pelanggan Anda memiliki ID integer, tetapi bukannya merasa sakit karena telah bocor dari DB ke domain, Anda merasa senang karena bisnis telah sepakat bahwa semua pelanggan akan diberi ID, dan Anda tetap menggunakannya untuk DB, seperti yang harus Anda lakukan. Anda masih bebas untuk memperkenalkan pengganti, misalnya untuk mendukung penggantian nama ID pelanggan.
Pendekatan ini juga berarti bahwa jika objek domain menyentuh lapisan persistensi, dan tidak memiliki ID, maka itu mungkin semacam objek nilai, jadi tidak perlu ID.
sumber