데이터베이스가 제대로 설계되지 않은 관계형 데이터베이스 기반 응용 프로그램에서 더 나은 OO 코드를 만드는 방법


19

모든 페이지에 여러 테이블과 해당 테이블에 적용되는 필터가있는 유사한 페이지로 구성된 Java 웹 응용 프로그램을 작성하고 있습니다. 이 테이블의 데이터는 SQL 데이터베이스에서 가져옵니다.

myBatis를 ORM으로 사용하고 있는데 데이터베이스가 제대로 설계되지 않았고 mybatis가 더 데이터베이스 지향 도구이기 때문에 내 경우에는 최선의 선택이 아닐 수 있습니다.

데이터베이스의 열악한 디자인으로 인해 유사한 쿼리에 대해 다른 쿼리를 작성해야하기 때문에 중복 코드를 많이 작성하는 것으로 나타났습니다. 즉, 쿼리를 쉽게 매개 변수화 할 수 없습니다. 이것은 내 코드로 전파되고 간단한 루프로 테이블의 열에 행을 채우는 대신 다음과 같은 코드가 있습니다.

얻을 데이터 (P1, ..., PI);

B 데이터를 얻는다 (p1, ..., pi);

C 데이터를 얻는다 (p1, ..., pi);

D 데이터를 얻는다 (p1, ..., pi); ...

그리고 열이 다른 테이블이 있으면 곧 폭발합니다.

또한 "위젯"을 사용한다는 사실을 복잡하게 만듭니다. 즉, 페이지에서 객체를 html 요소에 매핑하는 것입니다. 따라서 Java 코드는 데이터베이스와 프론트 엔드 사이의 어댑터가되어 논리가 섞인 많은 배선 코드를 생성합니다.

올바른 솔루션이 ORM 맵퍼를 db에 더 균일 한 인터페이스를 제공하는 추가 계층으로 래핑합니까? 아니면 내가 작성하는 스파게티 코드를 처리하는 더 좋은 방법이 있습니까?

편집 : 데이터베이스에 대한 추가 정보

데이터베이스는 주로 전화 정보를 보유합니다. 열악한 디자인은 다음으로 구성됩니다.

도메인 지식과 관련이없는 기본 키로 인공 ID가있는 테이블.

고유 한 트리거, 검사 또는 외래 키가 없습니다.

레코드마다 다른 개념과 일치하는 일반 이름을 가진 필드

조건이 다른 다른 테이블과 교차하여 만 분류 할 수있는 레코드.

문자열로 저장된 숫자 또는 날짜 여야하는 열입니다.

요컨대, 지저분한 / 게으른 디자인.


7
데이터베이스 디자인 수정이 옵션입니까?
RMalke

1
데이터베이스가 어떻게 제대로 설계되지 않았는지 설명하십시오.
Tulains Córdova

@Renan Malke Stigliani 불행히도, 그것에 의존하는 레거시 소프트웨어가 있기는하지만 약간 다른 디자인으로 테이블을 미러링하여 채워서 코드를 단순화합니다. 그러나 나는 이것을 자랑스럽게 생각하지 않으며 차분하게 테이블을 무차별 적으로 복제하지 않을 것입니다.
DPM

1
이 책은 datbase 문제를 해결하고 레거시 코드의 작동을 유지하는 방법에 대한 훌륭한 아이디어를 제공합니다. amazon.com/…
HLGEM

4
당신이 나열한 대부분의 문제. . . 그렇지 않습니다. 자연 키가 아닌 대리 키를 사용하는 것은 실제로 오늘날 매우 표준적인 권장 사항입니다. 전혀 "나쁜 디자인"이 아닙니다. 제약 조건이 부족하고 부적절한 열 유형을 사용하는 것이 "빈약 한 디자인"이되는 한 더 좋은 예이지만 실제로 이러한 문제를 악용하려는 경우가 아니라면 실제로 응용 프로그램 코드에 전혀 영향을 미치지 않아야합니다.
ruakh

답변:


53

객체 지향은 이러한 유형의 시나리오가 발생하기 때문에 특히 유용하며 복잡성을 캡슐화 할 수있는 추상화를 합리적으로 디자인 할 수있는 도구를 제공합니다.

여기서 진짜 질문은, 그 복잡성을 어디에 캡슐화합니까?

잠시 후퇴하고 여기서 말하는 '복잡성'에 대해 이야기하겠습니다. 귀하의 문제는 (내가 이해하고, 틀린 경우 수정) 지속성 모델로, 데이터로 완료해야하는 작업에 효과적으로 사용할 수있는 모델이 아닙니다. 그것은 효과적이고 다른 작업에 사용할 수 있지만 수 있습니다 귀하의 작업.

