본문 바로가기
★ 프로젝트 + 트러블 슈팅 ★

[DDD] 도메인, 바운디드 컨텍스트, 애그리거트 개념 정의

by 리승우 2024. 11. 5.

현재 조직 내에서 특정 도메인을 DDD (도메인 주도 개발)와 헥사고날을 접목하여 Java API로 재구축하는 프로젝트를 맡았다.

 

해당 프로젝트를 진행하면서 느낀 가장 큰 점은,

프로젝트 인원끼리 특정 용어를 사용하여 의사소통할 때, 동일한 의미로 알고 있지 않으면 의사소통에 있어서 큰 어려움을 겪는 다는 것이였다.

 

그런 의미로 추후 다른 도메인으로 API 재구축을 진행하게 될 인원들을 위해,

그리고 현재 내부 인원들이 계속 통일되게 인지하고 있으면 좋을 주요 개념들에 대해 미리 해당 페이지에 정리해둔다.

 

도메인(Domain)

정의

  • 사전적 의미 : 영토, 분야, 영역, 범위, 책임범위
  • 인터넷 용어 : 인터넷 주
  • DDD 용어 : 소프트웨어로 구현하고자 하는 현실의 문제 또는 상황
    • 웹소설 플랫폼을 위한 소프트웨어를 만든다면, 이 소프트웨어가 다뤄야 하는”도메인” 은 웹소설 서비스 제공이 된다.


상위 도메인(Core Domain)

정의

  • 프로젝트에서 가장 핵심이 되는 도메인, 이 영역이 해결하는 문제는 서비스에 가장 큰 가치를 표현한다.
    • 웹 소설 플랫폼

하위 도메인(Sub Domain)

정의

  • 상위 도메인을 지원하거나, 부수적인 역할을 하는 문제 영역 단위
    • 작품, 회원, 게시판 등

도메인 모델(Domain model)

정의

  • 도메인을 더 잘 이해하기 위해 개념화 한 모델

⚠️[참고]

모델 작성에 절대적인 기준은 따로 없다!

어떤 것을 표현하고 싶은지에 따라 도메인 모델을 선택하면 된다.

가령, 객체와의 관계를 표현하고 싶을 때는 객체 모델을 이용하여 도메인을 표현하면 된다. 만약 어떤 도메인을 동작 흐름에 따라 표현하고 싶다면, 상태 다이어그램을 통해 도메인 모델을 도출하면 된다. 관계가 중요하다면 관계 다이어그램을 통해 도메인 모델을 도출하면 된다.

 

⚠️[참고]

도메인 모델은 도메인 자체를 이해하기 위한 개념 모델일 뿐이다!

도메인 모델을 통해 바로 코드를 작성할 수는 없다. 구현하려는 기술에 맞는 구현 모델이 따로 필요하다. 너무 어렵게 생각하지 말고 도메인을 이해하기 위한 목적으로 이해하자.

도메인 모델 도출해보기

방법

  1. 만들고 싶은 웹 사이트를 생각해보자
    • 인터넷 강의 사이트
  2. 해당 사이트에서 필요로 하는 기능을 정의해보자
    • 강의를 수강하고 싶은 사람은 사이트에서 회원 가입을 할 수 있다 → 회원 기능
    • 회원이 신청한 강의를 결제할 수 있어야 한다 → 결제 기능
    • 회원이 듣고싶은 강의에 대해 수강신청을 할 수 있어야 한다 → 주문 기능
    • 수강을 마친 회원은 강의평을 남길 수 있어야 한다 → 강의평 기능
    • 강사는 강의를 제작하여 업로드 할 수 있어야 한다 → 강의 기능
    • 관리자는 선생님 정보를 관리할 수 있어야 한다 → 선생님 기능
    • 결제된 강의금을 확인하고, 강사에게 지불할 수 있어야 한다 → 정산 기능

참고 - 요구사항 정의에 대해 학습하면 본 내용을 더 잘 이해할 수 있다.

 

⚠️[참고]

온라인 강의를 제공하는 도메인의 하위 도메인 모델

