Tham chiếu các giá trị cơ sở dữ liệu trong logic nghiệp vụ


43

Tôi đoán đây là một câu hỏi khác về mã hóa cứng và thực tiễn tốt nhất. Giả sử tôi có một danh sách các giá trị, giả sử là trái cây, được lưu trữ trong cơ sở dữ liệu (nó cần phải có trong cơ sở dữ liệu vì bảng được sử dụng cho các mục đích khác như báo cáo SSRS), với ID:

1 Apple 
2 Banana 
3 Grapes

Tôi có thể trình bày chúng cho người dùng, anh ta chọn một, nó được lưu trong hồ sơ của anh ta dưới dạng FavouriteFnut và ID được lưu trong hồ sơ của anh ta trong cơ sở dữ liệu.

Khi nói đến các quy tắc kinh doanh / logic miền, các khuyến nghị để gán logic cho các giá trị cụ thể là gì. Giả sử nếu người dùng đã chọn Nho tôi muốn thực hiện một số tác vụ bổ sung, cách tốt nhất để tham chiếu giá trị Nho:

// 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)

hay cái gì khác?

Vì tất nhiên FavouriteFnut sẽ được sử dụng trong toàn bộ ứng dụng, danh sách có thể được thêm vào hoặc chỉnh sửa.

Ai đó có thể quyết định rằng họ muốn 'Nho' được đổi tên thành 'Nho' và điều này tất nhiên sẽ phá vỡ tùy chọn chuỗi mã hóa cứng.

ID mã hóa cứng không hoàn toàn rõ ràng, như được hiển thị, bạn chỉ cần thêm một nhận xét để nhanh chóng xác định đó là mục nào.

Tùy chọn enum liên quan đến việc sao chép dữ liệu từ cơ sở dữ liệu có vẻ sai vì nó có thể không đồng bộ.

Dù sao, cảm ơn trước cho bất kỳ ý kiến ​​hoặc đề xuất.


1
Cảm ơn tất cả mọi người: những gợi ý và lời khuyên chung thực sự hữu ích. @RemcoGerlich ý tưởng của bạn để phân tách các mối quan tâm của một chuỗi được sử dụng cho mục đích hiển thị và một chuỗi riêng biệt như một mã tra cứu cho mã dễ đọc hơn là rất tốt.
Kate

1
Tôi sẽ cho @Mike Nakis ý tưởng về các đối tượng được tải sẵn của bạn mặc dù điều này có vẻ như là tốt nhất của cả hai thế giới.
Kate

1
Tôi sẽ đề nghị một biến thể về giải pháp đầu tiên của bạn. Có bảng của bạn chứa cột thứ 3 về cách nó sẽ được xử lý và bạn sử dụng trường đó để xác định mã nào sẽ thực thi. Không phải là một lĩnh vực hiển thị, và có thể được chia sẻ giữa nhiều loại trái cây.
Khởi động

1
Tùy chọn enum liên quan đến việc sao chép dữ liệu từ cơ sở dữ liệu có vẻ sai vì nó có thể không đồng bộ. Tôi thích điều này, thực sự. Nó giống như sổ sách kế toán kép. Nếu cả hai mặt của sổ cái không cân bằng, bạn sẽ biết có gì đó không ổn. Nó làm cho việc thay đổi mọi thứ có chủ ý hơn.
radarbob

1
Hmmm ... Nếu có mối quan hệ 1: 1 của ID với Chuỗi thì điều này là dư thừa và có cả hai là vô nghĩa. Chuỗi có thể đóng vai trò là khóa DB cũng như số nguyên. MyApplication.Grape.IDđang nói lắp, có thể nói như vậy. "Apple" không phải là "Red_Apple" nhiều hơn ID 3 cũng là 4. Vì vậy, tiềm năng đổi tên "Apple" thành "Red_Apple" không có ý nghĩa gì hơn khi tuyên bố rằng 3 là 4 (và thậm chí là 3). Điểm của một enum là trừu tượng hóa DNA số của nó. Vì vậy, có lẽ đã đến lúc phải thực sự tách rời các khóa DB quan hệ tùy ý, theo nghĩa đen không có ý nghĩa gì trong các mô hình kinh doanh của một người.
radarbob

Câu trả lời:


31