따라서 수단에 적합한 모델이 아닌 데이터가있을 때 어떻게해야합니까?

옮기다. 당신이 할 수 있는 유일한 일 입니다. 그 번역은 위에서 언급 한 '복잡성'입니다. 이제 모델을 번역한다는 점을 받아들이 기 때문에 몇 가지 요소를 결정해야합니다.

양방향을 번역해야합니까? 다음과 같이 두 방향이 동일하게 번역됩니까?

(Tbl A, Tbl B)-> Obj X (읽기)

Obj X-> (Tbl A, Tbl B) (쓰기)

또는 삽입 / 업데이트 / 삭제 활동이 다른 유형의 객체를 나타내므로 Obj X와 같은 데이터를 읽지 만 데이터가 Obj Y에서 삽입 / 업데이트됩니까? 이 두 가지 방법 중 어느 것이 가고 싶 거나 업데이트 / 삽입 / 삭제가 불가능한 경우 번역을하려는 위치에 중요한 요소가 있습니다 .


어디에서 번역합니까?

이 답변에서 내가 한 첫 번째 진술로 돌아가십시오. OO를 사용하면 복잡성을 캡슐화 할 수 있으며 여기서 내가 참조하는 것은 복잡성을 캡슐화하여 누출되지 않고 모든 코드에 침투하지 않도록하려면 해당 복잡성을 캡슐화 해야 한다는 것입니다. 동시에, 완벽한 추상화 를 할 수 없다는 것을 인식하는 것이 중요 하므로 매우 효과적이고 유용한 것을 갖는 것보다 걱정하지 않아도됩니다.

다시 지금; 문제는 :이 복잡성을 어디에 두시겠습니까? 글쎄 당신은 선택이 있습니다.

저장 프로 시저를 사용하여 데이터베이스 에서 이를 수행 할 수 있습니다 . 이것은 종종 ORM과 잘 어울리지 않는 단점이 있지만 항상 그런 것은 아닙니다. 저장 프로시 저는 성능을 포함하여 몇 가지 이점을 제공합니다. 그러나 저장 프로 시저에는 많은 유지 관리가 필요할 수 있지만 특정 시나리오를 분석하고 유지 관리가 다른 선택보다 많거나 적은지 여부를 결정하는 것은 사용자의 몫입니다. 저는 개인적으로 저장 프로 시저에 매우 능숙하므로 이러한 가용 한 재능은 간접비를 줄입니다. 아는 바에 따라 의사 결정의 가치를 과소 평가 하지 마십시오 . 최적의 솔루션보다 솔루션을 더 잘 작성하고 유지 관리하는 방법을 알고 있기 때문에 차선의 솔루션이 올바른 솔루션보다 최적 일 수 있습니다.

다른 데이터베이스 내 옵션은 뷰입니다. 데이터베이스 서버에 따라 이들은 최적 또는 하위 최적화이거나 전혀 효과적이지 않을 수 있으며, 데이터베이스에서 사용 가능한 색인 옵션에 따라 쿼리 시간이 단축 될 수 있습니다. 데이터를 수정할 필요가없는 경우 (삽입 / 업데이트 / 삭제)보기가 더 나은 선택이됩니다.

데이터베이스를 지나서 리포지토리 패턴을 사용하는 이전 대기 상태가되었습니다. 이것은 매우 효과적 일 수있는 시간 테스트 된 접근 방식입니다. 단점은 보일러 플레이트를 포함하는 경향이 있지만 잘 정리 된 리포지토리는이 정도의 양을 피할 수 있으며, 이로 인해 불행히도 보일러 플레이트가 발생하더라도 리포지토리는 이해하기 쉽고 유지하기 쉬우 며 우수한 API를 제공하는 간단한 코드 인 경향이 있습니다. /추출. 또한 리포지토리는 데이터베이스 내 옵션으로 손실되는 단위 테스트 가능성에 적합 할 수 있습니다.

자동 매퍼와 같은 도구가있어 ORM을 사용하여 데이터베이스 모델을 orm에서 사용 가능한 모델로 변환 할 수 있지만,이 도구 중 일부는 마술처럼 동작을 유지 / 이해하기가 까다로울 수 있습니다. 그러나 최소한의 오버 헤드 코드를 만들어 잘 이해하면 유지 관리 오버 헤드가 줄어 듭니다.

다음으로 데이터베이스 에서 점점 더 나아가고 있습니다. 이는 번역되지 않은 퍼시스턴스 모델을 다루는 더 많은 양의 코드가 있다는 것을 의미하며, 이는 실제로 불쾌합니다. 이 시나리오에서는 번역 레이어를 UI에 배치하는 방법에 대해 설명합니다. 이것은 일반적으로 매우 나쁜 생각이며 시간이 지남에 따라 끔찍하게 붕괴됩니다.


