Merujuk nilai basis data dalam logika bisnis

43

Saya kira ini adalah pertanyaan lain tentang hard coding dan praktik terbaik. Katakanlah saya memiliki daftar nilai, katakanlah buah, disimpan dalam basis data (harus ada dalam basis data karena tabel digunakan untuk keperluan lain seperti laporan SSR), dengan ID:

1 Apple 
2 Banana 
3 Grapes

Saya dapat menyajikannya kepada pengguna, ia memilih satu, itu akan disimpan di profilnya sebagai FavouriteFruit dan ID disimpan dalam catatannya di database.

Ketika datang ke aturan bisnis / logika domain, apa saja rekomendasi untuk menetapkan logika ke nilai tertentu. Katakanlah jika pengguna telah memilih Grapes Saya ingin melakukan beberapa tugas tambahan, apa cara terbaik untuk referensi nilai Grapes:

// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")

// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes

// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)

atau sesuatu yang lain?

Karena tentu saja FavouriteFruit akan digunakan di seluruh aplikasi, daftar dapat ditambahkan ke, atau diedit.

Seseorang mungkin memutuskan bahwa mereka ingin 'Grape' diganti namanya menjadi 'Grape' dan ini tentu saja akan mematahkan opsi string hardcoded.

Hardcoded ID tidak sepenuhnya jelas, seperti yang ditunjukkan, Anda bisa menambahkan komentar untuk mengidentifikasi item mana yang cepat.

Opsi enum melibatkan duplikasi data dari database yang tampaknya salah karena mungkin tidak sinkron.

Bagaimanapun, terima kasih sebelumnya atas segala komentar atau saran.

Kate
sumber
1
Terima kasih semuanya: saran dan saran umum sangat membantu. @RemcoGerlich ide Anda untuk memisahkan masalah string yang digunakan untuk tujuan tampilan dan yang terpisah sebagai kode pencarian untuk kode yang lebih mudah dibaca sangat baik.
Kate
1
Saya akan memberikan @Mike Nakis objek preloaded Anda dengan ide pergi karena ini sepertinya yang terbaik dari kedua dunia.
Kate
1
Saya akan menyarankan variasi pada solusi pertama Anda. Mintalah tabel Anda berisi kolom ke-3 untuk bagaimana itu akan diproses, dan Anda menggunakan bidang itu untuk menentukan kode apa yang akan dieksekusi. Bukan bidang tampilan, dan dapat dibagikan di antara banyak buah.
Kickstart
1
Opsi enum melibatkan duplikasi data dari database yang tampaknya salah karena mungkin tidak sinkron. Saya suka ini, sebenarnya. Ini seperti pembukuan entri ganda. Jika kedua sisi buku besar tidak seimbang Anda akan tahu ada sesuatu yang salah. Itu membuat perubahan hal-hal yang lebih disengaja.
radarbob
1
Hmmm ... Jika ada hubungan ID 1: 1 dengan sebuah String maka ini berlebihan dan memiliki keduanya tidak ada gunanya. String dapat berfungsi sebagai kunci DB sama seperti integer. MyApplication.Grape.IDberbicara gagap. "Apple" bukan "Red_Apple" lebih dari ID 3 juga 4. Jadi potensi untuk mengubah nama "Apple" menjadi "Red_Apple" tidak masuk akal daripada menyatakan bahwa 3 adalah 4 (dan mungkin bahkan 3). Tujuan dari enum adalah untuk memisahkan DNA numeriknya. Jadi mungkin sudah saatnya untuk benar - benar memisahkan kunci DB relasional sewenang-wenang yang secara harfiah tidak memiliki makna dalam model bisnis seseorang.
radarbob

Jawaban:

31

Hindari string dan konstanta sihir di semua biaya. Mereka benar-benar keluar dari pertanyaan, mereka bahkan tidak boleh dianggap sebagai pilihan. Ini tampaknya membuat Anda hanya memiliki satu opsi yang aktif: pengidentifikasi, yaitu enum. Namun, ada juga satu opsi lagi, yang menurut saya adalah yang terbaik. Mari kita sebut opsi ini "Objek Dimuat". Dengan Objek yang Dimuat, Anda dapat melakukan hal berikut:

