brunch

You can make anything
by writing

C.S.Lewis

by 에디의 기술블로그 May 14. 2021

[스터디] 도메인 주도 설계 2주차

값객체,엔티티,애그리거트


해당 글은 작년 스터디 중 작성한 글입니다. 잘못된 내용이 있어서 잠시 취소 처리하였습니다. 조만간 새로운 글을 작성해서 발행하겠습ㄴ다.




도메인 주도 설계에 대해서 전혀 모른다면, 아래 글을 먼저 읽어보길 바랍니다.

https://brunch.co.kr/@springboot/605



목차 (확정은 아님..해보고 바뀔수 있음)

[1주차] 1. 도메인 주도 설계
[1주차] 2. 전략적 설계

[1주차] 3. 전술적 설계

[2주차] 4. 값객체

[2주차] 5. 엔티티

[2주차] 6. 애그리거트

[3주차] 7. 도메인 서비스

[3주차] 8. 애플리케이션 서비스

[3주차] 9. 테스트 코드 작성하기

[4주차] 10. 리포지토리

[4주차] 11. 리포지토리 구현

[5주차] 12. 아키텍처

[5주차] 13. 의존성

[5주차] 14. 팩토리

[6주차] 15. 도메인 이벤트

[6주차] 16. 데이터 무결성

7주차, 8주차 미정..




미리 생각해볼 내용(주저리주저리...)


1주차에서는, 도메인주도 설계(전략적 설계 와 전술적 설계)에 대해서 간략하게 소개하였다. 2주차에서는 "도메인 주도 설계"의 전술적 설계 중 엔티티, 값객체, 애그리거트 에 대해서 공부할 예정이다. 간혹, 일부 개발자는 도메인 지식에 대한 관심보다는, 기술적인 관심 위주로 도메인 모델링을 하는 경우를 종종 볼 수 있는데, 바람직하지 않다. 기술적 관심 위주가 아닌, 도메인 지식을 기반으로 한 도메인 모델링이 포함된 전략적 설계가 도메인 주도설계 의 핵심이라고 생각한다. 그럼에도 불구하고, 이 글에서 전술적 설계에 대해서 먼저 스터디하는 이유는... (필자가 전략적 설계에 대해서 설명할 자신이 없기 때문이다...ㅠㅠ)


비록 이 글에서, 전략적 설계가 아닌, 전술적 설계에 대해서 허접하게 겨우겨우 알아가고 있지만... 사실 도메인 전문가와 함께 도메인 개념을 분석, 모델링 및 소프트웨어로 잘 구현하는 것이 도메인 주도 설계의 핵심이다.


소프트웨어가 궁극적으로 해결해야 하는 도메인 지식이 무엇인지,

요구사항이 무엇인지에 대해서 파악하는 것 이 훨씬 중요하다.


그리고, 다른 참고자료의 사례를 베끼고 싶지 않아서,

질병관리시스템이라는 생소한 주제를 선정하였는데 이해하기 어려울 수도 있다. 


만약, 이 글이 조금이라도 이해가 안 된다면... 그냥 최범균 님의 "ddd start" 3장을 정독하길 바란다~



4. 값객체


4장에서는 값객체에 대해서 알아본다.


도메인 모델은 개념을 추상화한 지식일 뿐이며, 동작하는 실체가 아니다.

도메인 객체는 도메인 모델을 소프트웨어에서 동작할 수 있도록 개발자가 구현한 것이다.

도메인 모델을 구현한 도메인 객체에는 아래와 같이 2가지가 있다.

- 값객체

- 엔티티


4.1 값객체

값객체는 개념적으로 식별성이 없는 객체인데, 사물의 어떤 특징을 묘사할 때 사용된다.


- 엔티티는 개념적 식별성이 있고, 값객체는 개념적 식별성이 없다.

- 엔티티는 속성이 변경될 수 있고, 값객체는 속성이 변경되지 않는 불변객체이다.


그럼, 질문을 하겠다.


[질문] 사람이라는 도메인 객체는 엔티티인가?

1. 식별자가 있으므로 엔티티이다.

2. 아니다, 값객체이다.

3. 질문만으로는 판단할 수 없다.