❗️도메인 이름 정의시 행위가 포함되지 않도록 유의할 것 단어와 행위가 결합되는 경우, 2가지 이상의 정의가 내포될 수 있음 ex) 수강신청 ⇒ 학습을 관리하는 기능과, 결제 플로우를 담당하는 기능이 중복되어 있다 ⇒ 학습, 주문 으로 도메인 분리

 

 

바운디드 컨텍스트란?

  • 바운디드 컨텍스트(Bounded Context)는 도메인 주도 설계(DDD)에서, 특정 도메인 간 경계를 나누는 것을 의미한다.

바운디드 컨텍스트 예시

  • 하나의 큰 도메인은 작은 여러 개의 하위 도메인을 가질 수 있다. 이 과정에서 각자 다른 도메인이지만, 같은 용어를 다른 의미로 사용하는 경우가 있다.
  • 조아라(웹 소설 플랫폼) 도메인이 있다고 가정하자
  • 조아라 도메인을 여러 하위 도메인으로 나눈다면 회원 도메인, 작품 도메인, 게시판 도메인 등으로 나눌 수 있다.

회원 도메인

  • 회원 가입, 아이디 및 비밀번호 찾기, 회원 정보 변경 기능을 제공한다

작품 도메인

  • 회원은 작품 등록, 작품 수정, 작품 삭제, 작품 조회 기능을 제공한다.

게시판 도메인

  • 회원은 게시글 등록, 게시글 삭제, 게시글 수정, 게시글 목록 조회, 게시글 상세 조회 기능을 제공한다

  • 세 도메인 모두에서 회원이라는 용어가 사용되며, 조아라 기능을 이용하는 회원이라는 관점에서는 같은 의미로 이해될 수 있다. 그러나 도메인 모델로 도출하는 과정에서 각 도메인마다 회원이 다른 의미를 가질 수 있다.
  • 회원 도메인에서의 회원은 회원 정보 관리에 초점을 둔다
  • 그러나 작품과 게시판 도메인에서의 회원은 작품을 등록한 작가, 게시글을 등록한 게시글 작성자를 의미하고 회원 정보 자체에 초점을 둔다.
  • 이처럼, 같은 용어를 사용하더라도 의미는 각 하위 도메인에 정의한 모델에 따라서 달라질 수 있다.
  • 이런식으로 용어의 충돌 및 용어의 재정의가 필요한 부분 단위로 바운디드 컨텍스트를 나눌 수 있다.
  • 바운디드 컨텍스트를 나누면 다음과 같다
    • 회원 바운디드 컨텍스트
    • 작품 바운디드 컨텍스트
    • 게시판 바운디드 컨텍스트
  • 작품 바운디드 컨텍스트에서 회원은 작가로 해석되고, 게시판 바운디드 컨텍스트에서 회원은 게시글 작성자로 해석된다.
    • 일반적으로 하위 도메인이 도출되면 하위 도메인 범위와 같은 바운디드 컨텍스트 경계가 생성되는데 필요에 따라서 도출된 하위 도메인을 더 작은 하위 도메인으로 나누어서 바운디드 컨텍스트를 나누기도한다.
  • 바운디드 컨텍스트를 나누면 컨텍스트 단위로 개발팀이 만들어지기도 하며, 프로젝트, 모듈, 패키지를 나누는 기준이 되기도한다.

