비즈니스 로직에서 데이터베이스 값 참조


43

하드 코딩 및 모범 사례에 대한 또 다른 질문이라고 생각합니다. 데이터베이스에 저장된 값 목록이 있고 fruit라고 가정 해 봅시다.이 테이블은 SSRS 보고서와 같은 다른 목적으로 사용되므로 데이터베이스에 있어야합니다.

1 Apple 
2 Banana 
3 Grapes

나는 사용자에게 그것들을 제시하고, 하나를 선택하고, 그의 프로파일에 FavouriteFruit로 저장되고 ID는 데이터베이스에 그의 레코드에 저장됩니다.

비즈니스 규칙 / 도메인 로직과 관련하여 특정 값에 로직을 할당하기위한 권장 사항은 무엇입니까? 사용자가 포도를 선택한 경우 추가 작업을 수행하고 싶습니다. 포도 값을 참조하는 가장 좋은 방법은 무엇입니까?

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

또는 다른 것?

물론 FavouriteFruit는 응용 프로그램 전체에서 사용되므로 목록을 추가하거나 편집 할 수 있습니다.

누군가 'Grapes'의 이름을 'Grape'로 바꾸겠다고 결정할 수 있으며 하드 코딩 된 문자열 옵션을 깨뜨릴 수 있습니다.

표시된대로 하드 코딩 된 ID는 명확하지 않지만 설명을 추가하면 해당 항목을 신속하게 식별 할 수 있습니다.

enum 옵션은 데이터베이스에서 데이터를 복제하는 것과 관련이 있으며 동기화되지 않을 수 있습니다.

어쨌든 의견이나 제안에 미리 감사드립니다.


1
모두에게 감사합니다. 제안과 일반적인 조언이 정말 도움이됩니다. @RemcoGerlich는 표시 목적으로 사용되는 문자열의 문제를 분리하고 더 읽기 쉬운 코드의 조회 코드로 별도의 문자열을 분리하는 아이디어가 매우 좋습니다.
Kate

1
@Mike Nakis에 미리로드 된 객체 아이디어를 제공 할 것입니다. 두 세계에서 가장 좋은 것처럼 보입니다.
Kate

1
첫 번째 솔루션의 변형을 제안합니다. 테이블에 처리 방법에 대한 세 번째 열이 포함되도록하고 해당 필드를 사용하여 실행할 코드를 결정하십시오. 표시 필드가 아니며 여러 과일간에 공유 할 수 있습니다.
킥 스타트

1
enum 옵션은 데이터베이스에서 데이터를 복제하는 것과 관련이 있으며 동기화되지 않을 수 있습니다. 나는 이것을 실제로 좋아한다. 이중 입장 부기와 같습니다. 원장의 양쪽이 균형을 이루지 못하면 무언가 잘못되었음을 알 수 있습니다. 변경 사항을보다 신중하게 만듭니다.
radarbob

1
흠 ... ID와 문자열의 1 : 1 관계가있는 경우 이것은 중복이며 무의미합니다. 문자열은 정수뿐만 아니라 DB 키 역할도 할 수 있습니다. MyApplication.Grape.ID말하자면 말더듬입니다. "Apple"은 ID가 3보다 큰 "Red_Apple"이 아닙니다. 또한 "Apple"의 이름을 "Red_Apple"로 바꾸는 가능성은 3이 4 (또는 3)임을 선언하는 것보다 더 의미가 없습니다. 열거 형의 요점은 숫자 DNA를 추상화하는 것입니다. 따라서 비즈니스 모델에 아무런 의미가없는 임의의 관계형 DB 키 를 실제로 분리 해야 할 때 입니다.
radarbob

답변:


31

모든 비용으로 문자열과 마법 상수를 피하십시오. 그들은 완전히 의문의 여지가 없으며 옵션으로 간주되어서는 안됩니다. 이것은 식별자, 즉 열거 형이라는 하나의 실행 가능한 옵션 만 남은 것으로 보입니다. 그러나 한 가지 더 옵션이 있는데, 제 생각에는 최고입니다. 이 옵션을 "사전로드 된 개체"라고합니다. 사전로드 된 오브젝트를 사용하여 다음을 수행 할 수 있습니다.

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

방금 일어난 일은 Grape메모리에 전체 행을로드 했으므로 비교에 사용할 수있는 ID가 있다는 것입니다. ORM (Object-Relational Mapping)을 사용하는 경우 훨씬 더 좋아 보입니다.

if( user.FavouriteFruit == MyApplication.Grape )

이것이 바로 "사전로드 된 객체"라고하는 이유입니다.