Tránh các chuỗi và hằng số ma thuật bằng mọi giá. Họ hoàn toàn không có câu hỏi, thậm chí họ không nên được coi là lựa chọn. Điều này dường như để lại cho bạn chỉ có một tùy chọn khả thi: định danh, nghĩa là enums. Tuy nhiên, cũng có một lựa chọn nữa, mà theo tôi là tốt nhất. Hãy gọi tùy chọn này là "Đối tượng tải sẵn". Với các đối tượng được tải sẵn, bạn có thể làm như sau:

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

Điều vừa xảy ra ở đây là tôi rõ ràng đã tải toàn bộ hàng Grapevào bộ nhớ, vì vậy tôi có ID của nó sẵn sàng để sử dụng trong so sánh. Nếu bạn tình cờ sử dụng Ánh xạ quan hệ đối tượng (ORM), nó thậm chí còn tốt hơn:

if( user.FavouriteFruit == MyApplication.Grape )

(Đó là lý do tại sao tôi gọi nó là "Đối tượng tải sẵn".)

Vì vậy, những gì tôi làm là trong khi khởi động, tôi tải tất cả các bảng "liệt kê" của mình (các bảng nhỏ như các ngày trong tuần, các tháng trong năm, giới tính, v.v.) vào lớp miền ứng dụng chính. Tôi tải chúng theo tên, vì rõ ràng, MyApplication.Grapephải nhận được hàng có tên là "Nho" và tôi khẳng định rằng mỗi và mọi thứ trong số chúng đều được tìm thấy. Nếu không, chúng tôi có một lỗi thời gian chạy được bảo đảm trong quá trình khởi động, đây là lỗi ít nhất trong tất cả các lỗi thời gian chạy.


17
Tôi không đồng ý với câu trả lời, nhưng tôi nghĩ rằng bắt buộc phải "Tránh các chuỗi và hằng số ma thuật bằng mọi giá" không đồng ý với phần còn lại của câu trả lời, điều này thực sự đòi hỏi bạn phải có ít nhất một nơi sử dụng các hằng số hoặc chuỗi ma thuật trong việc điền "các đối tượng tải sẵn" của bạn. Điều đó đáng chú ý, tôi nghĩ, bởi vì có nhiều cách để tránh hoàn toàn "chuỗi và hằng số ma thuật", mặc dù nó thường gây khó chịu hơn so với giá trị của nó ...
svidgen

2
@svidgen bạn có đồng ý rằng có một sự khác biệt cơ bản giữa việc phân tán ràng buộc theo tên ở khắp mọi nơi so với ràng buộc theo tên chỉ một lần, để tải nội dung của một bản ghi có cùng tên và chỉ làm như vậy khi khởi động, lỗi thời gian chạy gần như lành tính như lỗi biên dịch? Dù sao, những cách tránh ngay cả ràng buộc nhỏ nhất bằng tên luôn luôn thú vị, bất chấp sự giấu giếm mà bạn đề cập, vì vậy tôi sẽ tò mò muốn nghe những gì bạn có trong đầu.
Mike Nakis

Ồ, tôi hoàn toàn đồng ý. Và, với bản chất của OP, tôi chỉ đề xuất câu trả lời này có thể có lợi từ việc thay đổi "bằng mọi giá" thành "bất cứ khi nào có thể và khả thi" hoặc một cái gì đó tương tự. ... Nếu tôi có nhiều thời gian hơn, chỉ vì mục đích hoàn chỉnh, tôi sẽ viết ra một câu trả lời liên quan đến một số loại vô nghĩa siêu lập trình ... nhưng, đó không phải là điều mà OP (hoặc bất kỳ ai trong hầu hết các trường hợp) có thể cần . Nhưng, một giải pháp siêu hình sẽ phù hợp hơn với câu lệnh đầu tiên của bạn.
Svidgen

1
@ user469104 sự khác biệt là các id có thể thay đổi và ứng dụng vẫn sẽ tải tất cả các hàng một cách chính xác và thực hiện tất cả các so sánh chính xác. Ngoài ra, bạn có thể tự do cấu trúc lại mã và đổi tên các hàng theo bất kỳ cách nào bạn thích và nơi duy nhất bạn phải tìm kiếm công cụ để sửa là trong quá trình khởi động ứng dụng và nó có xu hướng rất rõ ràng: Grape = fetchRow( Fruit.class, NameColumn, "Grape" ); Và nếu bạn làm điều gì đó không chính xác, AssertionErrorsẽ cho bạn biết.
Mike Nakis

