직접 참조와 간접 참조의 구분?

Picbel·2023년 1월 21일
2

Architecture

목록 보기
2/3
post-thumbnail

이번 포스팅은 도메인을 설계하다보면 느끼게되는 각 도메인이 서로를 어떻게 참조해야하는지에 대한 기준이 무엇인가에 대한 고민을 작성하여보겠습니다.
(C나 C++에서 메모리 주소관련한 키워드의 설명이 아닙니다.)
단순한 제 의견일 뿐 잘못된 관점일수 있으니 많은 지적 부탁드립니다.

DDD관련한 책을 읽다보면 애그리거트에 관한 개념이 나옵니다.

애그리거트는 관련 도메인을 하나의 군집으로 묶은 것

이 개념에서 추가적으로 파생되는것이 루트(root)애그리거트개념입니다.
루트 엔티티는 애그리거트의 대표 엔티티로, 애그리거트에 속한 엔티티는 루트 엔티티에 직접 혹은 간접적으로 속합니다.

개념적으로만 생각하면 도메인 설계시에 큰 문제가 없을것 같지만 기준이 모호해지는 순간이 오게됩니다.
주문과 상품이라는 각각의 도메인이 있다 가정하여 보겠습니다.
대략적인 요구사항은 다음과 같습니다

  • 유저는 주문을 통해 상품을 구매 할 수 있습니다.
  • 상품이 구매되면 상품의 재고가 한개 차감됩니다.
data class Item(
	val id : Long,
    var stock : Long,
    val amount : BigDecimal
    ...
)

data class Order(
	val id : Long,
    val items : ???
)

주문 도메인에 상품의 정보를 표현할려면 어떻게 해야할까요?

직접참조의 경우

직접참조의 경우엔 다음과같습니다

data class Order(
	val id : Long,
    val items : List<Item>
)
  1. 주문에서 item 정보를 수정 할 수 있다.

    val order : Order = ...
    order.items.first.stock -= 1

    이런식으로 주문 도메인에서 item 정보 수정이 가능해집니다.
    얼핏보면 재고 차감 요구사항을 해결하기 아주 좋아보이지만 2번 문제가 발생합니다.

  2. 주문 영속성 계층에서 items도 영속화의 책임을 지게됩니다.
    DDD 설계 중 영속화 계층은 root 애그리거트당 하나 씩 만든다는 원칙이 있습니다.
    그런데 위의 방식으로 설계시 주문 영속계층에서 item의 정보까지 관리해야한다는 문제가 있습니다.

  3. 테스트시 객체 정보의 복잡화
    테스트시 order객체를 만들때 item정보까지 전부 다 입력해야합니다.
    주문이 모든 item의 정보를 많이 사용하는것은 아니지만 전부 입력해야합니다.

위 방법은 DDD에서 권장하는 설계 원칙하고도 잘 맞지 않고 테스트의 복잡성을 시킨다는 문제가 있습니다.
그래서 도메인 모델을 루트로 묶을때는 정말 신중하게 고민하고 정말 root에 묶일 필요성이 있는지 꼭 생각해야합니다.


간접참조의 경우

data class Order(
	val id : Long,
    val itemIds : List<Long> // items의 식별자
)

직접참조와 달리 Item객체를 직접 참조하는것이 아니라 item의 식별자를 프로퍼티로 들고있어 도메인간의 연결은 유지하지만 직접 접근은 불가능합니다.
장점으로는 직접참조의 문제 1,2,3을 전부 해결 할 수 있습니다.
주문에서 item정보를 조회할려면 itemIds를 통해 상품 영속 계층을 이용해야합니다.

 val order = orderRepo.getById(id)
 val items = itemRepo.findAllByIds(order.itemIds)

위와 같은 형태를 띄우게 됩니다.
id기반으로 도메인을 조회하는것은 응용서비스에서 영속계층을 통해 해당 문제를 해결 할 수 있습니다.
하지만 이방법도 은탄환이 아닙니다.
도메인을 쪼개고 루트가 너무 정보가 빈약하면 도메인 모델의 표현력이 약해진다는 문제가 있습니다.

예시로 주문 정보와 배송지 정보가 있다 가정하겠습니다.
주문 도메인의 표현력을 훼손하면서 까지 배송지 정보를 간접참조 해야할 이유가 있을까요?

결론

직접참조와 간접참조를 결정짓는 부분은 도메인 모델의 변경의 책임을 누가 져야하는것 인가에 관한 문제라고 생각합니다.
즉 관리에 대한 주체를 누가 할 것인지 정하는 문제라고 할 수 있습니다.
주문내용이 변한다고 상품이 변하는 책임을 지는것은 주문 도메인 책임은 아니라 판단하여 상품이 주문의 하위 도메인모델로 속할 이유는 없다고 생각됩니다.
하지만 재고같이 상품쪽의 정보이나 주문에 의해 변경해야하는 경우에는 별도의 도메인서비스를 이용하거나 도메인 이벤트를 발행하여 도메인을 변경합니다.

좋은 도메인 모델 설계를 할려면...

결론을 보시고도 아리송 할 수 도있습니다.
도메인의 책임을 나눈다는게 쉽지도 않을 뿐더러 나 자신이 도메인전문가가 아닌경우가 많기때문입니다.
또는 정말 양쪽 도메인에 딱 걸쳐있는 애매한 문제가 존재 할 수 도있습니다.
이럴때 마틴 파울러 선생님이 한 명언을 하나 생각하시면 좋을 것 같습니다.

"컴퓨터가 이해하는 프로그래밍은 누구나 할 수 있지만 사람이 이해할 수 있는 프로그래밍은 아무나 할 수 없다"

우리가 DDD를 하는 이유는 UL(UBIQUITOUS LANGUAGE)을 통해 개발자가 아닌 다른 직군의 도메인 전문가들도 이해 할 수 있는 프로그램을 만드는것을 목적으로 해야한다고 생각합니다.
다른 직군의 동료들하고 논의하며 각 경계를 정하여 모두가 도메인의 구현을 공감 및 이해 할 수 있는 도메인이라면 훌륭하다 생각합니다.

번외로

저는 애매 하다싶으면 간접참조 + (이벤트 발행 or 도메인서비스)를 많이 사용하긴합니다.
이게 추후에 변경하기엔 더 쉽거든요

profile
Software Developer

0개의 댓글