if( user.FavouriteFruit.ID == MyApplication.Grape.ID )

Apa yang baru saja terjadi di sini adalah bahwa saya sudah jelas memuat seluruh baris Grapeke dalam memori, jadi saya memiliki ID-nya yang siap digunakan dalam perbandingan. Jika Anda menggunakan Pemetaan Objek-Relasional (ORM), itu terlihat lebih baik:

if( user.FavouriteFruit == MyApplication.Grape )

(Itu sebabnya saya menyebutnya "Objek Preloaded".)

Jadi, yang saya lakukan adalah selama startup saya memuat semua tabel "enumerasi" (tabel kecil seperti hari dalam seminggu, bulan dalam setahun, jenis kelamin, dll.) Ke dalam kelas domain aplikasi utama. Saya memuatnya dengan nama, karena jelas, MyApplication.Grapeharus menerima baris yang disebut "Anggur", dan saya menyatakan bahwa masing-masing dari mereka ditemukan. Jika tidak, kami memiliki galat run-time yang dijamin selama startup, yang merupakan kesalahan paling ganas dari semua galat run-time.

Mike Nakis
sumber
17
Saya tidak setuju dengan jawabannya, tetapi saya pikir keharusan untuk "Menghindari string dan konstanta sihir di semua biaya" tidak setuju dengan sisa jawaban, yang sebenarnya mengharuskan Anda memiliki setidaknya satu tempat di mana konstanta sihir atau string digunakan dalam mengisi "objek yang dimuat sebelumnya". Itu penting, saya pikir, karena ada yang cara untuk menghindari "string dan konstanta ajaib" yang sama sekali, meskipun itu biasanya lebih obfuscating dari itu layak ...
svidgen
2
@viden tidakkah Anda setuju bahwa ada perbedaan mendasar antara penghamburan-ikatan-nama di semua tempat vs mengikat-oleh-nama hanya sekali, untuk memuat konten catatan bertuliskan nama yang sama, dan melakukannya hanya saat startup, di mana kesalahan runtime hampir sama jinaknya dengan kesalahan kompilasi? Bagaimanapun, cara untuk menghindari ikatan nama sekecil apa pun selalu menarik, terlepas dari kebingungan yang Anda sebutkan, jadi saya ingin tahu apa yang ada dalam pikiran Anda.
Mike Nakis
Oh, saya setuju sepenuhnya. Dan, mengingat sifat OP, saya hanya menyarankan jawaban ini dapat mengambil manfaat dari mengubah "di semua biaya" menjadi "bila memungkinkan dan layak" atau sesuatu yang serupa. ... Jika saya punya lebih banyak waktu, hanya demi kelengkapan saja, saya akan menulis jawaban yang berhubungan dengan semacam metaprogramming omong kosong ... tapi, bukan itu yang mungkin dibutuhkan OP (atau siapa pun dalam kebanyakan kasus) . Tapi, solusi metaprogamming akan lebih sesuai dengan pernyataan pertama Anda apa adanya.
svidgen
1
@ user469104 perbedaannya adalah id dapat berubah, dan aplikasi akan tetap memuat semua baris dengan benar dan melakukan semua perbandingan dengan benar. Juga, Anda bebas untuk memperbaiki kode dan mengganti nama baris dengan cara apa pun yang Anda suka, dan satu-satunya tempat di mana Anda harus mencari barang untuk diperbaiki adalah pada permulaan aplikasi, dan itu cenderung sangat jelas: Grape = fetchRow( Fruit.class, NameColumn, "Grape" ); Dan jika Anda lakukan sesuatu dengan tidak benar, surat AssertionErrorwasiat akan memberi tahu Anda.
Mike Nakis
1
@ grahamparks tidak lebih dari sebuah enumstring ajaib. Intinya adalah konsentrasi semua ikatan-dengan-nama hanya di satu tempat , validasi semuanya selama startup , dan ketik keamanan .
Mike Nakis
7

Memeriksa string adalah yang paling mudah dibaca, tetapi itu tugas ganda: itu digunakan baik sebagai pengidentifikasi dan deskripsi (yang dapat berubah karena alasan yang tidak terkait).

Saya biasanya membagi kedua tugas menjadi bidang yang terpisah:

id  code    description
 1  grape   Grapes
 2  apple   Apple

Di mana deskripsi dapat berubah (tetapi bukan "Anggur" menjadi "Pisang"), tetapi kode tidak diizinkan untuk berubah, selamanya.

Meskipun ini sebagian besar karena id kita hampir selalu dihasilkan secara otomatis, dan karenanya tidak cocok. Jika Anda dapat memilih id secara bebas, mungkin Anda dapat menjamin bahwa mereka selalu benar dan menggunakannya.

Juga, seberapa sering seseorang benar-benar mengedit "Anggur" menjadi "Anggur"? Mungkin semua ini tidak diperlukan.

RemcoGerlich
sumber
8
Saya kira belum ada redundansi yang lebih banyak adalah jawabannya ...
Robbie Dee
4
Saya telah mempertimbangkan opsi ini juga, dan saya telah mencobanya, tetapi inilah yang akhirnya terjadi: pada beberapa titik, "apel" harus dibedakan menjadi "green_apple" dan "red_apple". Tetapi karena "apel" sudah digunakan di banyak tempat dalam kode, saya tidak bisa mengganti nama, jadi saya harus punya "apel" dan "green_apple". Dan sebagai hasilnya, Sheldon di dalam diri saya mencegah saya tidur selama beberapa malam sampai saya masuk ke sana dan mem-refact semuanya untuk "Preloaded Objects". (lihat jawaban saya.)
Mike Nakis
1
Saya pasti suka Objek Preloaded Anda, tetapi jika "apel" Anda dibedakan, bukankah Anda harus membahas semuanya, metode apa pun yang Anda pilih?
RemcoGerlich
Anda bahkan mungkin memiliki tabel terpisah untuk nama deskripsi, untuk mendukung internasionalisasi.
Erik Eidt
1
@MikeNakis dan Refactoring pada dasarnya adalah Pencarian & Ganti seluruh Codebase Anda menggantikan Fruit.Apple dengan Fruit.GreenApple. Jika saya menggunakan nilai-nilai String Hardcoded saya akan melakukan Pencarian & Ganti seluruh Codebase untuk mengganti "apple" dengan "green_apple" yang merupakan hal yang sama. - Refactoring memang terasa lebih baik, karena IDE sedang melakukan Replacing.
Falco
4

Apa yang Anda harapkan di sini adalah logika pemrograman agar secara otomatis dapat beradaptasi dengan perubahan data. Opsi statis sederhana seperti Enum tidak berfungsi di sini karena Anda tidak dapat secara layak menambahkan enum tambahan dalam runtime.

Beberapa pola yang saya lihat:

  • Enums + default untuk melindungi terhadap entri basis data baru yang merusak hari program Anda.
  • Pengkodean tindakan yang harus dilakukan (logika biz) dalam database itu sendiri. Dalam banyak kasus ini sangat mungkin karena banyak logika dapat digunakan kembali. Implementasi dari logika harus dalam program.
  • Atribut / kolom tambahan dalam database untuk menandai nilai baru sebagai 'to-be-diabaikan' dalam program sampai program disebarkan dengan benar.
  • Gagal mekanisme cepat di sekitar jalur kode yang memuat / memuat nilai dari database. (Jika tindakan yang sesuai tidak ada dalam program DAN tidak ditandai untuk diabaikan, jangan lakukan penyegaran).

Secara umum saya suka data yang lengkap dalam hal merujuk pada tindakan yang menyiratkan - meskipun tindakan itu sendiri dapat diimplementasikan di tempat lain. Kode apa pun yang menentukan tindakan yang tidak bergantung pada data baru saja membelokkan representasi data Anda yang kemungkinan besar akan menyimpang dan mengarah ke bug.

Subu Sankara Subramanian
sumber
4

Menyimpannya di kedua tempat (di meja dan di ENUM) tidak terlalu buruk. Alasannya adalah sebagai berikut:

Dengan menyimpannya dalam tabel basis data, kita dapat menegakkan integritas referensial dalam basis data melalui kunci asing. Jadi, ketika Anda mengasosiasikan seseorang atau entitas apa pun untuk buah, itu hanya buah yang ada di tabel database.

Menyimpannya sebagai ENUM juga masuk akal karena kita dapat menulis kode tanpa string ajaib dan membuat kode lebih mudah dibaca. Ya, mereka harus tetap menyinkronkan, tetapi betapa sulitnya menambahkan baris ke ENUM dan pernyataan penyisipan baru ke database.

Satu hal, setelah ENUM didefinisikan, jangan ubah nilainya. Misalnya, jika Anda memiliki:

  • apel
  • Anggur

JANGAN mengganti nama Grape menjadi Grapes. Cukup tambahkan ENUM baru.

  • apel
  • Anggur
  • Anggur

Jika Anda perlu melakukan migrasi data, maka terapkan pembaruan untuk memindahkan semua Anggur ke Anggur.

Jon Raynor
sumber
Sebagai langkah selanjutnya saya telah bekerja di toko-toko di mana nilai metadata memiliki tanda hapus pada tabel untuk menunjukkan bahwa mereka tidak boleh digunakan (mereka sudah usang atau ada versi yang lebih baru).
Robbie Dee
1

Anda benar untuk menanyakan pertanyaan ini, ini adalah pertanyaan yang bagus ketika Anda berusaha untuk bertahan terhadap evaluasi kondisi yang tidak akurat.

Yang mengatakan, evaluasi ( ifkondisi Anda ) tidak perlu menjadi fokus bagaimana Anda mengatasinya. Alih-alih, perhatikan cara Anda menyebarkan perubahan yang akan menyebabkan masalah 'tidak sinkron'.

Pendekatan String

Jika Anda harus menggunakan string, mengapa tidak memaparkan fungsionalitas mengubah daftar melalui UI? Rancang sistem sehingga saat diubah Grapemenjadi Grapes, misalnya, Anda memperbarui semua catatan yang saat ini dirujuk Grape.

Pendekatan ID

Saya selalu lebih suka referensi ID, meskipun ada kompromi dari beberapa keterbacaan. The list may be added tolagi dapat menjadi sesuatu yang Anda akan diberitahu jika Anda mengekspos fitur UI seperti itu. Jika Anda khawatir dengan pemesanan ulang item yang mengubah id, sebarkan lagi perubahan tersebut ke semua catatan dependen lagi. Demikian pula untuk di atas. Pilihan lain (mengikuti konvensi normalisasi yang tepat, adalah memiliki kolom enum / id Anda - dan merujuk FruitDetailtabel yang lebih terperinci , yang memiliki kolom 'Pesanan' yang dapat Anda cari).

Apa pun itu, Anda dapat melihat saya menyarankan untuk mengontrol amandemen atau memperbarui daftar Anda. Apakah Anda melakukan ini melalui penggunaan ORM atau akses data lainnya ditentukan oleh spesifikasi teknologi Anda. Apa yang Anda, pada dasarnya, lakukan adalah mengharuskan orang yang jauh dari DB untuk perubahan seperti itu - yang menurut saya baik-baik saja. Sebagian besar CRM utama akan membuat persyaratan yang sama.