정답은 잠시 후에 다시 설명하겠다.

 

4.1.1 예시로 알아보는 값객체 vs 엔티티

몇가지 샘플 사례를 생각해봤는데, 적합한 예시인지는 솔직히 잘 모르겠다. 알아서 잘 생각하길 바란다.


1)첫번 째 예시 - 위치정보

필자는 운전할 때 카카오 네비를 사용한다. 구로에서 판교까지의 자동차 이동 경로를 알고 싶다.

이동경로에 대한 동선 정보는 아래와 같이 확인할 수 있다.


이동동선의 위치 정보를 도메인 객체(값객체 or 엔티티)로 구현할 것이다. 이동 경로에서 필자는 "제2경인고속도로"를 지나간다. "제2고속도로"는 엔티티, 값객체 중 어떤 걸로 구현하면 좋을까? 이 경우 위치 정보는 값객체로 구현하면 좋을 것이다. 이동 경로 중 하나인 각각의 동선(위치)는 소프트웨어에서 특별하게 개념적으로 식별성을 가질 필요가 없다. 그래서 값객체가 적합하다.

하지만, "제2경인고속도로"의 오전 출근시간 이동량 측정하고 싶다면... 이 경우에 "제2경인고속도로" 라는 위치 정보는 개념적 식별성이 필요하기 때문에 엔티티가 적합하다.


위치 정보에 대한 다른 예시이다. 닌텐도의 포켓몬고를 해봤는가? 포켓몬고에서 포켓스탑은 몬스터를 잡을 수 있는 장소(위치 정보)이다. 체육관은 몬스터를 잡으면 체육관을 점령할 수 있다고 알고 있다.(필자가 해본적은 없다.) 포켓몬고에서의 포켓스탑, 체육관은 개념적 식별성이 반드시 필요하다. 그래서, 이 경우 도메인 객체로 구현할 때 위치 정보는 엔티티로 구현해야 한다.

위치 정보는 소프트웨어에서 어떻게 사용되는지에 따라서(개념적으로 식별성이 필요한지, 필요없는지에 따라서) 엔티티, 값객체 로 구현할 수 있다.


2)두번 째 예시 - 집주소

쿠X 에서 로켓배송으로 주문을 해보자. 12시가 되기 전에 필자는 치약을 주문했다. 잠시 후, 칫솔이 없다는 사실을 뒤늦게 깨닫고 12시가 되기 전에 칫솔을 신규 주문으로 추가하였다. 쿠X에서는 우리집에 2개의 상품(치약 & 칫솔)을 발송할 것이다. 로켓배송으로 다음날 우리집 앞에 도착한 물품은 어떻게 왔을까? 비효율적이지만, 두개의 박스가 도착했다.

박스 하나에 같이 보내주면 안되나?

아마도, 해당 배송 시스템에서 관리하는 각각의 주문에 대한 집주소는 값객체로 구현되어있을 것이다.


와이프랑 대화 중, 여름이 오기전에 에어컨 청소를 해야한다고 대화를 나누었다. 필자는, 서비스 센터에 전화해서 에어컨 청소를 요청하였다. 근데, 필자가 요청했다는 사실을 모르는 와이프가 똑같은 서비스 센터에 전화를 해서 에어컨 청소를 중복으로 요청하였다. 비록 중복 요청이지만, 서비스 센터에서는 같은 주소에서 중복이라는 사실을 이미 알고, 한번만 방문해서 청소를 해줄것이다.

아마도, 에어컨 청소 서비스 센터에서 관리하는 집주소는 엔티티로 관리될 것이다.


3)세번 째 예시 - 타이어

"타이어는 자동차를 구성하는 한 부품이다. 타이어의 속성은 차이가 있지만, 바꿔 쓸 수 있으므로 값객체에 적합한 개념이다. 그러나 타이어를 만드는 공장이라면? 공장에서의 타이어는 생산 라인이 있으며, 생산 라인에서 타이어는 언제 만들어졌는지 식별하는 것이 중요하다. 즉, 이때의 타이어는 엔티티로 나타내는 것이 더 적합하다." [2] 도메인 주도 설계 철저 입문 (나루세 마사노부 지음, 위키북스)



