> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# ID로 다른 애그리게잇을 참조하라
- URL: https://www.popit.kr/reference-other-aggregates-by-id/
- Published: 2019-03-05T00:27:30.000Z
- Updated: 2019-03-08T04:10:20.000Z
- Author: 유 영모
- Tags: #소프트웨어, DDD, JPA, Hibernate, 도메인 주도 설계, Aggregate, 도메인 주도 설계 구현, IDDD

필자는 [지난 글](https://www.popit.kr/one-repository-per-aggregate/) 전반부에서 [도메인 주도 설계Domain-Driven Design](http://www.yes24.com/Product/goods/5312881?scode=032&OzSrank=1&ref=popit.kr)에서 말하는 [구조물 중 하나](https://en.wikipedia.org/wiki/Domain-driven%5Fdesign?ref=popit.kr#Building%5Fblocks)인 조립물AGGREGATE\[1\]에 대해 이야기했다. 조립물을 구현하다 보면 다른 조립물을 참조해야 할 때가 있다. 이를 구현하는 방식은 크게 조립물의 루트 엔터티Root Entity 객체를 참조(포인터) 하는 것과 식별자로 어디서든 쓸 수 있는 값을 사용하는 방법(ID)이 있다. [도메인 주도 설계 구현Implementing Domain-Driven Design](http://www.yes24.com/24/Goods/25100510?Acode=101&ref=popit.kr)의 저자 반 버논Vernon, Vaughn은 ***"ID로 다른 애그리게잇을 참조하라"***라고 말했다. 이 글은 [지난 글](https://www.popit.kr/one-repository-per-aggregate/)에 이어 주문 도메인 예시를 통해 ID로 다른 조립물을 참조하는 방식에 대해 설명한다.

# 주문 예시

아래와 같이 Order 조립물과 Customer 조립물이 존재하고 Order 조립물이 Customer 조립물을 참조한다고 생각해보자.

![image2019-2-20_10-4-12](https://www.popit.kr/content/images/wp/2019/02/image2019-2-20_10-4-12-1024x603.png)

Customer 조립물은 [JPAJava Persistence API](https://en.wikipedia.org/wiki/Java%5FPersistence%5FAPI?ref=popit.kr) 코드로 아래와 같이 구현할 수 있다.(Order 코드는 [이전 글](https://www.popit.kr/one-repository-per-aggregate/)을 참조하라)

```java
@Entity
@Table(name = "customers")
public class Customer {
  @Id
  @GeneratedValue
  private Long id;
 
  private String name;
 
  @OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
  private List<BillingAddress> billingAddresses = new ArrayList<>();
  // ...
}
 
@Entity
@Table(name = "billing_addresses")
public class BillingAddress {
  @Id
  @GeneratedValue
  private Long id;
 
  private String zipCode;
 
  @ManyToOne
  @JoinColumn(name = "customer_id", referencedColumnName = "id")
  private Customer customer;
 
  // ...
}
```

# 객체 참조

객체 참조(포인터) 방식은 Order 조립물 루트 엔터티에 참조할 Customer 조립물 루트 엔터티를 추가함으로써 구현할 수 있다.

```java
@Entity
@Table(name = "orders")
public class Order {
  // ...
  @ManyToOne
  @JoinColumn(name = "customer_id", referencedColumnName = "id")
  private Customer customer;
   
  // ...
}
```

Order 조립물과 Customer 조립물은 직접 연결되기 때문에 Order 조립물을 생성, 수정할 때 Customer 조립물 객체 참조가 필요하다.

```java
Customer customer = customerRepository.findById(customerId);
 
Order order = new Order.Builder()
    .customer(customer)
    .shippingAddress(new ShippingAddress("12345", "Yoo Young-mo"))
    .addLineItem(new LineItem("P-0001", "상품 A", 1000l, 2))
    .addLineItem(new LineItem("P-0002", "상품 B", 2000l, 1))
    .addOrderPayment(new CreditCardPayment(2000l, "1234-123"))
    .addOrderPayment(new CreditCardPayment(2000l, "010-0000-0000"))
    .build();
```

데이터베이스에서 Order 조립물 객체 인스턴스를 가져올 때\[2\] Customer 조립물을 함께 가져오기 때문에 Order 조립물만 가져오는 것보다 더 많은 시간과 메모리를 사용한다.

또한, Order 조립물은 언제 참조하게 될지 모르는 Customer 조립 객체 참조를 항상 가지고 있어야 한다. 이 글에서는 이해를 위해 단순하게 Order 조립물이 하나의 객체 참조만 가지고 있지만 실제로는 여러 개의 조립물 객체 참조를 가지는 경우가 많다.

# 전역 고유 식별자 참조

반 버논은 그의 책에서 전역 고유 식별자를 이용하여 다른 애그리게잇을 참조하라고 말했다.

> 외부 애그리게잇보다는 참조를 사용하되, 객체 참조(포인터)를 직접 사용하지 말고 전역 고유 식별자를 이용하자. - 도메인 주도 설계 구현, 465 쪽

이전 코드를 아래와 같이 리팩토링 할 수 있다.

```java
@Entity
@Table(name = "orders")
public class Order {
  // ...
  private Long customerId;
}
```

```java
Order order = new Order.Builder()
      .customerId(customerId)
      .shippingAddress(new ShippingAddress("12345", "Yoo Young-mo"))
      .addLineItem(new LineItem("P-0001", "상품 A", 1000l, 2))
      .addLineItem(new LineItem("P-0002", "상품 B", 2000l, 1))
      .addOrderPayment(new CreditCardPayment(2000l, "1234-123"))
      .addOrderPayment(new CreditCardPayment(2000l, "010-0000-0000"))
      .build();
```

객체 참조에 비해 조립물 간의 경계가 명확해지며, 성능 면에서 이점이 있다.

> 추론 객체 참조inferred object reference를 가진 애그리게잇은 참조를 즉시 가져올 필요가 없기 때문에 당연히 더 작아진다. 인스턴스를 가져올 때 더 짧은 시간과 메모리가 필요하기 때문에, 모델의 성능도 나아진다. - 도메인 주도 설계 구현, 466 쪽

# 모델 탐색

Order 조립물에서 주문(placeOrder)을 하려면 Customer 조립물 객체 참조가 필요하다고 가정해 보자. ID로 참조하고 있는 상황에서 어떻게 Customer 조립 객체 참조를 찾을까?

> 애그리게잇의 행동을 호출하기에 앞서 리파지토리나 도메인 서비스를 통해 의존 관계에 있는 객체를 조회하는 방법도 추천할 만하다. 클라이언트 에플리케이션 서비스는 이를 제어하며 애그리게잇으로 디스패치할 수 있게 된다. - 도메인 주도 설계 구현, 467 쪽

아래와 같은 코드로 구현할 수 있다. 애플리케이션 서비스에서 Order 조립물의 placeOrder 메서드(행동)를 호출하기 앞서 리파지토리REPOSITORY를 사용하여 Customer 조립 객체를 얻는다.

```java
@Service
public class OrderService {
  // ... 
  @Transactional
  public void placeOrder(final Long orderId) {
    Order order = orderRepository.findById(orderId);
    // ID를 사용하여 Customer 획득
    final Long customerId = order.getCustomerId();
    Customer customer = customerRepository.findById(customerId);
    // 주문
    order.placeOrder(customer);
    // ...
  }
}
```

# 주석

\[1\] 조립물이라는 표현은 글을 검토해 주신 [안영회](https://www.popit.kr/author/tony/) 님이 제안한 애그리게잇을 대신하는 우리말 표현이다. 자세한 내용은 [Aggregate를 애그리게잇 대신 조립물로 쓴 사연](https://www.popit.kr/why-i-translate-aggregate-as-assembly/) 참고하라. \[2\] 도메인 주도 설계에서는 이를 재구성reconstiution이라고 부른다.

# 참고 자료

- [도메인 주고 설계 구현 - 반 버논](http://www.yes24.com/Product/Goods/25100510?Acode=101&ref=popit.kr) 저
- [도메인 주도 설계 - 에릭 에반스](http://www.yes24.com/24/goods/5312881?scode=032&OzSrank=1&ref=popit.kr) 저