바운디드 컨텍스트 구현

  • 하나의 바운디드 컨텍스트는 도메인 계층 뿐만이 아닌 표현계층 부터 인프라 계층, 데이터 저장소 까지 전 계층을 포함한다.
  • 도메인 모델이 변경되면 데이터 구조가 변경되고 이에 따라서 데이터베이스 테이블 스키마가 변경 될 수 있다. 그래서 데이터 저장소 또한 바운디드 컨텍스트에 포함해야 한다.
  • 회원, 작품, 게시판 별로 바운디드 컨텍스트를 구성한다고 했을 때, 아래와 같은 구조로 바운디드 컨텍스트들이 구성될 수 있다.

 

 

  • 얼핏 보면, 하나의 바운디드 컨텍스트가 하나의 도메인으로 구성되어 바운디드 컨텍스트와 도메인이 같은 뜻이라고 오해할 수 있다.
  • 하나의 도메인 모델을 하나의 바운디드 컨텍스트로 구현하는 것이 이상적이지만, 게시판 바운디드 컨텍스트와 여러 도메인이 있을 수 있다.

 

  • 게시판 기능에 하위 기능으로 묶여있다고 판단되면 전략적으로 하나의 바운디드 컨텍스트로 묶기도 한다
    • 하나로 묶을 수도 있지만 게시판 바운디드 컨텍스트, 게시물 바운디드 컨텍스트, 댓글 바운디드 컨텍스트, 게시물 추천 바운디드 컨텍스트, 게시물 조회수 바운디드 컨텍스트로 나뉘어 질 수도 있다
    • 바운디드 컨텍스트 분리 기준은 여러개가 있는데 각 바운디드 컨텍스트를 나눔으로서 팀 협업, 프로젝트 구조 (모듈, 패키지)가 결정될 수 있기 때문에 전략적으로 협의하에 나누어야 한다.

바운디드 컨텍스트 간 관계

  • 바운디드 컨텍스트를 명확하게 분리했다면, 데이터 저장소를 공유하지 않게 된다.
  • 그렇게 된다면, 작품 컨텍스트에서 회원 정보가 필요할 경우, 회원 컨텍스트로 필요한 데이터를 요청해야한다.
  • 이 경우 작품 컨텍스트는 회원 컨텍스트에 의존하게 되는데, 이때 작품 컨텍스트는 하류 컴포넌트, 회원 컨텍스트는 상류 컴포넌트가 된다.
  • 상류 컴포넌트는 하류 컴포넌트에게 데이터를 공급하고, 하류 컴포넌트는 상류 컴포넌트가 제공한 데이터를 자신의 비지니스에 사용한다.
  • 위 같이 독립적인 바운디드 컨텍스트 간에 데이터를 공유하기 위해서는 기본적으로 상류 컴포넌트와 하류 컴포넌트가 주고 받는 데이터 구조를 맞춰야 한다.
  • 예를 들어, 작품 컨텍스트에서는 회원 바운디드 컨텍스트를 통해 회원ID, 회원 이름, 회원 전화번호, MBTI, 선호테마 받고 싶어 하는데, 회원 컨텍스트에서 회원ID, 회원 이름, 회원 전화번호 를 주면, 데이터 구조가 맞지 않아서 정상적인 로직을 수행할 수 없다.
  • 하류 컴포넌트 입장에서 상류 컴포넌트로 부터 받아야할 데이터가 존재하면 협의를 통해 데이터 구조를 정의해야한다.
  • 그런데 만약 상류 컴포넌트는 하나인데 하류 컴포넌트가 여러개 존재한다면 어떻게 될까?
  • 하류 컴포넌트가 원하는 구조가 각각 다르면 상류 컴포넌트는 하류 컴포넌트 별로 데이터 구조를 정의해야한다. 이는 상류 컴포넌트 입장에서 상당한 부담이 될 수 있다.
  • 이런 경우, 상류 컴포넌트에서 공통 데이터 구조를 하나로 정의해 하류 컴포넌트들에게 제공하는데 이러한 방식을 공개 호스트 서비스 라고 한다.

 

  • 조아라 도메인 예시

 