4.1.2 개념적 식별성

도메인 객체(엔티티, 값객체)를 구현할 때, 개념적 식별성이 중요하다고 설명하였다. 일반적으로 엔티티는 식별자를 갖고, 값객체는 식별자가 없다. 하지만, 식별자가 있는데도 값객체로 구현하는게 좋은 경우도 있다. 물리적으로는 식별자를 갖지만, 소프트웨어의 개념적으로 식별성이 필요 없는 경우이다.


[질문] 사람이라는 도메인 객체는 엔티티인가?

1. 식별자가 있으므로 엔티티이다.

2. 아니다, 값객체이다.

3. 질문만으로는 알수 없다.


[정답] 3번

사람이라는 도메인 객체는, 일반적으로는 거의 모든 소프트웨어에서 엔티티로 구현될 것이다. 하지만, 어떤 도메인에서는 사람이라는 객체는 개념적으로 식별성이 필요 없을 수도 있다. 물리적으로는 식별자를 가질수 있지만, 소프트웨어에서 굳이 식별할 필요가 없는 경우이다.



도메인 객체를 잘 구현하기 위해서는,

즉, 엔티티와 값객체를 잘 구현하기 위해서는 도메인 지식에 대해서 잘 파악해야 한다. 개발자의 관점이 아니라, 도메인 전문가의 관점에서 이해해야 한다. 


도메인 전문가와 협업을 잘 해야 하며, 이때 필요한 과정은 전략적 설계이다.(비록, 필자의 스터디에서는 다루지 않지만...)



4.2 값객체는 불변객체

값객체는 절대 변할 수 없는, 불변격체로 정의해야 한다. 아래와 같이, 이름이라는 값객체를 정의하였다.

이름이라는 도메인 객체는 불변이다. 불변이기 때문에, 아래와 같이 이름을 변경하는 메서드를 구현하면 안된다.

또한, 세터 메서드를 사용하면 안되며, 당연히 롬복의 @Data, @Setter 어노테이션 역시 사용하면 안될것이다. 해당 특징을 지키기위해서, 속성은 final 키워드로 정의하자. 생성자에서 초기화한 이후에는 객체를 변경할 수 없도록 유지해야 한다.


근데, 개명할수도 있다. 이런 경우에는 어떻게 해야하는가? 그렇지만, 값객체는 변경될 수 없다. 새로운 값객체를 만들어서 사용하면 된다.


정리하면,

- 값객체는 개념적 식별성이 없다. (물리적으로 식별자를 가질수는 있지만,, 드문 케이스이다.)

- 값객체는 불변이다.

- 속성 변경 메서드를 구현하지 않는다.

- 롬복의 @Setter, @Data 는 사용하지 않는다.

- 모든 속성은 final 로 정의한다.

- 생성자에서 초기화한 이후로 변경할 수 없도록 한다.


4.3 값객체의 비교

(시간 관계 상) 자세한 설명은 생략한다.

두개의 객체가 eqals 가 true 인 경우에 같은 사람을 의미하는 건 아니다. 동명이인일수도 있고, 실제 한사람일수도 있다. 두 객체의 이름이 같은지만 비교한다.

(자세한 설명은 생략한다.)


4.4 값객체는 얼마나 작게 쪼개야 하는가?

(이렇게까지 할 필요는 없겠다..)

아래와 같이 FirstName 이라는 값객체를 정의해보자.

그리고, 이름 값객체에서 FirstName 의 타입을 String 에서 FirstName 으로 변경할 수 있다.

이렇게까지 쪼갤필요가 있을까? 그렇지 않다. 참고자료 어디서 봤는지 정확히 기억이 나지 않는다. 무조건 작게 쪼갤 필요는 없단다. 참고자료 링크는 찾으면 다시 공유하겠다.


4.5 (중요)소프트웨어 복잡성에 대해서


"엔티티의 식별성을 관리하는 일은 매우 중요하지만 그 밖의 객체에 식별성을 추가한다면 시스템의 성능이 저하되고, 분석 작업이 별도로 필요하며, 모든 객체를 동일한 것으로 보이게해서 모델이 혼란스러워질 수 있다. 소프트웨어 설계는 복잡성과의 끊임없는 전투다. 그러므로 우리는 특별하게 다뤄야할 부분과 그렇지 않은 부분을 구분해야 한다. " - [3]에릭 에반스 - 도메인 주도 설계 100page