1
@grahamparks không nhiều hơn một enumchuỗi ma thuật. Vấn đề là sự tập trung của tất cả các tên ràng buộc chỉ trong một nơi , xác nhận tất cả trong khi khởi độngloại an toàn .
Mike Nakis

7

Kiểm tra đối với chuỗi là dễ đọc nhất, nhưng nó thực hiện nhiệm vụ kép: nó được sử dụng cả làm định danh và mô tả (có thể thay đổi vì những lý do không liên quan).

Tôi thường chia cả hai nhiệm vụ thành các lĩnh vực riêng biệt:

id  code    description
 1  grape   Grapes
 2  apple   Apple

Trường hợp mô tả có thể thay đổi (nhưng không phải là "Nho" thành "Chuối"), nhưng mã không được phép thay đổi.

Mặc dù điều này chủ yếu là do id của chúng tôi hầu như luôn được tạo tự động, và do đó không phù hợp. Nếu bạn có thể chọn id một cách tự do, có lẽ bạn có thể đảm bảo rằng chúng luôn chính xác và sử dụng chúng.

Ngoài ra, tần suất ai đó thực sự chỉnh sửa "Nho" thành "Nho"? Có lẽ không ai trong số này là cần thiết.


8
Tôi không nghĩ rằng dư thừa nhiều hơn là câu trả lời ...
Robbie Dee

4
Tôi cũng đã xem xét tùy chọn này và tôi đã thử nó, nhưng đây là điều đã xảy ra: đến một lúc nào đó, "apple" phải được phân biệt thành "green_apple" và "red_apple". Nhưng vì "apple" đã được sử dụng ở vô số vị trí trong mã, nên tôi không thể đổi tên nó, vì vậy tôi phải có "apple" và "green_apple". Và kết quả là, Sheldon trong tôi đã ngăn tôi ngủ vài đêm cho đến khi tôi vào đó và tái cấu trúc mọi thứ thành "Các vật thể được tải sẵn". (xem câu trả lời của tôi.)
Mike Nakis

1
Tôi chắc chắn thích các Đối tượng được tải sẵn của bạn, nhưng nếu "quả táo" của bạn khác biệt, bạn không cần phải vượt qua mọi thứ, dù bạn chọn phương pháp nào?
RemcoGerlich

Bạn thậm chí có thể có một bảng riêng cho tên mô tả, để hỗ trợ quốc tế hóa.
Erik Eidt

1
@MikeNakis và Tái cấu trúc về cơ bản là Tìm kiếm & Thay thế toàn bộ Codebase của bạn thay thế Fruit.Apple bằng Fruit.GreenApple. Nếu tôi sử dụng các giá trị Chuỗi mã hóa cứng, tôi sẽ thực hiện Tìm kiếm & Thay thế toàn bộ Codebase để thay thế "apple" bằng "green_apple", điều tương tự. - Tái cấu trúc chỉ cảm thấy tốt hơn, vì IDE đang thực hiện Thay thế.
Falco

4

Điều bạn đang mong đợi ở đây là logic lập trình có thể tự động thích ứng với việc thay đổi dữ liệu. Các tùy chọn tĩnh đơn giản như Enum không hoạt động ở đây vì bạn không thể thêm các enum bổ sung trong thời gian chạy.

Một vài mẫu tôi đã thấy:

  • Enums + mặc định để bảo vệ chống lại một mục cơ sở dữ liệu hoàn toàn mới làm hỏng ngày của chương trình của bạn.
  • Mã hóa các hành động sẽ được thực hiện (logic biz) trong chính cơ sở dữ liệu. Trong nhiều trường hợp, điều này rất có thể bởi vì nhiều logic được sử dụng lại. Việc thực hiện logic nên có trong chương trình.
  • Các thuộc tính / cột bổ sung trong cơ sở dữ liệu để đánh dấu giá trị hoàn toàn mới là 'bị bỏ qua' trong chương trình cho đến khi chương trình được triển khai đúng.
  • Thất bại các cơ chế nhanh xung quanh đường dẫn mã tải / tải lại các giá trị từ cơ sở dữ liệu. (Nếu hành động tương ứng không có trong chương trình VÀ nó không được đánh dấu để bỏ qua, đừng thực hiện làm mới).