바운디드 컨텍스트 나누었을 때 얻는 장점

  • 바운디드 컨텍스트 별로 맥락에 맞게 일관적인 용어를 사용해서 이해도를 높일 수 있다.
  • 바운디드 컨텍스트를 나누면 하나의 컨텍스트를 수정하더라도 다른 컨텍스트에 영향을 주지 않는다.
    • 왜냐하면 각 바운디드 컨텍스트는 직접 의존하지 않고 인터페이스 혹은 API, 이벤트 방식으로 협력하고 있기 때문이다.
    • 물론 인터페이스, API 스펙 등이 수정되면 서로 영향이 가기는 하지만 이는 잘 수정되지 않고 수정 시 각 바운디드 컨텍스트 진형에 공유가 필요하다.
  • 바운디드 컨텍스트 단위로 업무를 나누면 작업을 병렬로 진행할 수 있으며, 팀원간 충돌이 줄어들고 개발 속도가 빨라지며 효율적인 협업이 가능하다.
  • 특정 기능에 대한 변경이나 확장이 필요할 때, 독립적인 컨텍스트로 관리된 부분만 수정하면 되므로 더 빠르고 유연하게 대응할 수 있다.
  • 바운디드 컨텍스트로 나뉜 기능들은 마이크로서비스와 같은 독립적인 서비스로 전환하기에 적합하다. 필요한 바운디드 컨텍스트만 독립적으로 확장하거나 배포할 수 있어 시스템 확장성이 좋아진다

 

 

애그리거트란?

  • 생명주기(생성/삭제)가 같으며, 서로 관련있는 데이터를 묶어서 관리하는 하나의 군집
    • 대체로 한 가지 주제를 중심으로 구성되어 있으며, 그 주제와 관련된 정보들을 모아둔 그룹
  • 복잡한 시스템을 단순하게 나누어 관리하는데 도움을 주기 위한 목적으로 사용하는 개념

애그리거트 구성 요소

 

Entity(엔티티)

  • ID(식별자) 값이 존재하는 객체
    • ID(식별자)가 존재한다는 것은, 상태를 가지고 있으며 시간의 흐름에 따라 변경될 수 있음을 의미함
    • 추후 시스템은 특정 시점에 Entity를 식별한 후 어떠한 명령 (Command)를 내림으로써 상태를 변경함
  • 애그리거트 내부에 Entity는 최소 1개 최대 N개까지 존재할 수 있음 (Aggregate Root 또한 Entity임)

VO(값 객체)

  • 속성 값이 존재하는 객체
    • Entity와 다르게, 식별자 개념보다는 속성 값 자체가 중요한 객체
    • 특정 값을 표현하는 것이 목적이며, 속성 값만 같다면 동일한 객체로 취급함

조아라 작품 애그리거트 예시

 

  1. Book 애그리거트 루트 (Entity)는 식별값 book_code (PK)를 가지고 있음
    1. 해당 값으로 Book 객체를 식별할 수 있음
  2. Price 값 객체(VO)는 Book 객체가 보유하고 있는 값을 의미함 (식별값이 없으며, 속성값만 보유)
    1. Price 구성요소 아래와 같음
      1. Amount = 작품 판매값
      2. Currency = 화폐 단위 (달러, 원화, 엔화 등)
      ⇒ 고유 식별자가 필요없고, 단순히 책의 속성을 나타내는 값 객체

⇒ 애그리거트는 Entity와 VO를 묶어 ‘완전한 하나’가 되며, 해당 개념을 통해 시스템 내 복잡성을 그룹화시켜 단순하게 나누는 데 도움을 줌

 

애그리거트 특징

  • 각 애그리거트는 필수로 애그리거트 루트를 1개 가지고 있음 (대표 엔티티)
    • 애그리거트의 관리 주체는 애그리거트 루트
    • 해당 애그리거트 내부의 정보를 변경할 때, 항상 애그리거트 루트를 통해 접근해야한다. (무분별한 변경사태를 막기위한 제약임)
  • 각 애그리거트는 독립된 객체군이며, 자시 자신을 관리(변경)할 뿐 다른 애그리거트를 관리하지 않음
  • 애그리거트는 DB에 도메인을 저장하거나 읽어들이는 단위를 의미하며, 읽을 때는 애그리거트 루트의 ID를 이용함
  • 대체로 하나의 애그리거트는 엔티티를 한개만 갖는 경우가 많음
    • 두 개 이상의 엔티티로 구성되는 애그리거트는 드물게 존재함
      • 만약 엔티티가 두개 이상 존재하다고 해도, 동일한 생명주기(생성,삭제)를 지니고 있다면 문제 없음
  • 일관성 있는 트랜잭션 작업 단위를 가지고 있음
    • 예시) 주문이 생성되거나 업데이트되면, 그 과정에서 주문 내의 모든 항목들이 함께 생성되고 함께 변경됨으로써 해당 작업이 하나의 트랜잭션에서 처리됨. 만약 문제발생 시, 트랜잭션이 롤백되면서 애그리거트 내의 데이터 상태는 이전 상태를 유지하게 됨
    • 만약 하나의 트랜잭션에서 여러 애그리거트를 수정하고 있다면, 애그리거트 설계 자체가 잘못된 것은 아닌지 한번 재고해보아야 함

 