5. 엔티티


5.1 엔티티 정의

엔티티는... 개념적 식별성을 갖는다...


5.2 엔티티는 식별자를 갖고, 속성이 변한다.

1주차에서 다룬 "역학조사시스템" 을 예시로 설명하겠다.


역학조사 시스템에서는, "확진자"는 개념적 식별성을 갖는다. 그래서, "확진자" 객체는 엔티티로 구현해야 하며, 반드시 식별자를 가져야 한다. 우리는 전략적 설계 과정에서 "확진자-ConfirmedCase" 라는 언어를 사용하기로 정의하였다. 아래와 같이 샘플 코드에서 ConfirmedCase 도메인 객체(엔티티)는 ConfirmedCaseId 라는 식별자를 갖는다. 엔티티에는 필요한 속성을 정의하였는데 해당 속성들은 변경이 가능하다.


값객체와는 다르게, 엔티티는 속성을 변경할 수 있다.

엔티티는 속성이 변할 수 있지만, 그렇다고 해서 세터 메서드를 전부 선언해주면 안된다. 즉, 롬복의 @Data, @Setter 어노테이션을 사용하면 안된다. 대신, 속성 변경이 필요한 경우에 변경 메서드를 구현해주면 된다. 예를 들어서, 확진자의 이름, 주소는 변경될 수 있다고 가정한다면, 아래와 같이 changeName, changeAddress 와 같은 메서드를 사용할 수 있다.

setName, setAddress 라는 메서드 네이밍보다는 가독성이 좋다. 단, Name은 값객체이며 불변이기 때문에, 값객체의 속성은 변경할 수 없다. 그래서, 새로운 객체를 파라미터로 전달받아서 변경해줘야 한다. 


5.3 엔티티의 비교

엔티티는 식별자에 의해서 비교할 수 있다.

만약, 확진자가 갑자기 이름을 개명했다. 소프트웨어에서 두 개의 객체가 서로 이름이 다르다고 가정해보자. 이 경우, 비록 이름은 다르지만, 두 객체에서 동일한 식별자를 갖고 있다면 해당 두 객체는 같은 사람으로 판단할 수 있을 것이다.

이름이 달라도, 식별자가 같다면 같은 사람으로 판단할 수 있다.


반대로, 이름이 같은 두 객체가 있다고 가정해보자. 이 경우에는 같은 사람일수도 있고 아닐수도 있다. 같은 사람이라면 식별자가 같을 것이고, 동명이인이라면(확진자 2명) 두 객체의 식별자는 서로 다를 것이다.


5.4 엔티티의 책임, 도메인 응집도

위에서도 설명했지만, 도메인 객체는 행위를 정의할 수 있었다. 이름을 변경하거나, 주소를 변경하는 등 기능을 도메인 엔티티에 응집하도록 한다.

하지만, 간혹 실무에서 사용하는 소프트웨어를 살펴보면, 도메인 객체의 속성만 정의되어있고 행위에 대한 정의가 전혀 없는 클래스를 볼 수 있다. 또한 롬복의 @Data 또는 @Setter 어노테이션을 남용하고, 모든 속성의 세터 메서드를 오픈하는 경우도 있다.(바람직하지 않다.) 세터를 전부 오픈하는 클래스는 자유롭게 객체를 변경할 수 있겠지만, 잘 설계된 도메인 객체가 아니다.


확진자 객체는 "역학조사" 진행 유무에 따라서 아래와 같은 상태값을 갖는다.

역학조사가 완전히 끝난 경우에 확진자 도메인 객체의 상태를 "INVESTIGATED" 로 변경해야 한다.


AS-IS) 응용서비스에서 구현해보자.

아래와 같이 InvestigationService 클래스에서, completeInvestigation 메서드에서 역학조사 완료에 대한 상태를 변경해줄 수 있는데, 확진자 도메인 객체의 "setStatus" 라는 메서드를 사용할 것이다.

