ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 2부
    [옛날 글들] 도메인 주도 설계 2024. 4. 11. 19:37
    728x90

    이전글 : 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 1부

    이 글은 제가 2008년 6월부터 10월까지 5개월간 마이크로소프트웨어에 연재했던 "도메인 주도 설계의 적용"이라는 원고의 원글입니다.  잡지에 맞추어 편집을 하는 과정에서 지면 상의 제약으로 인해 수정되거나 삭제된 부분이 있어 제 블로그에 원글을 올립니다. 도메인 주도 설계(Domain-Driven Design)에 관심 있는 분들에게 도움이 되었으면 합니다. 

    애그리게이트

    애그리게이트(AGGREGATE)는 데이터 변경 시 하나의 단위로 취급할 수 있는 연관된 객체들의 클러스터이다. 각 AGGREATE는 루트(root)와 경계(boundary)를 가진다. 경계는 AGRREGATE 내부에 무엇이 포함되어야 하는지를 정의한다. 루트는 애그리게이트 내에 포함된 하나의 참조 객체다. 루트는 외부에서 참조 가능한 유일한 애그리게이트의 내부 객체이다. 외부에서는 루트 객체만이 참조 가능하지만 애그리케이트 내부의 객체는 외부 객체를 자유롭게 참조할 수 있다. 루트를 제외한 나머지 참조 객체들은 외부로부터 접근이 불가능하기 때문에 지역 식별자(local identity)를 가진다. 지역 식별자는 애그리케이트 내부에서 참조 객체를 식별하기 위한 용도로만 사용된다. 반면 루트는 전역 식별자(global identity)를 가진다.

     

    루트(root)는 애그리케이트 내에 포함된 객체 그룹을 항해하기 위해 필요한 시작 위치를 제공한다. 루트라는 용어는 Eric Evans가 그의 저서 “Domain-Driven Design”에서 애그리케이트의 개념을 설명하면서 사용한 것으로 지난 아티클에서 소개한 엔트리 포인트(ENTRY POINT)와 동일하다. 엔트리 포인트는 Martin Fowler가 “Analysis Patterns”에서 사용한 용어로 엔트리 포인트라는 용어가 객체 그룹 내의 항해를 위한 시작 위치라는 본연의 역할을 더 잘 전달하기 때문에 본 아티클에서는 루트 대신 엔트리 포인트를 사용하기로 한다.

     

    본 아티클에서는 엔트리 포인트를 다음과 같은 의미로 사용한다.

    엔트리 포인트는 애그리케이트를 대표하는참조 객체로 애그리케이트에 속한 객체 그룹을 항해하기 위한 시작 위치를 제공한다. 엔트리 포인트는 전역 식별자(identity)를 가진다.외부에서는 엔트리 포인트 이외의 다른 객체들에 직접 접근할 수 없으며 오직 엔트리 포인트로부터의 항해를 통해서만 접근 가능하다.

     

    Eric Evans 애그리케이트 패턴에 대한 규칙을 다음과 같이 정리하고 있다.

    • 엔트리 포인트는 전역 식별자(global identity)를 가지며 궁극적으로 불변식(invariant)을 검증하는 책임을 가진다.
    • 엔트리 포인트는 전역 식별자(global identity)를 가진다. 애그리케이트 내부에 속한 참조 객체들은 지역 식별자(local identity)를 가지며, 지역 식별자는 애그리케이트 내부에서만 유일하다.
    • 애그리케이트 경계 외부에 있는 어떤 객체도 엔트리 포인트 이외의 애그리케이트 내부 객체를 참조할 수 없다. 엔트리 포인트는 내부에 속한 참조 객체를 외부에 전달할 수는 있지만 이를 전달 받은 객체는 일시적으로만 사용할 뿐 이에 대한 참조를 유지하지 않는다. 엔트리 포인트는 값 객체에 대한 복사본을 다른 객체에게 전달할 수 있다.값 객체는 단지 값일 뿐이며, 값 객체는 애그리케이트와 연관관계를 가지지 않기 때문에 값 객체에 어떤 일이 발생하는지에 대해서는 신경 쓰지 않는다.
    • 위 규칙으로부터 오직 엔트리 포인트만이 리포지토리(REPOSITORY)로부터 직접 얻어질 수 있다는 사실을 유추할 수 있다. 모든 다른 객체들은 엔트리 포인트로부터의 연관 관계 항해를 통해서만 접근 가능하다.
    • 애그리케이트 내부의 객체들은 다른 애그리케이트엔트리 포인트를 참조할 수 있다.
    • 삭제 오퍼레이션은 애그리케이트 내부의 모든 객체를 제거해야 한다.(가비지 컬렉션을 가진 언어의 경우 이 규칙을 준수하는 것이 용이하다. 애그리케이트 외부의 어떤 객체도 엔트리 포인트를 제외한 내부 객체를 참조하지 않기 때문에 엔트리 포인트를 제거하면 이에 수반된 모든 내부 객체가 제거될 것이다.)
    • 애그리케이트 내부의 어떤 객체에 대한 변경이 확약되면, 전체 애그리게이트에 관한 모든 불변식이 만족되어야 한다. 

    여기에서 눈 여겨봐야 할 규칙은 네 번째로 애그리케이트엔트리 포인트만이 리포지토리를 통해 얻어질 수 있으며, 애그리케이트 내부의 다른 객체들은 엔트리 포인트로부터의 연관 관계를 통해서만 접근 가능하다는 부분이다. 이 규칙은 주문 도메인 예에서 살펴본 애그리케이트 식별 규칙에 다음과 같은 한 가지 규칙을 추가한다.

    어떤 참조 객체가 다른 객체에 대해 독립적으로 얻어져야 한다면 이 참조 객체를 중심으로 애그리게이트 경계를 식별하고 해당 참조 객체를 엔트리 포인트로 정한다.

     

    다시 주문 도메인을 살펴 보자. 만약 시스템 내의 모든 주문이 특정 고객 객체를 얻은 후에만 접근할 수 있다면 주문과 주문 항목은 고객 객체를 엔트리 포인트로 하는 애그리케이트의 일부가 되어야 한다. 즉, 고객 객체를 리포지토리로부터 얻은 후 해당 고객 객체로부터 연관 관계를 통해 해당하는 주문들을 항해하면서 작업을 처리한다. 그러나 고객과 무관하게 주문에 직접 접근해야 할 필요가 있다면 주문을 엔트리 포인트로 하는 애그리케이트를 만드는 것이 좋다. 따라서 특정 일자에 발생한 모든 주문을 조회해야 한다는 요구사항이 존재한다면 주문을 엔트리 포인트로 하는 애그리케이트가 필요하다.

     

    애그리케이트엔트리 포인트 역시 시스템의 복잡도를 낮춰주는 유용한 기법이다. 애그리케이트를 정의함으로써 자칫 자잘한 도메인 클래스 더미에 빠져 허우적댈 수도 있는 상황을 피해 불변식(invariant)을 공유하는 도메인 클래스들의 클러스터에 집중할 수 있다. 선택과 집중은 도메인 모델링 영역에서도 어김없이 적용되는 캐치프레이즈인 것이다.

     

    구현과 관련해서는 미묘한 동시성 컨텍스트 하에서 일관성을 유지하기 위해 도메인 클래스의 잠금 전략을 적용할 수 있는 위치를 제공하며, 높은 경합 지점의 식별을 통해 전반적인 성능 향상을 꾀할 수 있는 기초 자료가 된다.

     

    리포지토리는 애그리케이트엔트리 포인트에 대해서만 할당한다. 리포지토리는 객체 그래프에 대한 무분별한 접근을 지양하고 통제되고 제어된 방식으로 객체에 접근하고 항해할 수 있도록 한다. 이처럼 애그리케이트리포지토리를 통해 접근해야 할 도메인 객체와 연관 관계 항해를 통해 접근해야 할 도메인 객체를 명확히 구분함으로써 효율적인 객체 항해를 위한 지침을 제공한다.

     

    애그리케이트, 엔트리 포인트, 리포지토리는 유용한 분석 기법인 동시에 도메인 객체에 대한 메모리 컬렉션 관점을 데이터베이스와 동시 실행 컨텍스트를 기반으로 한 엔터프라이즈 어플리케이션 환경으로 자연스럽게 이어주는 구현 기법이기도 하다. 이에 관해서는 ORM(Object-Relational Mapping)을 소개하는 부분에서 자세히 살펴보기로 한다.

     

    다음은 애그리케이트엔트리 포인트를 결정한 후 리포지토리를 추가한 주문 도메인 모델을 도시한 것이다. 주문에 대한 불변식을 애그리케이트에 추가했음에 주목하라.

     

    그림 9 Order, Customer, Product에 대한 리포지토리 추가

     

    다음글 : 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 3부

    728x90
Designed by Tistory.