콘텐츠로 건너뛰기

DDD – Entities, Value Objects, Aggregates

Domain-Driven Design 모델링을 할 때 알아야할 가장 기본적인 요소들에 대해서 포스팅해보려고 한다. DDD의 전체적인 개념은 여기에 소개되어있다.

이번 포스팅에서는 세 가지 개념을 설명하려고 한다:

  • Entities
  • Value Objects
  • Aggregates and Roots

Entities

Eric Evan의 Domain-Driven Design 책에서:

Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity.

보통 object-oriented 방식으로 디자인할 때는 model들의 nouns와 verbs들을 정하면서 출발하게 된다. DDD에서 모델링을 할때는 먼저 Ubiquitous Language를 정하고 그에 맞춰서 model의 naming를 정하고 identity를 드러내게한다.

예를 들어서, Person이라는 객체에 대해서 생각해보자. Person이라는 struct엔 Name이라는 attribute가 있고 그 밖의 다양한 attribute들이 있을 수 있다. 여기서 만약 두 개의 Person객체가 있는데 두 객체의 Name이라는 attribute가 같다고 그 두개는 같은 객체라고 할 수 있을까? 꼭 그렇지만은 않을 것이다. Name이 같다고 할지라도 다른 Address, Age 등 다른 attribute가 다를 수도 있다.

위와 같은 상황에서 Person이라는 객체의 identity는 Name, Address 등과 같은 attribute에 의해서 정해지는 것이 아니다. Person이라는 객체는 그 객체를 유일하게 식별할 수 있는 ‘어떤 값’에 의해서 정해져야할 것이다. 그 ‘값’을 정하는 방법은 여러가지 방법이 있을 수 있고 또 시스템마다도 다를 것이다.

어떤 객체가 Entity인지 확인할 수 있는 가장 간단한 질문이 있다:

만약 같은 class의 두 instance가 다른 attribute들을 가지고 있지만 같은 identity 값을 가지고 있다면, 그 두 인스턴스는 같은 instance일까?

만약 이 질문의 대답이 “같다” 라면 instance와 그에 해당하는 class는 Entity로 생각해야한다.

Value Objects

Eric Evan의 Domain-Driven Design 책에서:

Many objects have no conceptual identity. These objects describe characteristics of a thing

모델링을 하다보면 Entity와는 다르게 어떤 object의 identity가 중요하지 않을 때도 있다. 그럴 땐 value object을 생각하면 좋다. 예를 들어, 만약 어떤 시스템에서 PaintBucket를 모델로 삼아야할 필요가 있을 때 Color와 같은 object를 value object로 생각하면 좋다.

PaintBucketColor를 확인할 때, Color의 identity 값은 필요 없다. 두 개의 Color의 색깔 attribute가 같다면 두 객체를 같은 것으로 생각해도 충분하다.

Entity는 attribute를 바꿔도 identity 값이 변하지 않는 이상 별로 문제가 없는 반면에 value object에서는 attribute를 바꾸는 것이 문제가 될 수도 있다. 왜냐하면 attribute 자체가 그들의 identity가 되기 때문이다. 그렇기 때문에 value object를 immutable하게 만드는 것도 value object를 모델링하는 방법이 될 수 있다. 또한 value object도 Entity와 마찬가지로 Ubiquitous Language에서 정의한 것들 중에서 개념적인 것을 나타내야하고 남용하지 않는 것이 중요하다.

Aggregates

실제 세계에서 많은 부분들은 서로 관계를 가지고 있다. 예를 들어, 어떤 사람이 신용 카드를 여러 장 가지고 있을 수 있다. 그런데 각 신용카드마다 소유주가 있고, 발급해준 회사가 다를 수도 있고 또 각 회사마다는 또 여러 계좌 정보를 가지고 있을 수도 있다. 이러한 수 많은 관계들을 전부 class로 나타내면 엄청 복잡해질 것이다.

이러한 각각의 개념적인 부분들을 entity로 나타낼 수 있을 것이다. 그런데 위와 같이 entity 사이의 관계가 복잡해지면 각각의 entity를 관리하기 힘들어진다. entity ‘A’가 entity ‘B’를 가지고 있는 구조가 있을 수 있다. 그런데 ‘A’가 operation을 하게 되면 ‘B’가 변하지 않는다는 보장을 쉽게 할 수 있을까? 갯수가 적을 때는 괜찮겠지만 조금만 복잡해지면 domain에서 난리가 날 수 있다.

aggregates는 한 개 또는 다수의 entity를 가지고 entity보다 큰 boundary를 만들어준다. aggregates가 감싸고 있는 모든 entity들은 변하지 않아야 하고 entity들이 operation을 했을 때 aggregate 내부의 entity들이 변하지 않아야한다. aggregate 마다 root entity가 있는데 aggregate 외부 객체들이 참조할 수 있는 유일한 entity이다. Eric Evan의 Domain-Driven Design 책에서 다음과 같은 규칙들을 지켜야한다고 명시했다:

  • Root entity는 global한 identity 값을 가지고 있고 aggregate 내부의 다른 entity들이 변하지 않았는지 체크해야한다.
  • Root entity는 global한 identity를 가지고 있다. aggregate 내부의 다른 entity들은 local한 identity를 가지고 있고 이 identity는 aggregate 내부에서만 유일하다.
  • Aggregate 외부에서는 root entity만 접근 가능하다. aggregate 내부의 다른 entity에 대해 접근하고 싶으면 root entity를 통해서 접근해야한다.
  • Aggregate Roots들은 database query를 통해서만 얻을 수 있다.
  • Aggregate에 대해서 delete operatation을 하면 aggregate 내부의 모든 entity를 삭제해야한다.

이런 aggregate들이 entity보다 더 큰 boundary를 만들어 줌으로써 모델링이 한결 간단해진다. 왜냐하면 위와 같은 규칙들을 지키도록 하면서 관계를 만들어야 하기 때문이다.

모든 관계들이 association을 통해서 만들어질 필요는 없다. 예를 들어 EmployeeManager의 관계에서 ManagerEmployee를 통해서 얻을 수 있고, Manager를 통해서 Employee를 얻으려면 EmployeeRepository를 통해서 가져오게 만들 수 있다. Employee를 aggregate root라고 하면 Manager를 직접 reference하는 것이 가능하기 때문이다.

Modeling and simplification

모델링을 할 때 우리는 실세계의 개념과 사물들을 모델로 삼고 각각의 그것들을 Entity나 Value Object(그리고 Service)들로 나타낼 것이다. 그리고 이러한 것들을 더욱 단순화하기 위해서 우리는 Aggregate와 Root를 사용한다. 그리고 각각의 모델들은 Ubiquitous Language로 표현할 수 있어야한다. 그래야 같이 개발하는 사람들이 다른 사람들이 작업한 코드를 보았을 때, 이 코드가 어떤 일을 하는지 알기 쉬워진다.

Reference

Leave a comment