만약, 확진자의 상태가 이미 "역학조사완료" 인 경우에 대한 예외 처리가 필요하다면, 아래와 같이 예외 처리 로직을 추가하면 된다.

하지만, 이 사실을 모르는 다른 개발자가 다른 응용서비스에서 역학조사 완료 및 예외 처리 로직을 똑같이 구현하게 된다면, 도메인 로직이 다수의 응용서비스에서 사용될 것이다. 역학조사의 완료 유무에 대한 상태체크는 도메인 엔티티의 고유한 책임으로서, 도메인 로직으로 정의하는게 좋다. 아래와 같이 ConfirmedCase 엔티티에 completeInvestigation 메서드를 정의하였고, 해당 메서드에서 도메인 로직(즉, 상태체크)에 대한 중요 비즈니스 로직을 정의하였다.

그리고, 응용서비스에서는 아래와 같이, 도메인 객체의 메서드를 실행해주면 된다. 도메인 로직에 대해서 관여하지 않는다.




단...

모든 도메인 로직을 도메인 객체의 책임으로 해야한다는 강박관념을 가질 필요는 없다. 오히려 도메인 객체에 모든 내용을 전부 정의하는 생각은 오히려 소프트웨어에 혼란을 줄 수도 있을 것이다.

(적절한 예시가 생각나지 않으니... 스터디를 진행하면서 생각나면 그때 다시 설명해보겠다.)



이제 드디어,

애그리거트에 대해서 공부하겠다. 참고로, 애그리거트는 3주차 까지 이어서 설명하겠다.


6. 애그리거트


애그리거트를 정의하기 위해서는!!

함께 변경되어야 하는 도메인 객체는 무엇인지 파악해야 하며,

결국 도메인 개념(지식)및 소프트웨어에서 문제를 해결하기 위한 요구사항을 명확히 알아야 한다.


샘플 사례에 대한 도메인 지식은 아래와 같다.

1) 확진자의 정보는 동선 정보와 별개로 등록된다.

(확진자 정보는 선별진료소에서 전달 받은 직후에 등록되며, 확진자에 대한 동선은 역학조사관이 역학조사가 완료된 이후에 등록한다.

2) 동선 데이터, 밀접접촉자는 둘다 개념적 식별성을 갖는다. 즉, 엔티티이다.

3-1) 동선 데이터와 밀접접촉자 정보는 함께 추가되고, 함께 변경된다. 

이 경우에는 동선,밀접접촉자는 하나의 애그리거트로 묶는다.

3-2) 동선 데이터와 밀접접촉자 정보는 개별적으로 등록된다. 

이 경우에는 동선,밀접접촉자는 두개의 애그리거트로 분리한다.


6.1 애그리거트


애그리거트 기본 규칙 -- 반버논 책 참고

1. 애그리거트 경계 내의 비즈니스 불변사항을 보호하라.

2. 작은 애그리거트 를 설계하라

3. 오직 식별자로만 다른 애그리거트를 참고하라.

4. 결과적 일관성을 사용해 다른 애그리거트를 갱신하라.



필자의 글이 이해가 잘 안될테니, 조금 쉬운 사례로 생각해보자.

자... 게시판을 구축한다.

게시판의 게시글에는 댓글이 달릴 수 있다. 이 경우, 게시글과 댓글은 하나의 애그리거트로 묶을 수 있나? 아니다. 이유는, 게시글에 댓글이 하나도 없을 수 있기 때문이다. 즉, 게시글 데이터와 댓글 데이터는 같이 추가되고 같이 변경되는 데이터가 아니다. 그러므로, 별도의 애그리거트로 분리해야 한다.

이커머스의 상품 상세 화면을 구축한다.

상품에 대한 리뷰가 한개도 없을 수도 있다. 상품 상세 정보와 리뷰 정보는 같이 등록되는 데이터가 아니다. 개별적으로 등록되기 때문에, 이 경우에도 별도의 애그리거트로 분리해야 한다.


3-1), 3-2) 의 사례를 다시 읽어보면

동선 데이터와 밀접 접촉자 데이터를 같이 등록하느냐, 따로 등록하느냐에 따라서