TIP 내용

애그리거트 분류기준

  • 관련된 데이터들을 하나의 애그리거트로 묶는다
  • 동일한 생명주기(생성/삭제 둘다 해당)를 가지면 하나의 애그리거트로 묶는다⇒ 두 기준이 전달하고자하는 바 >> 애그리거트를 구성하는 객체들은 서로 높은 결합도를 가지고 있음
  • ⇒ 위 2개는 거의 동의어에 가까우나, 자세한 설명을 위해 위와 같이 표현함
  • 강하게 결합된 비즈니스 로직으로 묶여있는 데이터의 경우, 같은 애그리거트로 묶는다

애그리거트 분류 기준에 따른, 실제 예시

  • 상황 >> 조아라 작품(book)과 작가(author) 대해, 위의 분류기준에 따라 애그리거트화를 진행
  • 관련된 데이터들을 하나의 애그리거트로 묶는다 + 동일한 생명주기(생성/삭제 둘다 해당)를 가지면 하나의 애그리거트로 묶는다
    1. 조아라 작품이 생성, 삭제, 수정되면 그 과정에서 작품 내의 모든 항목들이 함께 생성, 삭제, 수정되어야 함. (하나의 트랜잭션에서 모두 처리되어야 함)
      1. 동일한 생명주기를 가진다는 것의 의미는, 곧 강한 일관성 (하나의 트랜잭션)을 의미함.
    2. 해당 기준으로 아래와 같이 애그리거트를 만들어 볼 수 있음
      1. 작품과 같이 생성, 삭제, 수정되는 요소로서 [가격, 제목, 내용, 작가] 등등이 존재함
      2. 작가와 같이 생성, 삭제, 수정되는 요소로서 [이름, 메일주소, 핸드폰번호] 등등이 존재함
      ⇒ 각 애그리거트에 맞는 요소는 비즈니스 상황에 따라 작업자가 판단

 

** 누군가는 이렇게 생각할 수도 있다

  1. 작품이 가격, 제목, 내용을 가지고 있는 건 알겠지만, 작가는 왜 있는 것이지? 위 내용대로라면 작품이 생성되었다고해서 작가도 생성되는 것도 아니고, 반대로 작가가 생성되었다고해서 작품이 생성되는 게 아니잖아!

답변 >> Author(VO)의 내용으로, 작가명만 존재할 경우, 해당 자료는 문제가 없음 (단순히 작품을 생성한 작가명만을 의미하니까)

  • 강하게 결합된 비즈니스 로직으로 묶여있는 데이터의 경우, 같은 애그리거트로 묶는다
    1. 아래 상황이 있다고 가정
      1. 작품 가격은, 작가의 평판에 따라 달라질 수 있다.
    2. 이런 비즈니스 로직 상황에 유연하게 대비하기 위하여 아래와 같은 데이터가 생기며, 아래와 같이 각 애그리거트가 수정될 수 있음
      1. 작품
        1. PriceHistory (가격이력) 추가
          1. mod_date : 가격이 변경된 날짜
          2. old_price : 이전 가격
          3. new_price : 변경된 가격
      2. 작가
        1. Reputation(작가 평판점수) 추가

 

**** 위와 같이 애그리거트 분류가 완료되었다**

헌데 만약 PriceHistory를 다른 서비스들에서 별도로 조회하고 싶어할 경우, 해당 요소를 Entity로 치환시키면 된다. (식별자 값, 작품 식별값을 갖게하여 관리)

그러면 애그리거트 내 2개 이상의 엔티티가 존재하는 경우가 된다.

댓글