이제 미친 이야기를 시작합시다 .

Object존재하는 경우에만 최종 모든 일이 모든 추상화가 아닙니다. 수년에 걸쳐 컴퓨터 과학을 연구 해 왔으며 그 이전에도 수학 연구를 통해 많은 추상화가 개발되었습니다. 우리가 창의력을 발휘하기 시작한다면 연구 된 알려진 추상화에 대해 이야기 해 보도록하겠습니다.

배우 모델이 있습니다.이것은 모든 작업을 다른 코드로 효과적으로 위임하는 다른 코드로 메시지를 보내는 것이므로 모든 코드에서 복잡성을 캡슐화하는 데 매우 효과적이기 때문에 흥미로운 접근 방식입니다. 이것은 배우에게 "I에게 Obj X를 보내야한다"라는 메시지를 보내고 위치 Y에서 응답을 기다리는 리셉터클이 있고 Obj X를 처리하는 한 작동 할 수있다. 심지어 지시하는 메시지를 보낼 수도있다. "Obj X와 계산 Y, Z가 필요해"기다릴 필요도 없습니다. 번역은 해당 메시지 전달의 다른 쪽에서 발생하며 결과를 읽을 필요가없는 경우 계속 진행할 수 있습니다. 이는 사용자의 목적에 따라 액터 모델을 약간 남용 할 수 있지만 모두 다릅니다.

또 다른 캡슐화 경계는 프로세스 경계입니다. 복잡성을 매우 효과적으로 분리하는 데 사용할 수 있습니다. SOAP, REST를 사용하거나 자신의 프로토콜을 원하는 경우 (권장되지 않음) 통신이 단순한 HTTP 인 웹 서비스로 변환 코드를 작성할 수 있습니다. STOMP는 완전히 새로운 프로토콜이 아닙니다. 또는 시스템 로컬 공개 메모리 파이프와 함께 일반 데몬 서비스를 사용하여 선택한 프로토콜을 사용하여 매우 빠르게 다시 통신하십시오. 이것은 실제로 몇 가지 좋은 이점이 있습니다.

  • 이전 및 최신 버전 지원을 위해 번역을 수행하는 여러 프로세스를 동시에 실행할 수 있으므로 변환 서비스를 업데이트하여 오브젝트 모델 V2를 공개 한 다음 나중에 별도로 소비 코드를 업데이트하여 새 오브젝트로 작업 할 수 있습니다 모델.
  • 성능을 위해 프로세스를 핵심에 고정시키는 것과 같은 흥미로운 작업을 수행 할 수 있으며,이 방법에서는 보안 권한으로 실행되는 프로세스 만 해당 데이터를 만지도록하여 보안 안전성을 확보 할 수 있습니다.
  • 번역 공간에서 코드를 작성하는 것은 번역 공간 외부에서 호출 될 수 없으므로 변환 공간에서 코드를 작성할 수 없기 때문에 오랫동안 추상화 유출을 최소화하는 고정 된 프로세스 경계에 대해 이야기 할 때 매우 강력한 경계를 갖게됩니다. 프로세스 범위를 공유하지 않으므로 계약에 따라 고정 된 사용 시나리오 세트가 보장됩니다.
  • 비동기 / 비 차단 업데이트 기능이 더 간단 해졌습니다.

단점은 성능 및 유지 관리에 영향을 미치는 통신 오버 헤드가 일반적으로 필요한 것보다 분명히 유지 관리입니다.


복잡성을 캡슐화하는 다양한 방법이 있으므로 시스템에서 복잡하고 이상한 곳으로 복잡성을 배치 할 수 있습니다. 고차 함수 형태 (전략 패턴이나 다양한 다른 이상한 형태의 객체 패턴을 사용하여 위조)를 사용하면 매우 흥미로운 일을 할 수 있습니다.

맞습니다. 모나드에 대해 이야기 해 봅시다.필요한 독립 번역을 수행하는 작은 특정 함수의 매우 독립적 인 방식으로이 변환 계층을 만들 수 있지만 이러한 변환 함수를 모두 보이지 않게 숨겨서 외부 코드에서는 거의 액세스 할 수 없습니다. 이는 외부 코드에 영향을주지 않으면 서 쉽게 변경할 수 있도록하는 의존성을 줄이는 이점이 있습니다. 그런 다음 멋진 OO 모델 유형 객체에서 작동하는 고차 함수 (익명 함수, 람다 함수, 전략 객체, 그러나 구조화해야 함)를 허용하는 클래스를 만듭니다. 그런 다음 해당 함수를 허용하는 기본 코드가 적절한 변환 방법을 사용하여 리터럴 실행을 수행하도록합니다.