애그리거트의 분리 여부를 결정할 수 있다.



6.1 애그리거트 샘플(1)

 역학조사의 동선 데이터 와 밀접접촉자 데이터가 같이 추가된다라는 가정이다.


반복해서 말하지만, 결국 도메인에 대한 지식, 이해가 우선이다.

(필자처럼 개발을 잘 못하는 사람은 도메인 지식을 잘 이해해도 개발 퀄러티가 떨어질 수 있지만, 그럴수록 도메인 지식이라도 잘 이해해야 한다. 도메인 지식도 없고, 개발도 잘 못하면 그건 망하는 길이다.)


(도메인 지식 부족한 상태에서)1주차 때 구현한 애그리거트는 아래와 같다. (동선을 값객체로 구현했었다. 또한, 확진자와 동선이 하나의 애그리거트로 묶여 있다.)




1)

1주차에서는 동선을 값객체로 정의하였지만, 도메인 지식을 다시 검토한 결과 역학조사 시스템에서의 동선은 개념적 식별성을 갖는다. 그래서, 동선은 엔티티로 구현해야 한다.


2)

확진자 정보와 동선 정보는 같이 추가되지 않는다. 확진자 정보는 선별진료에서 확진 판정을 받은 사람으로서 코로나 검사 결과가 양성으로 나오면 등록된다. 반면에, 동선 정보는 확진자 정보를 기본으로, 역학조사가 완료되는 시점에서 등록된다. 즉, 확진자 데이터와 동선 데이터는 같이 추가&변경 되는 데이터가 아니기 때문에, 하나의 애그리거트로 묶을 수 없다. 


3)

동선 정보와 밀접접촉자 정보는 같이 추가되고, 같이 변경된다. 그래서 하나의 애그리거트로 묶는다.


변경된 설계는 아래와 같다. (필자의 역량부족으로 인해서, 또 바뀔수도 있다...)

(이쯤에서.. 이해가 잘 안되면, 그냥 최범균님의 ddd start 3장을 읽자.)


도메인 지식에 의해서, 확진자 애그리거트 와 동선 애그리거트를 분리하였다. 사실, 소프트웨어를 생각하지 않고, 일반적인 상식에 의하면 "확진자는 동선 정보를 포함한다" 라고 생각할 수도 있다. 그래서, 개발자의 개인적인 생각에 의해서, 확진자는 동선 정보를 포함하니깐, 같은 애그리거트로 묶을 수 있다고 생각할수도 있다.


하지만, 포함관계라고 해서 같은 애그리거트로 묶는 것이 아니다.


같이 추가되는 데이터도 아니고, 같이 변경되지도 않는 데이터이기 때문에 별도의 애그리거트로 분리해야 한다.


동선과 밀접접촉자는 일단 한 애그리거트로 묶었다. (3주차에는 분리된 버전도 설명한다..) 동선 데이터와 밀접접촉자 데이터를 같이 추가&변경된다는 요구사항을 수렴하였다. 사실, 반버논의 애그리거트 기본 규칙에 의하면, 작은 애그리거트를 설계하는게 좋다고 했다. 그래서, 최종적으로 애그리거트에는 하나의 엔티티가 존재하는것이 가장 이상적일 것이다. 2개 이상인 경우는 드물다고 한다. 필자가 뭔가 잘못 설계한 걸까...


그리고, 애그리거트 사이의 참조는 반드시 루트 애그리거트의 식별자를 참조한다.



더이상 자세한 설명은 생략한다...



6.2 애그리거트 샘플(2)

6.1의 경우와 반대로, 만약 동선 데이터와 밀접 접촉자 데이터가 따로 등록된다면 어떻게 설계하면 될까? 애그리거트를 분리해야 한다. 해당 내용은 3주차에 좀 더 설명하겠다...  


6.2 애그리거트 루트

애그리거트 루트는?

 - 애그리거트 전체를 관리하는 주체이며, 애그리거트의 일관성이 깨지지 않도록 하는 중요한 역할을 한다.


애그리거트 루트는 아래와 같다.

- 확진자 애그리거트 : 확진자 엔티티

- 동선 애그리거트 : 동선 엔티티

