DDD 집계 루트 찾기
On 2월 1, 2021 by admin모두가 좋아하는 게임을 플레이하고 집계 루트를 찾으십시오. 표준 고객 / 주문 / 주문 라인 / 제품 문제 도메인을 사용하겠습니다. 전통적으로 고객, 주문 및 제품은 주문 라인이 주문 아래에있는 엔티티 인 AR입니다. 이 논리는 고객, 주문 및 제품을 식별해야하지만 OrderLine은 주문 없이는 존재하지 않는다는 것입니다. 따라서 문제 영역에는 고객이 미배송 주문을 하나만 가질 수 있다는 비즈니스 규칙이 있습니다.
그러면 주문이 고객 집계 루트 아래로 이동합니까? 그렇다고 생각합니다.하지만 그렇게하면 고객 AR이 다소 커지고 나중에 동시성 문제가 발생할 수 있습니다.
또는 고객이 일생 동안 특정 제품을 한 번만 주문할 수 있다는 비즈니스 규칙이 있다면 어떨까요. 이것은 고객이 주문을 소유해야한다는 더 많은 증거입니다.
하지만 그럴 때 배송까지 고객이 아닌 주문에 대한 모든 작업을 수행합니다. 개별 주문을 배송 된 것으로 표시하기 위해 전체 고객을로드해야하는 것은 어리석은 일입니다.
이것은 내가 제안하는 것 :
class Customer { public Guid Id {get;set;} public string Name { get; set; } public Address Address { get; set; } public IEnumerable<Order> Orders { get; set; } public void PlaceOrder(ThingsInTheOrder thingsInTheOrder) { // Make sure there aren"t any pending orders already. // Make sure they aren"t ordering a Widget if they"ve already ordered a Widget in the past. // Create an Order object and add it to the collection. Raise a domain event to trigger emails and other stuff } } class Order { public Guid Id { get; set; } public IEnumerable<OrderLine> OrderLines { get; set; } public ShippingData {get;set;} public void Ship(ShippedByPerson shippedByPerson, string trackingCode) { // Create a new ShippingData object and assign it from the data passed in. // Publish a domain event } }
내 가장 큰 관심사는 동시성 문제와 주문 자체에 특징이 있다는 사실입니다. s.
답변
집계 정의 기준은 무엇입니까?
Big Blue Book의 기본으로 돌아가 보겠습니다.
집계 : 데이터 변경을 위해 단위로 처리되는 관련 개체의 클러스터입니다. . 외부 참조는 루트로 지정된 AGGREGATE의 한 멤버로 제한됩니다. 일관성 규칙 세트는 AGGREGATE의 경계 내에서 적용됩니다.
목표는 불변을 유지하는 것입니다. 그러나 그것은 또한 적절하게 로컬 신원을 관리하는 것입니다. 즉, 의미가없는 객체를 식별하는 것입니다.
Order
및 Order line
는 확실히 그러한 클러스터에 속합니다. 예 :
-
Order
를 삭제하려면 모든 행을 삭제해야합니다. - 줄을 삭제하려면 다음 줄의 번호를 다시 지정해야 할 수 있습니다.
- 새 줄을 추가하려면 동일한 순서의 다른 모든 줄을 기준으로 줄 번호를 결정해야합니다.
- 예를 들어 통화와 같은 일부 주문 정보를 변경하면 광고 항목의 가격 의미에 영향을 미칠 수 있습니다 (또는 가격을 다시 계산해야 함).
여기서 전체 집계는 일관성 규칙과 불변성을 보장하기 위해 필요합니다.
언제 멈출까요?
이제 몇 가지 비즈니스 규칙을 설명하고이를 보장하기 위해 고객을 고려해야한다고 주장합니다. 집계의 일부 :
비즈니스가 있습니다. 고객은 한 번에 하나의 미배송 주문 만 가질 수 있다는 규칙입니다.
물론, 그 이유는 없습니다. 의미를 살펴 보겠습니다. 주문은 항상 고객을 통해 액세스됩니다. 이것이 실제입니까? 작업자가 주문을 배송하기 위해 상자를 채울 때 주문에 액세스하기 위해 고객 바코드와 주문 바코드를 읽어야합니까? ? 사실, 일반적으로 주문의 ID는 고객에게 국한되지 않고 전역 적이며 이러한 상대적 독립성은 그를 집합 외부로 유지하도록 제안합니다.
또한 이러한 비즈니스 규칙은 정책으로 더 많이 보입니다. 이러한 규칙에 따라 프로세스를 실행하는 것은 회사의 임의적 인 결정입니다. 규칙을 준수하지 않으면 상사가 불만을 가질 수 있지만 데이터는 실제로 일관성이 없습니다. 또한 밤새 ” 고객 당 한 번에 하나의 미배송 주문 “이 “가 될 수 있습니다. 고객 당 배송되지 않은 주문 10 건 ” 또는 고객과 독립적으로 ” 창고 당 배송되지 않은 주문 100 건 “, 집계가 더 이상 정당화되지 않도록합니다.
답변
짧은 버전
DDD의 이론적 근거는 도메인 개체가 기능적 도메인 요구 사항을 충족해야하는 추상화라는 것입니다. 도메인 개체가 이러한 요구 사항을 쉽게 충족 할 수없는 경우 잘못된 추상화를 사용하고있을 수 있습니다.
이름 지정 엔티티 명사 를 사용하는 도메인 객체 는 이러한 객체가 서로 밀접하게 결합되어 부풀어 오른 “신”객체가 될 수 있으며,이 질문에있는 것과 같은 문제를 일으킬 수 있습니다. “P에 적합한 장소는 어디입니까 ut CreateOrder 메소드? “.
“올바른”집계 루트를 더 쉽게 식별하려면 도메인 개체가 기능적 상위 수준 비즈니스 요구 사항을 기반으로하는 다른 접근 방식을 고려하십시오.시스템 사용자가 수행해야하는 기능적 요구 사항 및 / 또는 동작을 암시하는 명사를 선택합니다.
Long Version
DDD는 비즈니스에서 도메인 개체 의 그래프를 생성하기위한 OO 디자인에 대한 접근 방식입니다. 시스템의 계층 -도메인 개체는 높은 수준의 비즈니스 요구 사항을 충족해야하며, 이상적으로는 기본 영구 데이터 저장소의 성능 및 무결성과 같은 작업을 위해 데이터 계층에 의존 할 수 있어야합니다.
다른 방법으로는이 목록의 글 머리 기호 일 수 있습니다.
- 엔티티 명사는 일반적으로 데이터 속성을 제안합니다.
- 도메인 명사는 행동을 제안해야합니다.
- DDD 및 OO 모델링은 기능적 요구 사항 및 핵심 도메인 / 비즈니스 로직에 기반한 추상화와 관련됩니다.
- 비즈니스 로직 계층은 높은 수준의 도메인 요구 사항을 충족시키는 역할을합니다.
DDD에 대한 일반적인 오해 중 하나는 도메인 개체가 물리적 현실을 기반으로해야한다는 것입니다. 세계 “사물”(즉, 모든 종류의 데이터 / 속성에 귀속되는 실제 세계에서 가리킬 수있는 일부 명사), 그러나 이러한 실제 세계 사물의 데이터 / 속성이 반드시 좋은 출발점이되는 것은 아닙니다. 다운 기능 요구 사항.
물론 Business Logic은이 데이터를 사용 해야하지만, 도메인 객체 자체는 궁극적으로 기능 도메인 요구 사항 및 동작을 나타내는 추상화 여야합니다.
예를 들어; Order
또는 Customer
와 같은 명사는 동작을 의미하지 않으므로 일반적으로 비즈니스 논리 및 도메인 개체를 나타내는 데 도움이되지 않는 추상화입니다.
비즈니스 로직을 나타내는 데 유용 할 수있는 추상화 유형을 찾을 때 시스템이 충족 할 것으로 예상 할 수있는 일반적인 요구 사항을 고려하십시오.
- 영업 담당자로서 저는 판매 할 제품의 가격과 수량에 대한 송장을 생성 할 수 있도록 신규 고객을위한 주문을 생성하려고합니다.
- 고객 서비스 어드바이저로서 대기중인 주문을 취소하여 창고 운영자가 주문을 이행하지 않습니다.
- 고객 서비스 고문으로서 제품을 재고로 조정하고 고객의 원래 지불을 통해 지불을 환불 할 수 있도록 주문 라인을 반품하고 싶습니다. 방법.
- 창고 운영자로서 대기중인 주문의 모든 제품과 배송 정보를보고 제품을 선택하고 택배를 통해 배송하려고합니다.
- etc.
DDD 접근 방식으로 도메인 요구 사항 모델링
위 목록을 기반으로 잠재적 인 도메인 개체 고려 이러한 주문 시스템의 경우 :
SalesOrderCheckout PendingOrdersStream WarehouseOrderDespatcher OrderRefundProcessor
도메인 개체로서 다양한 동작 도메인 요구 사항의 소유권을 갖는 추상화를 나타냅니다. 실제로 그들의 명사는 그들이 충족시키는 특정 기능적 요구 사항을 강력하게 암시합니다.
(통과 할 EventMediator
와 같은 추가 인프라가있을 수도 있습니다. 새 주문이 생성 된시기 또는 주문이 배송 된시기 등을 알고 싶어하는 관찰자를위한 알림).
예 : SalesOrderCheckout
는 고객, 배송 및 제품에 대한 데이터를 처리하지만 “주문 배송, 보류중인 주문 정렬 또는 환불 처리에 대한 동작과는 관련이 없습니다.
SalesOrderCheckout
는 도메인 요구 사항을 충족하기 위해 고객이 너무 많은 항목을 주문하는 것을 방지하고, 일부 유효성 검사를 실행하고, 시스템의 다른 부분에 대한 알림을 발생시키는 것과 같은 비즈니스 규칙을 적용하는 것을 포함합니다.이 모든 작업을 반드시 의존 할 필요없이 수행 할 수 있습니다. 기타 객체
도메인 객체를 나타내는 엔티티 명사를 사용하는 DDD
Ther e는 Order
, Customer
및 Product
도메인 개체로; 이러한 문제 중에는 질문에서 언급 한 문제가 있습니다.
- 메서드가
Order
를 처리하는 경우Customer
및Product
, 어떤 도메인 개체에 속합니까? - 이 3 개 개체의 집계 루트는 어디에 있습니까?
도메인 개체를 나타내는 엔티티 명사를 선택하면 다음과 같은 여러 가지 상황이 발생할 수 있습니다.
-
Order
,Customer
및Product
는”신 “개체로 성장할 위험이 있습니다. - 단일 모든 것을 하나로 묶는 신 개체
- 이러한 개체는 서로 밀접하게 결합 될 위험이 있습니다.
this
를 통과하지 않고는 도메인 요구 사항을 충족하기 어려울 수 있습니다. div> (또는self
)
get
/ set
메서드를 노출 할 것으로 예상되는 도메인 개체 (또는 그렇지 않은 경우 다른 프로그래머 아마도 나중에 ..). DDD, OO 디자인 및 일반 모델
DDD 및 OO 디자인에 대한 일반적인 오해는 “일반”모델이 어떻게 든 ” 마틴 파울러가 Anaemic Domain Model 을 설명하는 기사를 작성했습니다. 기사에서 분명히 말했듯이 DDD 자체는 레이어 간의 깔끔한 분리 접근 방식을 “모순”하지 않습니다.
“또한 도메인 개체에 동작을 넣는 것이 모순되지 않아야 함을 강조 할 가치가 있습니다. 레이어링을 사용하여 분리하는 확실한 접근 방식 지속성 및 표현 책임과 같은 것에서 도메인 로직을 평가합니다. 도메인 객체에 있어야하는 로직은 도메인 로직 (검증, 계산, 비즈니스 규칙 등)입니다. “
즉, 다른 계층간에 전송 된 비즈니스 데이터를 보관하기 위해 일반 모델 (예 : 사용자가 새 주문을 생성하려고 할 때 사용자 애플리케이션이 전달한 주문 모델)을 사용하는 것은 “빈혈 도메인 모델”과는 다릅니다. “일반”데이터 모델은 데이터를 추적하고 계층 (예 : REST 웹 서비스, 지속성 저장소, 응용 프로그램 또는 UI 등)간에 데이터를 전송하는 가장 좋은 방법입니다.
비즈니스 로직은 이러한 모델에 데이터를 저장하고이를 비즈니스 상태의 일부로 추적 할 수 있지만 반드시 해당 모델의 소유권을 가지는 것은 아닙니다.
집계 루트
예제 도메인 개체를 다시 살펴 보겠습니다. –SalesOrderCheckout
, PendingOrdersStream
, WarehouseOrderDespatcher
, OrderRefundProcessor
아직 명확한 Aggregate Root가 없습니다. 이러한 도메인 개체는 겹치지 않는 것처럼 보이는 별도의 책임이 있기 때문에 실제로 중요하지 않습니다.
기능적으로는 SalesOrderCheckout
가 PendingOrdersStream
와 대화 할 필요가 없습니다. 데이터베이스에 새 주문을 추가하면 완료됩니다. 반면에 PendingOrdersStream
는 데이터베이스에서 새 주문을 검색 할 수 있습니다. 이러한 개체는 실제로 각 주문과 상호 작용할 필요가 없습니다. 다른 직접 (아마도 이벤트 중재자가 둘 사이에 알림을 제공 할 수 있지만 이러한 객체 간의 결합이 매우 느슨 할 것으로 예상합니다.)
아마도 집계 루트는 하나 이상의 항목을 주입하는 IoC 컨테이너 일 것입니다. 이러한 도메인 개체를 UI 컨트롤러에 추가하고 EventMediator
및 Repository
와 같은 다른 인프라도 제공합니다. 또는 비즈니스 계층 위에있는 일종의 경량 Orchestrator 서비스 일 수도 있습니다.
집계 루트는 도메인 개체 일 필요가 필요하지 않습니다 . 도메인 개체간에 우려 사항을 분리하기 위해 일반적으로 집계 할 때 유용합니다. 루트는 비즈니스 로직이없는 별도의 개체입니다.
댓글
- 당신의 답변이 책에서 발췌 한 도메인 기반 디자인을 사용하는 Microsoft 고유 기술인 Entity Framework의 개념을 통합했기 때문에 반대 투표를했습니다. 같은 이름의 Eric Evans가 작성했습니다. 답변에 DDD 책과 직접 모순되는 몇 가지 진술이 있으며이 질문은 Entity Framework에 대해 언급하지 않지만 특히 DDD로 태그가 지정됩니다. ‘도 질문에 지속성에 대한 언급이 전혀 없기 때문에 데이터베이스 테이블이 어디에 관련되어 있는지 ‘ 알 수 없습니다.
- @RibaldEddie 시간을내어 답변과 의견을 검토해 주셔서 감사합니다. 영구 데이터에 대한 언급이 ‘ 정답에 포함될 필요가 없다는 데 동의하므로 ‘이를 제거하기 위해 단어를 변경했습니다. 답변의 주요 초점은 ” 엔티티 명사가 종종 ‘ 성향으로 인해 매우 좋은 도메인 객체 클래스 이름이 아니라고 요약 할 수 있습니다. 밀접하게 결합 된 부풀어 오른 신 개체가됩니다 “, WRT 기능 요구 사항 / 동작에 대한 메시지와 추론이 이제 더 명확 해 지길 바랍니다.
- DDD 책은 ‘ IIRC 개념이 없습니다. 인스턴스화 될 때 두 개의 개별 인스턴스가 두 개의 고유하고 지속 가능한 것을 의미하도록 인스턴스화 될 때 영구적이고 고유 한 ID를 갖는 클래스 인 엔티티가 있습니다. 이는 ‘가 아닌 값 개체와 대조됩니다. 신원이없고 ‘ 시간이 지나도 지속되지 않습니다. 이 책에서 엔티티와 값 객체는 모두 도메인 객체입니다.
답변
문제 도메인에는 고객은 한 번에 하나의 미배송 주문 만 가질 수 있다는 비즈니스 규칙.
토끼 구멍에 너무 깊이 들어가기 전에 Greg를 검토해야합니다. Young의 일관성 설정 에 대한 토론, 특히 :
실패가 비즈니스에 미치는 영향은 무엇입니까?
많은 경우 정답은 “예방하려고하지 않기 때문입니다. 잘못된 일이 발생하는 대신 문제가있을 수있는 경우 예외 보고서를 생성 합니다.
하지만 여러 미배송 주문이 있다고 가정합니다. 비즈니스에 중대한 책임이 있습니다 ….
예, 배송되지 않은 주문이 하나만 있는지 확인하려면 고객의 모든 주문을 볼 수있는 집계가 있어야합니다. .
이 집계는 반드시 고객 집계 일 필요는 없습니다 .
주문 대기열 또는 주문 내역과 같을 수 있습니다. 고객은 동일한 대기열로 이동합니다. 말씀하신대로 “고객의 모든 프로필 데이터가 필요하지 않으므로이 집계의 일부가되어서는 안됩니다.
그러나 배송과 관련하여 고객이 아닌 주문에 대한 모든 작업을 수행합니다.
예, 실제로 주문 처리를하고 풀 시트에서 이력보기는 특별히 관련이 없습니다.
이력보기는 불변성을 적용하기 위해 주문 ID와 현재 처리 상태 만 필요합니다. 이는 반드시 주문과 동일한 집계의 일부일 필요는 없습니다. 집계 경계는 뷰를 구조화하는 것이 아니라 변경 관리에 관한 것임을 기억하십시오.
따라서 주문을 집계로 처리 할 수 있습니다. , 주문 내역을 별도의 집계로, 둘 간의 활동을 조정합니다.
답변
설정했습니다. 빨대 사람의 예. 너무 단순하고 실제 시스템을 반영하는 것 같지 않습니다. 그 때문에 지정한 방식으로 이러한 엔티티 및 관련 동작을 모델링하지 않습니다.
클래스는 여러 집계에 반영되는 방식으로 주문 상태. 예를 들어 고객이 시스템을 고객의 주문 요청을 처리해야하는 상태로 설정하면 CustomerOrderRequest
또는 또는 CustomerOrder
또는 비즈니스에서 사용하는 언어에 관계없이 고객과 주문 라인 모두에 대한 포인터를 보유한 다음 canCustomerCompleteOrder()
서비스 계층에서 호출됩니다.
이 도메인 개체에는 주문이 유효한지 여부를 결정하는 비즈니스 로직이 포함됩니다.
주문이 유효하고 처리 되었다면이 개체를 처리 된 주문을 나타내는 다른 개체로 전환 할 수있는 방법이 있습니다.
당신이 이해하는 데 문제는 당신이 인위적인 것을 사용하고 있다는 것입니다. 집계의 지나치게 단순화 된 예입니다. PendingOrder
는 UndeliveredOrder
에서 분리 된 자체 집계 일 수 있으며 다시
또는 CancelledOrder
또는 기타
댓글
- 하지만 젠더 중립적 인 언어는 재미 있습니다. 여성들은 결코 까마귀를 겁 주려고 들판에 서지 않는다는 점에 주목하고 싶습니다.
- @RobertHarvey ‘ 집중해야 할 이상한 일입니다. 내 게시물에. 허수아비와 조각상은 모두 역사를 통틀어 정기적으로 여성 형태로 나타났습니다.
- 그렇지 않았다면 게시물에서 구별을하지 않았을 것입니다 ‘ ‘ 중요하다고 생각하지 마세요. 언어학 상이 용어는 ” straw man입니다. ” 성 차별에 대한 모든 의구심은 ” 도대체 그가 말하는 것은 ” 자신의 용어를 발명하여 만든 요소입니다.
- @RobertHarvey man은 ‘ 그 말을 듣지 못했다면 ‘ 그들이 짚 사람이 무엇을 의미하는지 이해할 수 있음을 의미합니다. 내 게시물의 내용에 초점을 맞출 수 있습니까?
답변
Vaughn Vernon은 7 장 (서비스) 시작 부분에있는 “Implementing Domain-Driven Design”책 :
“수행해야하는 작업이 만족 스러울 때 도메인 모델에서 서비스를 만들어야한다는 가장 좋은 표시가되는 경우가 많습니다. Aggregate 또는 Value Object에 대한 방법으로서의 위치 “.
이 경우에는 Customer 인스턴스와 주문 항목 목록을받는 “CreateOrderService”라는 도메인 서비스가있을 수 있습니다.
class CreateOrderService { public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder) { // Get all the orders for the customer // Check if any of the things to be ordered exist in previous orders // If none have been previously ordered, create the order and return it // Otherwise return null } }
댓글
- 질문에서 동시성 문제를 해결하는 데 도메인 서비스가 어떻게 도움이 될 수 있는지 자세히 설명해 주시겠습니까?
답글 남기기