이 글에서 얻는 것

  • 엔티티(Entity)와 값 객체(Value Object)를 “그럴듯한 정의”가 아니라 **실무 기준(식별/불변/수명)**으로 구분할 수 있습니다.
  • 애그리게이트(Aggregate)를 “객체 묶음”이 아니라 트랜잭션 일관성 경계로 이해하고, 경계를 잡는 기준이 생깁니다.
  • 바운디드 컨텍스트(Bounded Context)로 “같은 단어가 다른 의미가 되는 지점”을 분리해, 모델이 커질 때도 유지보수가 가능하게 만드는 감각을 얻습니다.

0) 도메인 모델링이 필요한 순간

처음엔 CRUD로 충분합니다. 그런데 아래가 보이기 시작하면 모델링이 필요해집니다.

  • “이 상태에선 이 작업이 되면 안 돼” 같은 규칙이 늘어난다(불변식)
  • 여러 테이블/객체를 동시에 맞춰야 해서 버그가 자주 난다(일관성)
  • 코드가 ‘데이터 옮기기’로만 구성되고, 규칙은 여기저기 흩어진다(빈약한 도메인)

도메인 모델링의 목적은 “멋있게 만들기”가 아니라, 규칙을 코드의 중심에 두고 변화 비용을 줄이는 것입니다.

1) 엔티티 vs 값 객체: 기준은 ‘식별’과 ‘불변’

1-1) 엔티티(Entity)

  • 핵심은 식별자(id) 입니다.
  • 속성이 바뀌어도 “같은 것”으로 취급됩니다(동일성).
  • 보통 수명이 길고, 상태 변화가 많습니다.

예: 사용자(User), 주문(Order)

1-2) 값 객체(Value Object)

  • 핵심은 값 그 자체입니다.
  • 보통 불변(immutable)으로 두고, 동등성(equality)을 값으로 비교합니다.
  • 도메인 개념을 캡슐화해 “의미 있는 타입”을 만들어줍니다.

예: Money(amount, currency), Email(value), Address(…)

실무 팁: 값 객체를 쓰면 “문자열 남발”이 줄고, 검증/정규화 로직이 한 곳으로 모입니다.

2) 애그리게이트: ‘트랜잭션 일관성’의 경계

애그리게이트는 한 번의 트랜잭션 안에서 반드시 지켜야 하는 불변식의 범위입니다.

  • 애그리게이트 내부의 변경은 “원자적으로” 일어난다고 가정합니다.
  • 외부는 애그리게이트 루트(Aggregate Root)만 통해서 접근합니다.
  • 다른 애그리게이트와의 관계는 보통 “객체 참조” 대신 “id 참조”로 둡니다(결합도 감소).

2-1) 경계를 잡는 질문 3개

  • 이 규칙(불변식)은 어떤 데이터가 함께 있어야 검증 가능한가?
  • 이 변경은 반드시 한 트랜잭션으로 묶여야 하는가?
  • 함께 잠그면(락) 병목이 되는가? 너무 큰 애그리게이트는 쓰기 경쟁이 심해집니다.

2-2) 예시(감각): 주문 애그리게이트

주문(Order)에서 흔한 불변식:

  • 결제 완료된 주문은 품목/수량을 바꿀 수 없다
  • 배송 시작 이후엔 배송지 변경이 제한된다

이런 규칙이 “주문”이라는 일관성 경계에 모이면, 서비스 계층에서 흩어진 if/else가 줄어듭니다.

3) 바운디드 컨텍스트: 같은 단어가 다른 의미가 되는 곳

“사용자”라는 단어도 컨텍스트가 달라지면 의미가 달라집니다.

  • 인증 컨텍스트의 사용자: 로그인/권한/세션
  • 커머스 컨텍스트의 사용자: 주문/배송/포인트

하나의 User 모델로 모든 걸 담기 시작하면 결국 거대한 엔티티와 결합도가 생깁니다. 컨텍스트 경계를 나누고, 컨텍스트 사이에는 DTO/ACL(anti-corruption layer) 같은 “계약”으로 연결하는 편이 안전합니다.

4) 자주 하는 실수(안티패턴)

  • 거대한 애그리게이트: “한 번에 다 맞추자”로 시작해 락 경쟁/성능 문제로 이어짐
  • 엔티티 남발: 값 객체로 충분한데 id를 붙여 엔티티로 만들어 복잡도가 증가
  • 외부에서 내부 상태를 직접 조작: 루트를 우회하면 불변식이 깨질 확률이 커짐
  • ORM 그래프 과의존: 애그리게이트 간 연관관계를 객체 그래프로 엮어 N+1/순환 의존/삭제 폭탄이 발생

5) 실무 적용 루틴(추천)

  1. 유스케이스를 먼저 적는다(무슨 명령을 처리하는가)
  2. 불변식을 뽑는다(“항상 참이어야 하는 규칙”)
  3. 불변식을 지키기 위한 최소 범위를 애그리게이트로 묶는다
  4. 외부 연동/비동기는 도메인 이벤트로 분리한다(애그리게이트는 작게 유지)

연습(추천)

  • “게시글/댓글/좋아요” 같은 간단한 도메인에서 불변식 5개를 뽑고, 애그리게이트 경계를 잡아보기
  • 값 객체로 만들면 좋은 개념(Email, Money, PhoneNumber 등) 3개를 골라 검증/정규화 로직을 캡슐화해보기
  • 애그리게이트 간 연관관계를 “객체 참조” 대신 “id 참조 + 조회는 별도”로 바꿨을 때 장단점을 정리해보기