따라서 시작하는 동안 모든 "열거"테이블 (요일, 월, 성별 등의 작은 테이블)을 기본 응용 프로그램 도메인 클래스에로드합니다. 분명히 MyApplication.Grape"Grape"라는 행을 받아야 하기 때문에 이름으로로드하고, 각각이 모두 발견된다고 주장합니다. 그렇지 않은 경우 시작 중에 보장 된 런타임 오류가 있으며 이는 모든 런타임 오류 중에서 가장 악의적 인 것입니다.


17
나는 대답에 동의하지 않지만 "모든 비용으로 문자열과 마법 상수를 피하십시오"라는 명령의 나머지 부분이 나머지 답변과 동의하지 않는다고 생각 합니다. 실제로 마술 상수 나 문자열이 사용되는 적어도 하나있어야 합니다. "사전로드 된 개체"를 채울 때 "문자열과 마법 상수"를 완전히 피할 수 있는 방법이 있기 때문에 주목할 만하다. 일반적으로 가치보다 더
난해하다

2
@ svidgen은 장소에 따른 이름 별 바인딩과 이름에 대한 바인딩 별 한 번만 동일한 이름을 가진 레코드의 내용을로드하는 것과 그렇게하는 것 사이에 근본적인 차이가 있다는 것에 동의하지 않습니다. 시작할 때 런타임 오류가 컴파일 오류만큼이나 중요한 곳은 무엇입니까? 어쨌든, 당신이 언급 한 난독 화에도 불구하고 이름으로 가장 작은 바인딩조차 피하는 방법은 항상 흥미 롭습니다. 그래서 나는 당신이 생각하는 것을 듣고 궁금합니다.
Mike Nakis

아, 전적으로 동의합니다. 또한 OP의 특성상이 답변이 "모든 비용으로" "가능한 경우 가능"또는 이와 유사한 것으로 변경하면 도움이 될 수 있다고 제안합니다. ... 완벽 성을 기하기 위해 더 많은 시간을 보낸다면 일종의 메타 프로그래밍 넌센스를 다루는 대답을 쓸 것입니다 ...하지만 OP (또는 대부분의 경우 누구나)가 아마 필요한 것은 아닙니다. . 그러나 메타 프로 게이밍 솔루션은 첫 번째 문장과 더 잘 어울립니다.
svidgen

1
@ user469104의 차이점은 ID가 변경 될 수 있으며 응용 프로그램은 여전히 ​​모든 행을 올바르게로드하고 모든 비교를 올바르게 수행한다는 것입니다. 또한, 당신은 당신이 좋아하는 어떤 방법으로 리팩토링 코드와 이름 바꾸기 행 무료이며, 당신이 수정에 물건을 찾고 갈 수있는 유일한 장소는 응용 프로그램의 시작이며, 그것은 매우 분명 경향 : Grape = fetchRow( Fruit.class, NameColumn, "Grape" ); 그리고 경우 잘못한 일을하면 AssertionError의지가 알려줄 것입니다.
Mike Nakis

1
@grahamparks는 더 이상 enum마법의 끈이 아니 었습니다 . 요점은 이름별로 모든 바인딩 별 집중 , 시작시 모든 유효성 검사유형 안전성 입니다.
Mike Nakis

7

문자열을 검사하는 것이 가장 읽기 쉽지만 이중 의무가 있습니다. 식별자와 설명으로 사용됩니다 (관련되지 않은 이유로 변경 될 수 있음).

나는 보통 두 업무를 별도의 분야로 나누었다.

id  code    description
 1  grape   Grapes
 2  apple   Apple

설명이 변경 될 수 있지만 ( "Grapes"가 "Banana"로 변경되지는 않음) 코드는 절대 변경 될 수 없습니다.

이것은 대부분 ID가 거의 항상 자동 생성되므로 적합하지 않기 때문입니다. ID를 자유롭게 선택할 수 있다면 항상 올바른지 확인하고 사용할 수 있습니다.

또한, 얼마나 자주 사람 않습니다 정말 "포도"를 편집 "포도"? 이 중 어느 것도 필요하지 않을 수 있습니다.


8
나는 ... 아직 더 중복 대답은 생각하지 않는다
로비 디

4
이 옵션도 고려해 보았지만 시도했지만 결국 "apple"이 "green_apple"과 "red_apple"로 구분되어야했습니다. 그러나 "apple"은 이미 코드의 무수한 장소에서 사용 되었기 때문에 이름을 바꿀 수 없으므로 "apple"과 "green_apple"이 있어야했습니다. 결과적으로, 나 안에있는 셀던은 내가 거기에 들어가서 모든 것을 "사전로드 된 물체"로 리팩토링 할 때까지 며칠 밤 잠을 자지 못하게했다. (내 답변을 참조하십시오.)
Mike Nakis

1
나는 사전로드 된 객체를 좋아하지만, "애플"이 차별화되어 있다면, 어떤 방법을 선택하든 상관없이 모든 것을 다룰 필요는 없습니까?
RemcoGerlich