- 역학조사관 애그리거트 : 역학조사관 엔티티


확진자 애그리거트만 살펴보자.

애그리거트 루트는 애그리거트의 일관성을 관리하는 주체이기 때문에, 루트가 아닌 다른 객체가 애그리거트에 속한 객체를 직접 변경해서는 안된다. 예를 들어서, 루트인 확진자 엔티티에서는 아래와 같이 주소를 변경할 수 있었다. 그리고, 주소 정보에 대한 유효성 검증이라는, 중요한 도메인 로직이 포함되어 있다.

하지만, 애그리거트 루트가 아닌 다른 객체에서, 애그리거트에 속한 객체(주소) 를 직접 변경하는 구문은 적합할까?(아니다)

위와 같은 구문은, 주소 정보에 대한 유효성 체크라는 도메인 로직을 전혀 수행하지 않는다.
(사실, Address 는 불변객체로 정의하였기 때문에 속성을 변경할 수 없다. 불변객체로 정의하였다면 애초에 불가능한 로직이다.)


위 내용은 최범균님의 ddd start 78 페이지에서 더 상세하게 확인할 수 있다. 꼭 읽어보길 바란다.



6.3 애그리거트 사이 참조

동선 애그리거트는 확진자 애그리거트를 참조할 때, 확진자의 식별자를 사용해서 참조한다.


AS-IS) 다른 애그리거트에 대한... 식별자 참조가 아닌, 객체 참조인 경우

아래와 같이 동선 애그리거트가 확진자 애그리거트 객체를 직접 참조하게 구현할수도 있지만...

이 경우, 다른 애그리거트 사이에서 객체를 직접 참조하게 된다.

즉, 동선에서 확진자 객체를 직접 참조할 수 있다.

바람직하지 않다...



TO-BE) 다른 애그리거트에 대한... 식별자 참조

애그리거트 사이에서는 식별자를 사용해서 참조해야 한다.

식별자 ID 를 참조하기 때문에, 아래와 같이 코딩을 해야 한다.


고민이다.

어떤 방법이 더 좋은지는 논란의 여지가 있을수는 있다. 객체를 직접 참조하는 것이 직관적일 수 있다. 선택은 각자 개발자가 알아서 하면 될것 같다.


"도메인 주도 설계"에서는,

다른 애그리거트의 참조는 반드시 식별자를 사용해야 한다고 가이드한다.


6.4 두 개의 애그리거트를 동시에 변경해야 하는 경우

두 개의 애그리거트롤 동시에 변경해야 하는 경우에는, 반드시 트랜잭션을 사용해야할 것이다. 3주차 때 다시 공부해보자.


6.5 애그리거트 하나에 Repository 하나

애그리거트에는 단 하나의 Repository 를 만든다. 애그리거트에서 가장 중요한 애그리거트 루트에 해당하는 리포지토리를 만들면 된다.

4주차에서 설명하겠다.


6.6 애그리거트 의존성

애그리거트는 리포지토리를 참조해서는 안된다. 상세한 설명은 5주차에 설명하겠다.


6.6 3주차에서 계속

애그리거트에 대한 내용이 더 남았지만, 글이 너무 길어졌다. 2주차는 이정도로 마무리하고 3주차에서 이어서 진행하기로 한다.


3주차에서는,

- 애그리거트 나머지

- 도메인 서비스

- 애플리케이션 응용서비스

- 테스트 코드 작성하기


필자의 샘플 코드는 참고만 하길 바라며, 각자 다른 주제로 샘플 코드를 작성하는게 좋을 것 같다. 이해하기 어렵다면, 최범균님의 ddd start 의 "주문 도메인"으로 공부하는게 좋을 것 같다.


레퍼런스

[1] 도메인 주도 설계 핵심 (반 버논 지음, 에이콘 출판사)

[2] 도메인 주도 설계 철저 입문 (나루세 마사노부 지음, 위키북스)

[3] 도메인 주도 설계 - 소프트웨어의 복잡성을 다루는 지혜 (에릭 에반스 지음, 위키북스)

[4] DDD START! 도메인 주도 설계 구현과 핵심 개념 익히기 (최범균 지음, 지앤선)

                    

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari