본문 바로가기
기타

[DDD] 도메인 주도 설계를 읽고 간단한 요약

by hjhello423 2020. 1. 7.

에릭 에반스의 책 [도메인 주도 설계, 위키북스]를 읽고 간단한 내용을 정리해 보려 합니다.

아래의 내용은 뜸하지만 가끔 업데이트를 계속해서 진행하려 합니다.

'도메인 주도 설계' 책은 교보문고에서 구매하였습니다.

혼자 알아볼 수 있는 요약이 많을 거라 예상되는데 흐름이나 정의만 이해하시고 자세한 내용은 책을 참고하면 좋습니다.

 


Model (모델)

연관관계를 다룰 때는 가능한 관계를 제약하는 것이 중요하며, 아래의 세 가지 방법을 고민해봐야 합니다.

1. 탐색 방향을 부여한다.

2. 한정자(qualifier)를 추가해서 사실상 다중성(multiplicity)을 줄인다.

3. 중요하지 않은 연관관계를 제거한다.

 

한정자는 다중성을 1:1로 줄일 수 있으며, 중요한 규칙을 명시적으로 모델이 포함시킬 수 있습니다.

연관관계의 탐색 방향을 제약하여 1:N 관계로 구현하는 방법을 찾아야 합니다.

제약조건은 모델과 구현에 포함되어야 합니다.

 


Entity (참조 객체)

어떤 객체를 해당 객체의 식별성으로 정의할 경우 그 객체를 Entity라고 합니다.

많은 객체들은 해당 객체의 속성이 아닌 연속성과 식별성이 이어지느냐를 기준으로 정의됩니다. 그리고 entity의 핵심은 식별성입니다.

 

객체(모델)는 식별과 속성이 있을 수 있습니다. 그리고 이를 설계할 때 entity로 설계를 해야 하는지 의문이 들 때는 형이상학적 질문을 한번 던져보면 도움이 됩니다. 예를 들어 어떠한 시스템에서 5살이었을 때의 나와 현재의 나는 같은 인물일까?라는 질문을 던져 볼 수 있습니다. 사람은 시간이 흐르면서 키, 몸무게와 같은 속성은 수없이 변하지만 자기 자신이라는 식별은 변하지 않죠.

 

야구경기장의 좌석을 예매하는 시스템을 예로 들어 보겠습니다.

좌석은 2가지의 종류가 있다고 예를 들어보죠. 좌석 번호가 정해져 있어서 해당 번호의 좌석을 구매하는 '지정석', 좌석 번호가 없이 표를 예매하면 비어있는 자리를 찾아 앉을 수 있는 '일반석'이 있습니다.

이때 일반석은 식별성이 없지만 지정석은 '좌석 번호'라는 식별성이 있기 때문에 entity로 설계하는 것이 바람직합니다.

 

식별성과 연관관계에 있는 속성은 Entity에 구현하는 것이 좋습니다.

Entity는 고유한 key를 제공받으며 시스템에서 유일해야 합니다.

'두 객체가 동일하다는 것이 무엇을 의미하는가?'라는 의문을 던지고 이를 equals()와 == 연산자를 통해 재구현 해야 합니다.

 


Value Object (VO, 값 객체)

식별성을 갖지 않으면서 도메인의 서술적 측면을 나타내는 객체를 Value Object(VO)라고 합니다.

VO는 설계 요소를 표현할 목적으로 인스턴스화 됩니다.

설계요소가 '어느 것인지'에 대해서가 아닌 '무엇인지'에 대해서 관심을 가지고 있습니다.

 

어떤 요소의 '속성'에만 관심이 있다면 VO로 분류하여야 합니다.

VO가 전하는 속성의 의미를 표현하고, 관련 기능을 부여하도록 구현해야 합니다.

불변적(immutable) 특성을 지니도록 해야 합니다.

VO 간의 연간 관계는 완전히 제거되도록 노력해야 합니다.

 

VO는 여러 객체를 조립한 것일 수 있습니다.

'유리'(VO)는 높이/너비 속성을 가지고 있을 때, '유리'는 '창문'에 통합할 수 있습니다. 또한 '창문'에서는 '유리'가 결합되는 규칙을 가질 수 있습니다.

이때 '창문'은 VO(유리)로 구성된 복합적인 VO에 해당합니다.

 

VO가 Entity를 참소할 수도 있습니다.

미금역~강남역까지의 경로를 탐색한다면 미금역-신분당선-강남역까지의 경로(route) 객체가 생성될 것입니다. 이때 route는 VO이지만 route가 참조하는 미금역, 강남역, 신분당선 객체는 Entity입니다.

 

VO는 객체 간에 오가는 message의 매개변수로 전달되기도 합니다.

VO는 연산에서 사용할 목적으로 일시적인 용도로 사용되고 폐기되기도 합니다.

VO는 Entity의 속성으로 사용되기도 합니다.

 


Service (서비스)

매우 잘 설계했더라도 개념적으로 어떤 객체에도 속하지 않는 연산이 있을 수 있습니다. 객체의 정의에 어울리지 않는 연산을 객체에 포함시킨다면 해당 객체는 개념적 명확성이 희미해질 것입니다. 이럴 때 해당 연산을 service 모델에 포함할 수 있습니다.

도메인의 중요 프로세스나 변환 과정이 Entity나 VO의 고유 책임이 아니라면 연산을 Service로 선언되는 독립 인터페이스로 모델에 추가해야 합니다.

 