국제화를 지원하기 위해 설명 이름에 대한 별도의 테이블이있을 수도 있습니다.
Erik Eidt

1
@MikeNakis와 리팩토링은 본질적으로 Fruit.Apple을 Fruit.GreenApple로 대체하여 전체 코드베이스를 검색 및 대체합니다. Hardcoded String 값을 사용하면 전체 코드베이스에 대해 검색 및 바꾸기를 수행하여 "apple"을 "green_apple"로 대체합니다. -IDE가 교체를 수행하고 있기 때문에 리팩토링이 더 좋아집니다.
팔코

4

여기서 기대하는 것은 변화하는 데이터에 자동으로 적응할 수있는 프로그래밍 논리입니다. 런타임에 여분의 열거 형을 추가 할 수 없기 때문에 Enum과 같은 간단한 정적 옵션은 여기에서 작동하지 않습니다.

내가 본 몇 가지 패턴 :

  • 프로그램의 날을 망치는 새로운 데이터베이스 항목으로부터 보호하기 위해 열거 + 기본값.
  • 데이터베이스 자체에서 수행 할 조치 인코딩 (비즈 로직). 많은 경우에 많은 논리가 재사용되기 때문에 이것이 가능합니다. 논리 구현은 프로그램에 있어야합니다.
  • 프로그램이 올바르게 배포 될 때까지 프로그램에서 새로운 값을 '무시할'것으로 표시하기위한 데이터베이스의 추가 속성 / 열.
  • 데이터베이스에서 값을로드 / 재로드하는 코드 경로 주위의 빠른 메커니즘에 실패합니다. (해당 조치가 프로그램에없고 무시 표시되지 않은 경우 새로 고치지 마십시오).

일반적으로 나는 행동 자체가 다른 곳에서 구현 될지라도 데이터가 의미하는 행동을 언급하는 데있어서 데이터가 완전한 것을 좋아합니다. 데이터와 무관하게 행동을 결정하는 모든 코드는 데이터 표현을 손상시켜 버그를 유발할 가능성이 높습니다.


4

두 곳 (테이블과 ENUM)에 저장하는 것은 그리 나쁘지 않습니다. 추론은 다음과 같습니다.

데이터베이스 테이블에 저장함으로써 외래 키를 통해 데이터베이스에서 참조 무결성을 강화할 수 있습니다. 따라서 사람이나 다른 개체를 과일에 연결할 때 데이터베이스 테이블에 존재하는 과일 일뿐입니다.

그것들을 ENUM으로 저장하는 것은 우리가 마술 문자열없이 코드를 작성할 수 있고 코드를 더 읽기 쉽기 때문에 의미가 있습니다. 그렇습니다. 동기화 상태를 유지해야하지만 ENUM에 행을 추가하고 데이터베이스에 새 삽입 문을 추가하는 것은 실제로 어려운 일입니다.

ENUM을 정의한 후에는 값을 변경하지 마십시오. 예를 들어 다음과 같은 경우

  • 사과
  • 포도

포도의 이름을 포도로 바꾸지 마십시오. 새로운 ENUM을 추가하기 만하면됩니다.

  • 사과
  • 포도
  • 포도

데이터를 마이그레이션해야하는 경우 업데이트를 적용하여 모든 포도를 포도로 이동하십시오.


추가 단계로 메타 데이터 값에 테이블에 삭제 플래그가있어 상점에서 사용하지 않아야 함을 나타내는 상점에서 일했습니다 (더 이상 사용되지 않거나 최신 버전이 있음).
로비 디

1

이 질문을하는 것이 맞습니다. 부정확 한 조건의 평가에 대해 방어하려고 할 때 실제로 좋은 질문입니다.

즉, 평가 ( if조건)가 반드시 그 문제를 해결하는 방법의 초점 일 필요는 없습니다. 대신 '비 동기화'문제를 야기하는 변경 사항을 전파하는 방식에주의하십시오.

문자열 접근

문자열을 사용해야하는 경우 UI를 통해 목록을 변경하는 기능을 공개하지 않겠습니까? 예를 들어, 현재 참조하고있는 모든 레코드를 다음과 같이 변경 Grape하도록 시스템을 설계하십시오 .GrapesGrape

ID 접근

나는 약간의 가독성에도 불구하고 항상 ID를 참조하는 것을 선호합니다. The list may be added toUI 기능을 노출 한 경우 다시 알림을받을 수 있습니다. ID를 변경하는 항목의 순서를 다시 지정하려면 해당 변경 사항을 모든 종속 레코드에 다시 전파하십시오. 위와 비슷합니다. 또 다른 옵션 (적절한 정규화 규칙에 따라 열거 형 / ID 열 FruitDetail을 갖는 것이 좋으며 조회 할 수있는 '주문'열이 있는보다 자세한 테이블을 참조하는 것입니다 ).

어느 쪽이든, 목록의 수정 또는 업데이트를 제어 할 것을 제안하고 있음을 알 수 있습니다. ORM 사용 또는 다른 데이터 액세스를 통해이 작업을 수행하는지 여부는 기술의 특성에 따라 결정됩니다. 본질적으로 당신이하고있는 일은 DB에서 멀리 떨어져있는 사람들에게 그러한 변화를 요구하는 것입니다. 대부분의 주요 CRM은 동일한 요구 사항을 수행합니다.


1
데이터베이스 에서 숫자 ID는 특히 해당 문제를 피하기 위해 하위 레코드에 저장됩니다. 이 질문은 프로그래밍 언어와 인터페이스하는 방법에 관한 것입니다.
Clockwork-Muse

1
@ Clockwork-Muse-어떤 문제를 피하기 위해? 말이되지 않습니다.
JᴀʏMᴇᴇ

ID 접근 방식을 꽤 많이 사용하지만 ID가 잠겨서 변경할 수 없습니다. 물론 첨부 된 문자열은 사람들이 종종 "로리"의 이름을 바꾸는 것을 좋아하기 때문에 "트럭"등이되지만 그 자체 (ID로 표시됨)는 바뀌지 않습니다.
Brian Knoblauch

ID 접근 방식을 사용하는 경우 개발 및 프로덕션 데이터베이스를 어떻게 처리합니까? 자동 증분 ID를 사용하면 두 DB에 다른 순서로 항목을 추가하면 다른 ID가 생성됩니다.
수호자 1

그래도 자동 증분 일 필요는 없습니까? 이 경우, 특히 우리가 사용하는 기본 열거 형 정수 값인 경우에는 안됩니다.
JᴀʏMᴇᴇ

0

매우 일반적인 문제입니다. 데이터 클라이언트 쪽을 복제하면 DRY 원칙 을 위반하는 것처럼 보일 수 있지만 실제로는 계층 간 패러다임의 차이로 인한 것입니다.

데이터베이스에서 열거 형 (또는 무엇이든)을 벗어나는 것이 실제로 드문 일이 아닙니다. 아직 클라이언트 측 코드에서 사용되지 않는 새로운 보고서 기능을 지원하기 위해 메타 데이터 테이블에 다른 값을 푸시했을 수 있습니다.

때로는 다른 방식으로도 발생합니다. 새로운 enum 값이 클라이언트 측에 추가되지만 DBA가 변경 사항을 적용 할 때까지 DB 업데이트를 수행 할 수 없습니다.


예, 문제를 설명하셨습니다. 당신의 해결책은 무엇입니까?
수호자 1

1
@Protectorone 내 경험에 잘못된 가정 인은 총알 솔루션 있다고 가정합니다. 당신이 기대할 수있는 최선의 방법은 일부 비즈니스 엔터티가 문제 도메인을 소유하고 있기 때문에 적어도 클라이언트 측 또는 데이터베이스 측이 지연되는 것을 볼 수 있다는 것입니다. 소매 부문이 눈에 띄게 덜해서 은행과 금융은 일반적으로 매우 효율적입니다 ...
Robbie Dee

0

우리가 본질적으로 정적 조회에 대해 이야기하고 있다고 가정하면 세 번째 옵션-열거 형은 기본적으로 유일한 제정신 선택입니다. 데이터베이스가 관련되지 않은 경우 수행하는 작업이므로 의미가 있습니다.

그런 다음 데이터베이스의 열거 형과 정적 / 조회 테이블을 동기화 상태로 유지하는 방법에 대한 질문이되지만 불행히도 아직 완전한 대답이없는 문제는 아닙니다.

선택적으로 모든 스키마 유지 관리를 코드로 수행하므로 응용 프로그램 빌드와 예상 스키마 버전 간의 관계를 유지할 수 있으므로 조회와 열거를 동기화하는 것이 간단하지만 기억해야 할 것은 간단합니다. 하다. 자동화 된 경우 (그리고 열거 형과 조회가 일치하는지 확인하기위한 자동화 된 통합 테스트) 더 좋을 것입니다. 그러나 그것은 내가 구현 한 것이 아닙니다.


1
나는 이것이 정적 조회라고 생각하지 않습니다. 그렇지 않으면 데이터베이스에서 가져 와서 그대로 소비 할 수 있습니다. 내가 이해하는 문제는 사용 된 조회 값에 따라 비즈니스 로직을 적용해야 할 때입니다. 그러나 그 외에도, 예-열거 형은 일반적 으로이 목적으로 사용됩니다.
Robbie Dee

좋아, 나는 "정적 조회"에 대해 더 나은 용어가 필요하다. 당신이 묘사하는 맥락은 내가 의미하는 바이다. 기존 값에 대한 의도는 아닙니다.
Murph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.