Nói chung, tôi thích dữ liệu được hoàn thiện về mặt đề cập đến các hành động mà nó ngụ ý - mặc dù chính các hành động đó có thể được thực hiện ở nơi khác. Bất kỳ mã nào đang xác định hành động độc lập với dữ liệu vừa loại bỏ biểu diễn dữ liệu của bạn, điều có thể sẽ phân kỳ và dẫn đến lỗi.


4

Lưu trữ chúng ở cả hai nơi (trong bảng và trong ENUM) không phải là xấu. Lý do là như sau:

Bằng cách lưu trữ chúng trong bảng cơ sở dữ liệu, chúng ta có thể thực thi tính toàn vẹn tham chiếu trong cơ sở dữ liệu thông qua các khóa ngoại. Vì vậy, khi bạn liên kết một người hoặc bất kỳ thực thể nào với một loại trái cây, đó chỉ là một loại trái cây tồn tại trong bảng cơ sở dữ liệu.

Lưu trữ chúng dưới dạng ENUM cũng có ý nghĩa bởi vì chúng ta có thể viết mã mà không cần chuỗi ma thuật và nó làm cho mã dễ đọc hơn. Đúng, họ phải giữ đồng bộ, nhưng thực sự khó đến mức nào khi thêm một dòng vào ENUM và một câu lệnh chèn mới vào cơ sở dữ liệu.

Một điều, một khi ENUM được xác định, không thay đổi giá trị của nó. Ví dụ: nếu bạn có:

  • táo
  • Giống nho

KHÔNG đổi tên Nho thành Nho. Đơn giản chỉ cần thêm một ENUM mới.

  • táo
  • Giống nho
  • Nho

Nếu bạn cần di chuyển dữ liệu, sau đó áp dụng bản cập nhật để di chuyển tất cả Nho sang Nho.


Một bước nữa tôi đã làm việc trong các cửa hàng nơi các giá trị siêu dữ liệu có cờ xóa trong bảng để cho biết rằng chúng không nên được sử dụng (chúng đã bị phản đối hoặc tồn tại phiên bản mới hơn).
Robbie Dee

1

Bạn có quyền hỏi câu hỏi này, đây thực sự là một câu hỏi hay khi bạn đang cố gắng chống lại việc đánh giá các điều kiện không chính xác.

Điều đó nói rằng, việc đánh giá ( ifđiều kiện của bạn ) không nhất thiết phải là trọng tâm của cách bạn vượt qua nó. Thay vào đó, hãy chú ý đến cách bạn tuyên truyền những thay đổi sẽ gây ra vấn đề 'không đồng bộ'.

Cách tiếp cận chuỗi

Nếu bạn phải sử dụng chuỗi, tại sao không thể hiện chức năng thay đổi danh sách thông qua UI? Thiết kế hệ thống sao cho khi thay đổi Grapethành Grapes, ví dụ, bạn cập nhật tất cả các bản ghi hiện đang tham chiếu Grape.

Phương pháp tiếp cận ID

Tôi luôn thích tham chiếu ID, mặc dù có sự thỏa hiệp về khả năng đọc. The list may be added tomột lần nữa có thể là một cái gì đó bạn sẽ được thông báo nếu bạn tiếp xúc với một tính năng UI như vậy. Nếu bạn quan tâm đến việc sắp xếp lại các mục thay đổi id, hãy truyền lại thay đổi đó cho tất cả các bản ghi phụ thuộc. Tương tự như trên. Một tùy chọn khác (theo quy ước chuẩn hóa phù hợp, sẽ có cột enum / id của bạn - và tham chiếu FruitDetailbảng chi tiết hơn , có cột 'Đặt hàng' bạn có thể tra cứu).

Dù bằng cách nào, bạn có thể thấy tôi đang đề xuất kiểm soát việc sửa đổi hoặc cập nhật danh sách của mình. Cho dù bạn làm điều này thông qua việc sử dụng ORM hoặc một số quyền truy cập dữ liệu khác được xác định bởi các chi tiết cụ thể về công nghệ của bạn. Về cơ bản, những gì bạn đang làm là yêu cầu những người tránh xa DB vì những thay đổi như vậy - điều mà tôi nghĩ là ổn. Hầu hết các CRM chính sẽ đưa ra yêu cầu tương tự.


1
Trong cơ sở dữ liệu , id số đang được lưu trữ cho các bản ghi con, đặc biệt để tránh vấn đề đó. Câu hỏi này là về cách giao tiếp với ngôn ngữ lập trình.
Đồng hồ-Muse