이렇게하면 모든 변환 에서 경계의 반대편 에만 모든 변환 이 존재 하는 경계가 만들어집니다 . 그것은 그 측면에서만 사용되어 나머지 코드는 해당 경계의 진입 점이 아닌 다른 코드를 알 수 없습니다.

그래, 정말 미쳤어.하지만 누가 알 겠어? 당신은 그 미쳤을 수도 있습니다.


4
와우, 정말 포괄적 인 답변입니다. SE만이 나를 허락한다면 나는 이것을 두 번 이상 공표했다.
Marjan Venema

11
영화 버전은 언제 나옵니까?
yannis

3
@JimmyHoffa Bravo 선생님 !!! 나는이 답변을 북마크하고 딸이 나이가 들면 보여줄 것이다.
Tombatron

4

나의 제안:

다음과 같은 데이터베이스보기를 작성하십시오.

  1. 열에 의미있는 이름 부여
  2. "조건이 다른 다른 테이블과의 교차"를 수행하여 복잡성을 숨길 수 있습니다.
  3. 문자열로 저장된 숫자 나 날짜를 각각 숫자와 날짜로 변환합니다.
  4. 일부 기준에 따라없는 고유성을 만듭니다.

아이디어는 나쁜 디자인 위에 더 나은 디자인을 모방하는 외관을 만드는 것입니다.

그런 다음 ORM을 실제 테이블 대신 해당 파사드와 관련시킵니다.

그러나 삽입을 단순화하지는 않습니다.


데이터베이스 뷰를 사용하는 것은 고려하지 않은 어떤 이유로 추악을 가장 낮은 수준에서 추상화하는 가장 좋은 아이디어와 가장 우아한 행동 과정처럼 보입니다. 감사합니다.
DPM

3

기존 데이터베이스 스키마로 인해 더 구체적인 디자인의 스키마로 추상화 될 수있는 작업에 대한보다 구체적인 코드와 쿼리를 작성하는 방법을 알 수 있지만 좋은 객체 지향 코드를 작성하는 데 방해가되지는 않습니다.

  • SOLID 원칙을 기억하십시오 .
  • 단위 테스트를 쉽게 수행 할 수있는 코드를 작성하십시오 (종종 SOLID 원칙을 따릅니다).
  • 비즈니스 로직을 디스플레이 로직과 분리하십시오.
  • Apache Wicket 문서 및 예제를 읽으십시오. 이 프레임 워크는 생각보다 더 많은 상용구 코드를 절약 할 수 있으므로 효과적으로 사용하는 방법을 배우십시오.
  • 비즈니스 로직이 작업 할 수있는 깔끔한 인터페이스를 제공하는 별도의 계층에 데이터베이스를 처리해야하는 로직을 유지하십시오. 이런 식으로, 당신 (혹은 미래의 관리자)이 스키마를 향상시킬 기회가 있다면, 비즈니스 로직을 너무 많이 변경하지 않고도 그렇게 할 수 있습니다.

완벽하지 않은 데이터베이스 스키마로 작업하는 것을 발견하면 작업을 어렵게 만드는 모든 방법을 쉽게 파악할 수 있지만 어느 시점에서 이러한 불만 사항을 해결하고 최대한 활용해야합니다.

불완전한 스키마에도 불구하고 창의성을 사용하여 깨끗하고 재사용 가능하며 쉽게 유지 관리 할 수있는 코드를 작성할 수있는 기회로 생각하십시오.


1

더 나은 객체 지향 코드에 대한 초기 질문에 대답하기 위해 SQL 사용 객체를 사용하는 것이 좋습니다 . ORM은 본질적으로 객체 지향 원칙에 반합니다. 객체를 기반으로 작동하기 때문에 OOP의 객체는 목표를 달성하기위한 모든 리소스를 보유한 자급 자족입니다. 이 접근 방식으로 코드를 간단하게 만들 수 있다고 확신합니다.

문제 공간, 즉 귀하의 도메인에 대해 이야기하면 집계 루트 를 식별하려고합니다 . 도메인의 일관성 경계입니다. 항상 일관성을 유지해야하는 경계. 집계는 도메인 이벤트를 통해 통신합니다. 시스템이 충분히 크면 아마도 서브 시스템에서 시스템을 분할 해야 합니다 (SOA, 마이크로 서비스, 자체 포함 시스템 등).

또한 CQRS 사용을 고려하고 싶습니다. 쓰기 및 읽기 측면을 크게 단순화 할 수 있습니다. 이 주제에 대한 Udi Dahan의 기사 를 읽으십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.