[DDD] Entity, ValueObject에 대해서
※ 일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.
DDD의 구현 패턴으로 Entity와 ValueObject이라는 것이 있다. 이번 포스트에서는 각각의 개념과 사용법에 대해서 소개하려고 한다.
아래의 이미지를 이용해서 설명할 예정이다.
어떤 목적으로 만는가?
DDD의 접근법은 아래의 두 가지 스텝이 있다.
- 도메인(소프트웨어화 대상의 세계)에 대해, 시스템으로 사용하기 위한 모델을 작성한다.
- 모델을 소프트웨어(코드)로 만들어간다.
DDD에서는 이 두 번째 스텝을 위해 아래의 네 가지를 정의하고 있다.
- Entity
- Value Object
- Domain Service
- Domain Event
이 중에서 모델을 "오브젝트(값과 행동을 가진 것)으로 표현하는 것"이 Entity와 Value Object 이 두 가지가 된다.
DomainService와 DomainEvent
오브젝트로서 표현하지 않는 이 두가지에 대해서 간단히는 설명해두고자한다.
DomainService는 모델을 오브젝트로서 표현하기에는 무리가 있는 것들을 표현하기 위해 사용한다. 예를 들어, 집합에 대한 조작등과 관련된 것이다.
조금 더 구체적으로 예를 들자면, "예약"이라는 오브젝트 자체가 있다고 하자. "지정된 특정 시간대에 예약이 비어있는가?"라고 물었을 때, 그에 대한 대답을 예약 오브젝트 자체가 하도록 모델링한다는 것은 무리가 있다. 자기자신의 예약 시간을 알고 있다고 한다고 해도, 그 외의 오브젝트의 상태에 대해서는 정보를 가지지 않고 있기 때문이다.
이 경우에는 DomainService이라는 것을 이용한다. 그러나, DomaiService는 무심코 신청같은 작성법이되어버리기 때문에, 가능하면 최대한 Entity와 Value Object로 표현할 수 없는지 검토하고 그럴 수 없을 때에만 사용하는 것이 좋다.
DomainEvent는 "예약이 진행됐다"등의 상황을 모델링한다. 이 모델을 바탕으로 다른 도메인 서비스등에서 다른 처리를 실시하는 등에 사용된다. 그러나, 다른 세 개에 비해서 응용편이므로, 이 포스트에서는 구체적인 이야기는 하지 않도록 하겠다.
Entity와 ValueObject의 차이
동일하게 모델을 오브젝트로 표현하기 위한 것인데 왜 굳이 나눴을까?
Entity | ValueObject | |
동일성 판정 | 식별자가 동일하면 동일 | 가지고 있는 속성이 모두 동일해야 동일 |
가변성 | 가변 (생성~변이한다는 라이프 사이클을 가진다.) |
불변 (생성후에 파기될뿐이다.) |
Entity
Entity의 동일성 판정과 가변성
사원이라는 Entity에 대해서 생각해보자.
홍길동이라는 사원은 어떤 회사에 있어서 사원 번호라는 식별자 123으로 동일성이 판정된다. 홍길동은 부서가 바뀌어도, 소유하고 있는 돈이 바뀌어도, 체중이 바뀌어도 동일한 홍길동으로 다른 사람이 아니다. 그리고, 이러한 속성이 바뀌는 것은 속성 자체가 본질적으로 가변적이기 때문이다.
한편, 이름은 같지만 새로운 사원이 들어왔으면 사원 번호는 456이 할당됐다. 이 사람과 첫 번째 홍길동이 부서, 소유하고 있는 돈, 체중이 전부 똑같다고 하더라도 다른 사람이다. 이러한 것이 Entity의 동일성 사고방식이다.
ValueObject
ValueObject의 동일성 판정
한편 돈에 대해서 생각해보자. 2개의 10원 동전이 나열되어 있다면, 이것을 "동일하다"라고 판정하고 싶은가? 이것은 문맥이나, 모델링의 목적에 따라 달라진다.
모델링시의 금전적 가치에만 관심이 있다면 두 개의 10원짜리 동전은 같다고 생각해도 무방하다. 예를 들어 홍길동이 1개째의 10원 동전을 가지고 있는 상황에서 두 개째의 10원 동전을 가졌을 때 홍길동의 소유 금액이 10원이라고 생각하지 않을 것이다. 이 경우 두 개의 동전을 구분할 필요가 없다. 이러한 경우와 같은 10원은 ValueObject로써 모델링하기에 적절하다.
혹시 조폐국이나 동전 수집가라고 한다면 사정에 따라 달라질지도 모른다. 각각의 10원 동전을 구분하고 다루고 싶은 경우가 있기 때문이다. 이것이 바로 방금 말한 "문맥, 모델링의 목적에 따른 것"이라는 의미이다. 따로 다뤄야할 필요가 있는 경우 돈을 Entity로 다루는 것을 검토하는 것이 좋겠다.
ValueObject의 불변성
홍길동이 10원을 소유하고 있었는데, 100원으로 늘었다고 치자. 이때 10원 동전 숫자 10에 취소선을 긋고 100으로 다시 적을까? 그러지는 않을것이다.
실제로는 10원 동전(이라는 ValueObject)를 100원 동전(이라는 ValueObject)로 변환할 것이다. 10원 동전은 제조됐을 때에 보유하는 금전적 가치는 확정되어 있으며, 나중에 어떤 일이 있어도 달라지는 것은 없다. 이러한 것이 바로 ValueObject가 불변하는 것을 의미한다.
Entity와 ValueObject의 관계
방금의 예에서 봤던 직원과 돈처럼 Entity가 자신의 속성으로 ValueObject를 유지하는 관계가 되는 것이 기본이다.
Entity 속성은 가변적이지만 ValueObject로써 가지는 속성의 값이 바뀔 경우에는 ValueObject 자체가 나태내는 값을 바꾸는 것이 아니라 새로운 값을 가진 ValueObject를 생성하고 이전의 ValueObject를 대체하는 방식으로 사용한다.
10원짜리 동전을 100원짜리 동전으로 대체한다는 것이 바로 이 비유가 된다.
설계한 의도
Entity와 ValueObject의 구별하는 장점
왜 이런 구분을 할까? 참고로 이후의 얘기는 개인적인 해석이다. 좋은 프로그래밍은 "불변할 수 있는 것은 최대한 불변하도록 하는 것이 좋다"라는 것에 있고, 모델 단계에서 부터 그 구별을 하는 것이 좋다라고 하고 있다.
특별히 신경쓰지 않는다면 사실 ValueObject를 사용하지 않고 모두 Entity로 모델링하고 구현하는 것은 가능하다. 단 그러면 불변이어야 할 부분이 가변이 되어 가독성이나 신뢰성이 떨어질 가능성이 있다.
Primitive형이 아닌 ValueObject로 설계하는 장점
primitive형과 비교하면 값 자체에 어떠한 동작을 하도록 함으로써 응집도를 높일 수 있다. 예를 들어 사원이라고 하는 Entity가 이메일 주소를 가지는 경우, 메일 주소의 서식 체크를 사원 Entity에서 가지는 것보다 메일 주소라고 하는 ValueObject를 생성하고 그것이 생성될 때의 로직(컨스트럭터 등)에서 서식 체크를 하도록 하는 것이 오브젝트가 가지는 값과 행동의 관련성이 가깝기 때문에 응집도가 높다고 할 수 있다.
참고자료
https://little-hands.hatenablog.com/entry/2018/12/09/entity-value-object