도메인 / 지속성 모델 격리가 일반적으로 어색합니까?


13

DDD (Domain-Driven Design) 개념에 대해 살펴보면서 특히 도메인 격리 및 지속성 모델과 관련하여 이상한 원칙을 발견했습니다. 기본 이해는 다음과 같습니다.

  1. 기능 세트를 제공하는 응용 프로그램 계층의 서비스는 기능을 수행하는 데 필요한 리포지토리에서 도메인 개체를 요청합니다.
  2. 이 저장소의 구체적인 구현은 구현 된 스토리지에서 데이터를 가져옵니다.
  3. 이 서비스는 비즈니스 논리를 캡슐화하는 도메인 개체에 상태를 수정하는 특정 작업을 수행하도록 지시합니다.
  4. 서비스는 리포지토리에 수정 된 도메인 개체를 유지하도록 지시합니다.
  5. 저장소는 도메인 오브젝트를 스토리지의 해당 표시에 다시 맵핑해야합니다.

흐름 그림

위의 가정을 감안할 때 다음은 어색한 것 같습니다.

광고 2 ::

도메인 모델은 요청한 기능에 필요하지 않더라도 전체 도메인 객체 (모든 필드 및 참조 포함)를로드하는 것 같습니다. 다른 도메인 객체를 참조하는 경우 해당 도메인 객체와 해당 객체를 차례로 참조하는 등의 작업을 수행하지 않는 한 완전히로드하지 못할 수도 있습니다. 게으른 로딩을 염두에 두어야합니다. 그러나 먼저 리포지토리를 담당해야하는 도메인 개체를 쿼리하기 시작합니다.

이 문제가 발생하면 도메인 개체를로드하는 "올바른"방법은 각 사용 사례에 대해 전용로드 기능이있는 것 같습니다. 그런 다음 이러한 전용 기능은 설계된 사용 사례에 필요한 데이터 만로드합니다. 어색함이 작용하는 위치는 다음과 같습니다. 먼저 리포지토리의 각 구현에 대해 상당한 양의 로딩 기능을 유지해야하며 도메인 객체는 null해당 필드를 수행하는 불완전한 상태 가됩니다. 값이로드되지 않은 경우 값을 요청한 기능에 필요하지 않기 때문에 후자는 기술적 으로 문제가되지 않습니다. 여전히 어색하고 잠재적 인 위험이 있습니다.

광고 3 .:

리포지토리에 대한 개념이없는 경우 도메인 개체가 구성시 고유성을 제한하는 방법은 무엇입니까? 예를 들어, User고유 한 사회 보장 번호 (주어진) 를 사용하여 새로 작성하려는 경우, 데이터베이스에 정의 된 고유성 제한 조건이있는 경우에만 저장소에 오브젝트를 저장하도록 요청할 때 가장 빠른 충돌이 발생합니다. 그렇지 않으면, 새로운 User사회 보장국을 만들기 전에 주어진 사회 보장국에서로 를 찾아서 존재하는 경우 오류를보고 할 수 있습니다. 그러나 제약 조건 검사는 해당 서비스가 속한 도메인 개체가 아닌 서비스에 존재합니다. 방금 도메인 객체가 유효성 검사를 위해 (주입 된) 리포지토리를 사용할 수 있다는 것을 깨달았습니다.

광고 5 .:

도메인 개체가 언더 레이 데이터를 직접 수정하는 것과 비교하여 작업 집약적 인 프로세스로 저장소 개체에 도메인 개체의 매핑을 인식합니다. 물론, 구체적인 스토리지 구현을 도메인 코드에서 분리하는 것은 필수 전제 조건입니다. 그러나 실제로 그렇게 높은 비용이 듭니까?

분명히 ORM 도구를 사용하여 매핑을 수행 할 수있는 옵션이 있습니다. 그러나 종종 ORM의 제한 사항에 따라 도메인 모델을 설계하거나 도메인 객체에서 예를 들어 ORM 주석을 사용하여 도메인에서 인프라 계층으로의 종속성을 도입해야합니다. 또한 ORM에 상당한 계산 오버 헤드가 발생한다는 것을 읽었습니다.

ORM과 유사한 개념이 거의없는 NoSQL 데이터베이스의 경우 도메인 모델에서 어떤 속성이 변경되었는지 추적하는 방법은 save()무엇입니까?

편집 : 또한 리포지토리가 도메인 개체의 상태 (예 : 각 필드의 값)에 액세스하려면 도메인 개체가 캡슐화를 방해하는 내부 상태를 나타내야합니다.

일반적으로 :

  • 트랜잭션 로직은 어디로 갈까요? 이것은 확실히 지속성에 특정한 것입니다. 일부 스토리지 인프라는 인 메모리 모의 리포지토리와 같은 트랜잭션도 전혀 지원하지 않을 수 있습니다.
  • 여러 개체를 수정하는 대량 작업의 경우 개체의 캡슐화 된 유효성 검사 논리를 거치려면 각 개체를 개별적으로로드, 수정 및 저장해야합니까? 이는 데이터베이스에서 단일 쿼리를 직접 실행하는 것과 반대입니다.

이 주제에 대한 설명을 부탁드립니다. 내 가정이 맞습니까? 그렇지 않은 경우 이러한 문제를 해결하는 올바른 방법은 무엇입니까?


1
좋은 지적과 질문, 나는 또한 그들에 관심이 있습니다. 한 가지 참고 사항-집계를 올바르게 모델링하는 경우 주어진 존재 시간에 집계 인스턴스가 유효한 상태에 있어야합니다. 즉 집계의 주요 지점이며 집계를 컴포지션 컨테이너로 사용하지 않아야합니다. 즉, DB 데이터에서 집계 형식을 복원하려면 리포지토리 자체가 일반적으로 특정 생성자와 돌연변이 작업 세트를 사용해야하며 ORM이 이러한 작업을 수행하는 방법을 자동으로 알 수있는 방법을 알 수 없습니다. .
Dusan

2
더 실망스러운 것은 당신과 같은 질문이 자주 제기된다는 것입니다. 그러나 제 지식으로는 책에 의해 집계 된 집합과 리포지토리의 구현에 대한 ZERO 예제가 있습니다
Dusan

답변:


5

기본 이해가 정확하고 스케치 한 아키텍처가 양호하고 잘 작동합니다.

더 데이터베이스 중심의 활성 레코드 스타일 프로그래밍에서 오는 것처럼 보이는 줄 사이를 읽는가? 작동하는 구현을 얻으려면 다음을 수행해야한다고 말하고 싶습니다.

1 : 도메인 개체는 전체 개체 그래프를 포함하지 않아도됩니다. 예를 들어 내가 가질 수있는 것은 :

public class Customer
{
    public string AddressId {get;set;}
    public string Name {get;set;}
}

public class Address
{
    public string Id {get;set;}
    public string HouseNumber {get;set;
}

"고객 이름은 집 이름과 같은 문자로만 시작할 수 있습니다"와 같은 논리가있는 경우 주소와 고객은 동일한 집계의 일부일 필요가 있습니다. 지연 로딩 및 'Lite'버전의 객체를 피할 수 있습니다.

2 : 고유성 제한 조건은 일반적으로 도메인 오브젝트가 아닌 저장소의 범위입니다. 도메인 개체에 리포지토리를 주입하지 마십시오. 즉, 활성 레코드로 다시 이동하므로 서비스가 저장하려고 할 때 오류가 발생합니다.

비즈니스 규칙은 "동일한 SocialSecurityNumber를 가진 두 개의 사용자 인스턴스가 동시에 존재할 수 없습니다"

동일한 저장소에 존재할 수 없습니다.

3 : 개별 속성 업데이트 방법보다는 리포지토리를 작성하는 것이 어렵지 않습니다. 실제로, 당신은 어느 쪽이든 거의 같은 코드를 가지고 있음을 알게 될 것입니다. 어떤 클래스에 넣었습니까?

요즘의 ORM은 쉽고 코드에 대한 추가적인 제약이 없습니다. 개인적으로 저는 SQL을 직접 작성하는 것을 선호합니다. 어렵지 않습니다. ORM 기능 관련 문제가 발생하지 않으며 필요한 경우 최적화 할 수 있습니다.

저장할 때 변경된 속성을 추적 할 필요가 없습니다. 도메인 개체를 작게 유지하고 단순히 이전 버전을 덮어 씁니다.

일반적인 질문들

  1. 트랜잭션 로직은 저장소에 들어갑니다. 그러나 당신은 그것을 많이 가지고 있으면 안됩니다. 집계의 하위 오브젝트를 넣을 하위 테이블이 있지만 일부는 SaveMyObject 저장소 메소드 내에 완전히 캡슐화되어 있어야합니다.

  2. 대량 업데이트. 예, 각 객체를 개별적으로 변경 한 다음 SaveMyObjects (List objects) 메소드를 리포지토리에 추가하여 대량 업데이트를 수행해야합니다.

    도메인 개체 또는 도메인 서비스에 논리가 포함되기를 원합니다. 데이터베이스가 아닙니다 . 즉, "고객 세트 이름 = x 여기서 y 업데이트"를 수행 할 수 없습니다. 고객 개체를 아는 모든 사람이나 이름을 변경할 때 CustomerUpdateService가 다른 이상한 일을하기 때문입니다.


좋은 대답입니다. 당신은 절대적으로 맞습니다, 나는 적극적인 레코드 스타일 코딩에 익숙합니다. 그래서 저장소 패턴이 처음에는 이상하게 보입니다. 그러나 "lean"도메인 객체 () AddressId대신 AddressOO 원칙과 모순되지 않습니까?
Double M

아니, 당신은 여전히 ​​고객의 자식이 아닌 Address 객체를 가지고 있습니다
Ewan

변경 추적없이 광고 개체 매핑 softwareengineering.stackexchange.com/questions/380274/…
Double M

2

짧은 답변 : 귀하의 이해가 정확하고 귀하가 가지고있는 질문은 솔루션이 간단하지 않거나 보편적으로 받아 들여지지 않는 유효한 문제를 지적합니다.

포인트 2 : (전체 객체 그래프 로딩)

나는 ORM이 항상 좋은 해결책은 아니라는 것을 처음 지적한 것은 아니다. 주된 문제는 ORM이 실제 사용 사례에 대해 전혀 모른다는 것이므로로드 할 항목이나 최적화 방법을 모릅니다. 이것은 문제입니다.

당신이 말했듯이, 확실한 해결책은 각 사용 사례에 대한 지속성 방법을 갖는 것입니다. 그러나 여전히 ORM을 사용하는 경우 ORM을 사용하면 모든 것이 데이터 객체로 압축됩니다. 실제로 객체 지향적이지 않은 것 외에도 일부 사용 사례에 가장 적합한 디자인은 아닙니다.

일부 레코드를 대량 업데이트하려는 경우 어떻게합니까? 모든 레코드에 대해 객체 표현이 필요한 이유는 무엇입니까? 기타.

따라서 그 해결책은 적합하지 않은 사용 사례에 ORM을 사용하지 않는 것입니다. 유스 케이스를 "자연스럽게"구현하십시오. 때로는 데이터 자체 (데이터 오브젝트)의 추가 "추상"이나 "테이블"(리포지토리)에 대한 추상화가 필요하지 않습니다.

반쯤 채워진 데이터 개체를 갖거나 개체 참조를 "id"로 바꾸는 것은 최선의 해결 방법이지만, 훌륭한 디자인은 아닙니다.

포인트 3 : (제약 확인)

지속성이 추상화되지 않으면 각 사용 사례는 원하는 제약 조건을 쉽게 확인할 수 있습니다. 객체가 "저장소"를 모른다는 요구 사항은 완전히 인공적인 것이며 기술의 문제가 아닙니다.

포인트 5 : (ORM)

물론, 구체적인 스토리지 구현을 도메인 코드에서 분리하는 것은 필수 전제 조건입니다. 그러나 실제로 그렇게 높은 비용이 듭니까?

아닙니다. 지속성을 갖는 다른 많은 방법이 있습니다. 문제는 ORM이 항상 (관계형 데이터베이스의 경우) 항상 "사용할"솔루션으로 간주된다는 것입니다. 프로젝트에서 일부 유스 케이스에 사용하지 말 것을 제안하는 것은 쓸모가 없으며 때로는 ORM 자체에 따라 캐시와 늦게 실행되는 도구가 때때로 사용되기 때문에 불가능합니다.

일반적인 질문 1 .: (거래)

단일 솔루션이 있다고 생각하지 않습니다. 디자인이 객체 지향적 인 경우 각 사용 사례에 대해 "최상의"방법이 있습니다. 거래가 있어야합니다.

다른 제한 사항은 완전히 인공적입니다.

일반적인 질문 2 .: (대량 운영)

ORM을 사용하면 (내가 알고있는 대부분의 ORM에 대해) 개별 개체를 통과해야합니다. 이것은 완전히 불필요하며 손이 ORM에 묶이지 않으면 디자인이 아닐 수도 있습니다.

"logic"과 SQL을 분리해야하는 요구 사항은 ORM에서 비롯됩니다. 그들은 그들이 그것을 지원할 수 없기 때문에, 그런 말을 할 수 있습니다. 본질적으로 "나쁜"것은 아닙니다.

요약

필자의 요점은 ORM이 항상 프로젝트에 가장 적합한 도구는 아니라는 것입니다. 그렇더라도 프로젝트의 모든 사용 사례에 가장 적합하지는 않습니다.

마찬가지로 DDD의 dataobject-repository 추상화도 항상 최고는 아닙니다. 나는 심지어 그들이 최적의 디자인 은 거의 없다고 말하기까지했다 .

따라서 모든 솔루션에 맞는 솔루션이 없으므로 각 사용 사례에 대한 솔루션을 개별적으로 고려해야합니다. 이는 좋은 소식이 아니며 작업을 더욱 어렵게 만듭니다.)