1
@ Clockwork-Muse - để tránh vấn đề nào? Điều đó không có ý nghĩa.
JMᴇᴇ

Tôi sử dụng phương pháp ID khá nhiều, nhưng ID bị khóa và không thể thay đổi. Chuỗi đính kèm tất nhiên có thể bởi vì mọi người thường thích đổi tên những thứ "xe tải" trở thành "xe tải", v.v., trong khi bản thân thứ (được đại diện bởi ID) không thay đổi.
Brian Knoblauch

Nếu bạn đi theo cách tiếp cận ID, làm thế nào để bạn đối phó với cơ sở dữ liệu phát triển và sản xuất? Với ID tăng tự động, việc thêm các mục vào cả hai DB theo thứ tự khác nhau sẽ dẫn đến các ID khác nhau.
Người bảo vệ một

Không phải tăng tự động mặc dù? Không nên trong trường hợp này, đặc biệt nếu đó là giá trị nguyên của enum bên dưới mà chúng ta đang sử dụng.
JMᴇᴇ

0

Một vấn đề rất phổ biến. Mặc dù sao chép phía máy khách dữ liệu có vẻ vi phạm các nguyên tắc DRY , nhưng thực sự là do sự khác biệt về mô hình giữa các lớp.

Có enum (hoặc bất cứ điều gì) không phù hợp với cơ sở dữ liệu cũng không phải là hiếm. Bạn có thể đã đẩy ra một giá trị khác vào bảng siêu dữ liệu để hỗ trợ tính năng báo cáo mới chưa được sử dụng trong mã phía máy khách.

Nó đôi khi xảy ra theo cách khác quá. Một giá trị enum mới được thêm vào phía máy khách nhưng cập nhật DB không thể xảy ra cho đến khi DBA có thể áp dụng các thay đổi.


Vâng, bạn đã mô tả vấn đề. Giải pháp của bạn là gì?
Người bảo vệ một

1
@Protectorone Bạn giả sử có một giải pháp đạn bạc mà là một giả định sai lầm trong kinh nghiệm của tôi. Điều tốt nhất bạn có thể hy vọng là một số thực thể kinh doanh sở hữu miền vấn đề để ít nhất bạn có thể thấy phía nào bị trễ - phía máy khách hoặc phía cơ sở dữ liệu. Ngân hàng và tài chính thường rất hiệu quả trong vấn đề này với lĩnh vực bán lẻ ít đáng chú ý hơn ...
Robbie Dee

0

Giả sử rằng chúng ta đang nói về những gì thực chất là một tra cứu tĩnh thì tùy chọn thứ ba - enum - về cơ bản là sự lựa chọn lành mạnh duy nhất. Đó là những gì bạn sẽ làm nếu cơ sở dữ liệu không liên quan để nó có ý nghĩa.

Câu hỏi sau đó trở thành câu hỏi về cách giữ enums và bảng tĩnh / tra cứu trong cơ sở dữ liệu đồng bộ và thật không may, đó không phải là vấn đề tôi chưa có câu trả lời đầy đủ.

Theo lựa chọn, tôi thực hiện tất cả bảo trì lược đồ của mình bằng mã và do đó tôi có thể giữ mối quan hệ giữa bản dựng của ứng dụng và phiên bản lược đồ dự kiến ​​để đơn giản hóa việc tra cứu và enum đồng bộ nhưng đó là điều mà người ta phải nhớ làm Sẽ tốt hơn nếu nó được tự động hóa hơn (và cũng là một thử nghiệm tích hợp tự động để đảm bảo rằng các enum và tra cứu phù hợp sẽ giúp ích) nhưng đó không phải là điều tôi từng thực hiện.


1
Tôi không tin rằng đây chỉ là các tra cứu tĩnh, nếu không chúng có thể được lấy từ cơ sở dữ liệu và tiêu thụ nguyên trạng. Vấn đề theo tôi hiểu là khi logic kinh doanh được áp dụng tùy thuộc vào giá trị tra cứu được sử dụng. Nhưng, bên cạnh đó, có - enums thường được sử dụng cho mục đích này.
Robbie Dee

Ok, tôi cần một thuật ngữ tốt hơn "để tra cứu tĩnh" bối cảnh bạn mô tả là ý tôi :) Khóa là "tĩnh" - đây là những giá trị không thay đổi vấn đề là thêm giá trị mới và thay đổi "nhãn" ( nhưng không có ý định) cho các giá trị hiện có.
Murph
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.