특정 연산을 수행하는 것 이상의 의미가 없는 객체들을 '행위자'(doer) + 'Manager'와 같은 이름으로 표현합니다. 이러한 Manager는 모델 객체들을 명확히 구분하도록 도와주는 근거를 만들어 줍니다.

 

Service는 모델에서 독립적인 인터페이스로 제공되는 연산으로 Entity, VO와 달리 상태를 캡슐화하지 않습니다.

service를 정의하는 기준은 클라이언트에 무엇을 제공할 수 있느냐에 있으며, 활동(행위)으로 이름을 지정합니다.

service도 규정된 책임이 있으며, service의 책임과 해당 책임을 이행하는 인터페이스는 도메인 모델의 일부로 정의됩니다.

service의 매개변수와 결과는 도메인 객체여야 합니다.

 

잘 만들어진 service는 아래의 특징이 있습니다.

1. 연산이 원래부터 Entity나 VO의 일부를 구성하는 것이 아니라 도메인 개념과 관련돼 있다.

2. 인터페이스가 도메인 모델의 외적 요소의 측면에서 정의된다.

3. 상태를 갖지 않는다.

 

계좌 이체 업무를 진행하는 시스템이 있다면 '이체' 작업은 어디에서 선언돼야 할까요?

별도의 service가 아닌 도메인 계층의 'Account', 'Ledger' 객체 간의 상호 작용에서 구현되어야 합니다.

응용

자금 이체 응용 서비스

- 입력(XML 요청과 같은) 내용의 암호화

- 이체 처리를 위한 도메인 서비스로의 메시지 전송

- 이체 확인 대기

- 인프라스트럭처 서비스를 이용한 통지 결정

도메인

자금 이체 도메인 서비스

- 금액 인출/입금에 필요한 Account와 Ledger 객체 간의 상호 작용

- 이체 결과 확인 정보 제공

인프라스트럭처

통지 서비스

- application에서 지정한 곳으로 이메일 전송

 


Module (모듈, 패키지)

module

 


Aggregate (집합체)

Aggregate는 우리가 데이터 변경의 단위로 다루는 연관 객체의 묶음을 말합니다.

root Entity와 경계(boundary)가 있습니다. root는 Aggregate내에 단 1개만 존재하며 경계 안의 객체를 참조하게 해주는 통로 역할을 합니다. 경계는 Aggregate에 무엇이 포함되고 포함되지 않는지를 정의합니다.

 

자동차를 예로 들어 보겠습니다.

자동차라는 모델은 국가에서 특별한 식별 값을 부여합니다. 자동차를 만드는 회사에서 또한 식별 값을 부여하여 생상 된 차량을 구분합니다. 이렇게 식별성이 있으므로 자동차는 Entity가 됩니다.

자동차의 바퀴는 어떨까요? 바퀴는 자동차당 4개가 존재하며 마모 정도, 크기, 생산연도 등의 속성을 가지고 있습니다.

대부분의 시스템에서는 바퀴를 식별할 필요가 없으므로 바퀴는 Model이라 볼 수 있습니다.

만약 사용자가 자신이 소유한 자동차 바퀴의 마모 상태를 알고 싶다면 어떻게 접근해야 할까요?

식별성이 있는 자동차(Entity)를 조회한 후 바퀴를 탐색하게 됩니다.

자동차는 타이어를 포함하는 경계를 지닌 Aggregate의 root Entity로 볼 수 있으며, 바퀴는 Aggregate내의 Model로 볼 수 있습니다.

 

Aggregate와 그에 속하는 root Entity 등의 특징에 대해 정리해 보겠습니다.

 

- root Entity는 전역 식별성을 지니며 불변식을 검사할 책임을 진다.

불변식은 데이터가 변경될 때마다 유지돼야 하는 일관성 규칙, Aggregate의 구성요소 간의 관계를 말합니다.

- root Entity는 전역 식별성을, 경계 안의 Entity는 지역 식별성을 지니고 있습니다. 

- Aggregate 내의 요소에 접근할 때는 반드시 root Entity를 통해서만 참조한다.

- 내부 Entity요소를 외부에 전달은 가능하지만 외부에서는 이를 일시적으로만 사용해야 한다.

- 내부 Entity는 다른 Aggregate의 root 만 참조 가능하다.

- 도메인의 생명주기 전 단계에서 불변식이 유지돼야 할 범위를 표시한다.

 


Factory

복잡한 객체나 Aggregate를 생성하는 데 필요한 지식을 캡슐화합니다.

복잡한 객체와 Aggregate의 인스턴스를 생성하는 책임을 Factory에 부여합니다.

Factory는 아무런 책임을 지니지 않을 수 있지만 도메인 설계의 일부로 구성됩니다.

클라이언트가 인스턴스화 되는 객체의 구상 클래스를 참조할 필요가 없는 인터페이스를 제공해야 합니다.

Factory Method(팩토리 메서드), Abstract Factory(추상 팩토리), Builder(빌더)와 같은 패턴으로 구현합니다.

 

Factory는 아래의 조건을 만족해야 합니다.

1. 각 생성 방법은 원자적(automic)이어야 한다.

2. 생성된 객체나 Aggregate의 불변식을 모두 지켜야 한다.

3. 일관성 있는 상태에서만 객체를 만들어 낼 수 있어야 한다.

4. 생성된 클래스보다는 생성하고자 하는 타입으로 추상화돼야 한다.

 


Repository 

Repository 

+

반응형

댓글