거기에 매우 흥미로운 점이 있습니다. 내 가정을 확인해 주셔서 감사합니다. 당신은 끈기를 가질 수있는 다른 많은 방법들이 있다고 말했습니다. PI를 제공하는 그래프 데이터베이스 (ORM 없음)와 함께 사용되는 성능있는 디자인 패턴을 추천 할 수 있습니까?
Double M

1
나는 실제로 당신이 처음에 고립이 필요한지 그리고 어떤 종류의 것이 필요한지 의문을 품고 있습니다. 분리 기술에 따라 (즉, 데이터베이스, UI 등) 거의 자동으로 "어색함"데이터베이스 기술의 다소 쉽게 교체의 우위 피하기 위해 노력하고있다을 제공합니다. 그러나 비용은 계층을 통해 확산되므로 비즈니스 로직을 변경하기가 더 어렵습니다. 또는 비즈니스 기능을 분할하여 데이터베이스를 변경하기는 어렵지만 논리를 쉽게 변경할 수 있습니다. 어느 것을 정말로 원하십니까?
Robert Bräutigam

1
도메인 (예 : 비즈니스 기능)을 모델링하고 데이터베이스를 추상화하지 않으면 (관계형 또는 그래프가 중요하지 않은 경우) 최상의 성능을 얻을 수 있습니다. 데이터베이스는 유스 케이스에서 추상화되지 않았으므로 유스 케이스는 원하는 최적의 쿼리 / 업데이트를 구현할 수 있으며 원하는 것을 달성하기 위해 어색한 오브젝트 모델을 거치지 않아도됩니다.
Robert Bräutigam

글쎄, 주요 목표는 이해, 확장 및 테스트가 쉬운 깨끗한 코드를 갖기 위해 지속성에 대한 우려를 비즈니스 논리에서 멀리하는 것입니다. DB 기술을 교환 할 수 있다는 것은 보너스입니다. 효율성과 무지 사이에 분명히 마찰이 있음을 알 수 있습니다. 이는 사용 가능한 강력한 쿼리로 인해 그래프 DB에서 더 강력 해 보입니다.
Double M

1
Java Enterprise 개발자로서 지난 20 년 동안 지속성과 논리를 분리하려고 시도했음을 알 수 있습니다. 작동하지 않습니다. 첫째, 분리는 실제로 이루어지지 않았습니다. 오늘날에도 "비즈니스"객체에는 모든 종류의 데이터베이스 관련 항목이 있으며, 주된 것은 데이터베이스 ID (및 많은 데이터베이스 주석)입니다. 둘째, 당신이 말했듯이 때로는 비즈니스 로직이 데이터베이스에서 어느 쪽이든 실행됩니다. 셋째, 데이터가있는 곳에서 가장 잘 수행되는 논리를 오프로드 할 수있는 특정 데이터베이스가있는 이유입니다.
Robert Bräutigam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.