JᴀʏMᴇᴇ
sumber
1
Dalam database , id numerik sedang disimpan untuk catatan anak, khusus untuk menghindari masalah itu. Pertanyaan ini adalah tentang bagaimana antarmuka ke bahasa pemrograman.
Clockwork-Muse
1
@ Clockwork-Muse - untuk menghindari masalah yang mana? Itu tidak masuk akal.
JᴀʏMᴇᴇ
Saya menggunakan pendekatan ID cukup sedikit, tetapi ID terkunci dan tidak dapat berubah. String yang terlampir tentu saja bisa karena orang sering suka mengganti nama benda "truk" menjadi "truk", dll, sedangkan benda itu sendiri (diwakili oleh ID) tidak berubah.
Brian Knoblauch
Jika Anda menggunakan pendekatan ID, bagaimana Anda menangani database pengembangan vs produksi? Dengan ID yang bertambah secara otomatis, menambahkan item ke kedua DB dalam urutan berbeda akan menghasilkan ID yang berbeda.
Pelindung satu
Tidak harus kenaikan otomatis? Seharusnya tidak dalam kasus ini, terutama jika itu adalah nilai integer enum yang mendasari yang kami gunakan.
JᴀʏMᴇᴇ
0

Masalah yang sangat umum. Sementara menduplikasi sisi klien data mungkin tampaknya melanggar prinsip KERING , itu sebenarnya karena perbedaan paradigma antara lapisan.

Memiliki enum (atau apa pun) keluar dari langkah dengan database sebenarnya tidak biasa juga. Anda mungkin telah mendorong nilai lain ke tabel metadata untuk mendukung fitur laporan baru yang belum digunakan dalam kode sisi klien.

Kadang-kadang terjadi sebaliknya juga. Nilai enum baru akan ditambahkan ke sisi klien tetapi pembaruan DB tidak dapat terjadi sampai DBA dapat menerapkan perubahan.

Robbie Dee
sumber
Ya, Anda telah menjelaskan masalahnya. Apa solusinya?
Pelindung satu
1
@Protectorone Anda menganggap ada adalah solusi peluru perak yang merupakan asumsi yang salah dalam pengalaman saya. Yang terbaik yang dapat Anda harapkan adalah bahwa beberapa entitas bisnis memiliki domain masalah sehingga setidaknya Anda dapat melihat sisi mana yang tertinggal - sisi klien atau sisi basis data. Perbankan dan keuangan biasanya sangat efisien dalam hal ini karena sektor ritel terasa kurang begitu ...
Robbie Dee
0

Dengan asumsi bahwa kita sedang berbicara tentang apa yang pada dasarnya adalah pencarian statis maka opsi ketiga - enum - pada dasarnya adalah satu-satunya pilihan yang waras. Ini yang akan Anda lakukan jika database tidak terlibat sehingga masuk akal.

Pertanyaannya kemudian menjadi satu tentang bagaimana menjaga enums dan tabel statis / lookup di database dalam sinkronisasi dan sayangnya itu bukan masalah saya belum memiliki jawaban yang lengkap.

Secara pilihan saya melakukan semua pemeliharaan skema saya dalam kode dan oleh karena itu saya dapat menjaga hubungan antara membangun aplikasi dan versi skema yang diharapkan sehingga mudah untuk menjaga pencarian dan enum dalam sinkronisasi tetapi itu adalah sesuatu yang harus diingat untuk melakukan. Akan lebih baik jika lebih otomatis (dan juga tes integrasi otomatis untuk memastikan bahwa enum dan pencarian cocok akan membantu) tetapi itu bukan sesuatu yang pernah saya terapkan.

Murph
sumber
1
Saya tidak percaya ini hanya pencarian statis, kalau tidak mereka hanya bisa ditarik dari database dan dikonsumsi apa adanya. Masalahnya seperti yang saya pahami adalah kapan logika bisnis diterapkan tergantung pada nilai pencarian yang digunakan. Tapi, selain itu, ya - enum umumnya digunakan untuk tujuan ini.
Robbie Dee
Oke, saya butuh istilah yang lebih baik "untuk pencarian statis" konteks yang Anda gambarkan adalah apa yang saya maksudkan :) Kuncinya adalah "statis" - ini adalah nilai yang tidak mengubah masalah adalah menambahkan nilai baru dan mengubah "label" ( tetapi bukan maksudnya) untuk nilai yang ada.
Murph