[DDD START!] CHAPTER 2. 아키텍처 개요

3 분 소요

// TODO : jyjang - 앞부분 TBD

도메인 영역의 주요 구성요소 (p.54)

  • entitiy
    • 고유의 식별자를 갖는 객체
    • 도메인의 고유한 개념을 표현함
    • 예시 : Order, Member, Product …
    • 도메인 모델의 데이터를 포함하며 해당 데이터와 관련된 기능을 함께 제공
  • value
    • 고유 식별자를 갖지 않는 객체
    • 주로 도메인 객체의 속성을 표현할 때 사용됨
    • 예시 : Address, Money …
    • entity의 속성으로 사용되거나 다른 value의 속성으로도 사용됨
  • aggregate
    • 관련된 entitiy와 value 객체를 개념적으로 하나로 묶은 것임
    • 예시 : 주문과 관련된 Order 엔티티, OrderLine 밸류, Orderer 밸류 객체를 ‘주문’ 애그리거트로 묶을 수 있음
  • repository
    • 도메인 모델의 영속성을 처리
    • 예시 : DBMS 테이블에서 entity 객체를 로딩하거나 저장하는 기능을 제공
  • domain service
    • 특정 entitiy에 속하지 않은 도메인 로직을 제공
    • 도메인 로직이 여러 entity와 value를 필요로 할 경우 도메인 서비스에서 로직을 구현함
    • 예시 : ‘할인 금액 계산’은 상품, 쿠폰, 회원 등급, 구매 금액 등 다양한 조건을 이용하여 구현하기 때문에 도메인 서비스에서 구현해야함

엔티티와 밸류

  • 도메인 모델의 엔티티와 DB 모델의 엔티티는 같은 것이 아님!!!
    • 두 모델의 차이
      1. 도메인 모델의 엔티티는 데이터와 함께 도메인 기능을 함께 제공함
        • 예시 : 주문을 표현하는 엔티티는 주문 관련 데이터 뿐만 아니라 배송지 주소 변경 기능도 함께 제공함
      2. 도메인 모델의 엔티티는 두 개 이상의 데이터가 개념적으로 하나인 경우 밸류 타입을 이용해서 표현할 수 있다
        • 도메인 모델의 엔티티
          • Order에 Orderer 밸류가 속성으로 있는 것이 명확하게 보임
        // 도메인 모델 엔티티
        public class Order {
            // ...
            private Orderer orderer;
            // ...
        }
      
        // 밸류
        public class Orderer {
            private String name;
            // ...
        }
      
      • DB 모델의 엔티티
        • 왼쪽 케이스는 주문자(Orderer) 라는 개념이 드러나지 않는 단점이 있음
        • 오른쪽 케이스는 주문자 데이터를 별도 테이블로 저장했지만 엔티티에 가깝지 밸류 타입의 의미가 드러나지는 않음
  • 밸류는 불변으로 구현하는 것을 권장함
    • 이는 엔티티의 밸류 타입 데이터를 변경할 때는 (기존 객체 값을 변경하는 것이 아니라) 객체 자체를 완전히 교체해야함을 의미함

애그리커트

  • 도메인이 커짐 -> 도메인 모델도 커짐 -> 많은 엔티티와 밸류가 출현 -> 모델은 점점 더 복잠해짐
  • 도메인 모델이 복잡해지면 개발자는 전체 구조가 아닌 한 개 엔티티와 밸류에만 집중하는 경우가 발생함
  • 애그리거트는 이러한 상황을 막고 개발자가 전체 구조를 이해하는데 도움을 줌
  • 애그리거트는 관련 객체를 하나로 묶은 군집이다 (자바로 치면 패키지?)
  • 관련 객체를 애그리거트로 묶으면 복잡한 도메인 모델을 관리하는데 도움이 됨
  • 개별 객체 간의 관계가 아닌 애그리거트 간의 관계로 도메인 모델을 이해하고 구현할 수 있게 됨, 이를 통해 큰 틀에서 도메인 모델을 관리할 수 있게 됨
  • root entity : 애그리거트는 군집에 속한 객체들을 관리하는 루트 엔티티를 갖음
    • 루트 엔티티는 애그리거트에 속해 있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야할 기능을 제공함
    • 애그리커트를 사용하는 코드는 애그리거트 루트가 제공하는 기능을 실행하고 애그리거트 루트를 통해서 간접적으로 애그리거트 내의 다른 앤티티나 밸류 객체에 접근하게됨

리포지터리

  • 도메인 객체를 지속적으로 사용하기 위해 DB, File 과 같은 저장소에 보관해야함. 이를 위한 도메인 모델이 리포지터리임
  • 엔티티나 밸류는 요구사항에 도출되는 도메인 모델인 반면, 리포지터리는 구현을 위한 도메인 모델임
  • 리포지터리를 통해서 도메인 객체를 구한 뒤에 도메인 객체의 기능을 실행하게 됨
    • 예시 : 주문 취소
      public class CancelOrderService {
          private OrderRepository orderRepository;
    
          public void cancel(OrderNumber number) {
              Order order = orderRepository.findByNumber(number); // 리포지터리를 통해 도메인 객체를 구함
              if (order == null) {
                  throw new NoOrderException(number);
              }
              order.cancel(); // 도메인 객체의 기능을 실행
          }
      }
    
  • 모듈 구조

    • Repository 인터페이스는 도메인 모델 영역에 속하며, 실제 구현 클래스는 infrastructure 역역에 속함
  • 응용 서비스는 의존 주입과 같은 방식을 사용해서 실제 리포지터리 구현 객체에 접근함
  • 응용 서비스는 리포지터리와 밀접한 연관이 있는 이유
    1. 응용 서비스는 필요한 도메인 객체를 구하거나 저장할 때 리포지터를 사용함
    2. 응용 서비스는 트랜잭션을 관리하는데, 트랜잭션 처리는 리포지터리 구현 기술에 영향을 받음
  • 리포지터리의 사용 주체는 응용 서비스임. 따라서 리포지터리는 응용 서비스가 필요로 하는 메소드를 제공함
    • 애그리거트를 저장하는 메서드
    • 애그리거트 루트 식별자로 애그리거트를 조회하는 메서드 2
        public interface SomeRepository {
        void save(Some some);
        Some findById(SomeId id);
        }
      

요청 처리 흐름 (p.65)

  • 사용자 요청 -> 표현 영역에서 받음 (사용자가 전송한 데이터 형식이 올바른지 검사, 응용 서비스에 맞는 형태로 데이터 변환) -> 응용 서비스에 기능 실행을 위임 -> 응용 서비스는 도메인 모델을 이용해서 기능 구현

인프라스트럭처 개요

  • 인프라스트럭처는 표현 영역, 응용 영역, 도메인 영역을 지원함
  • 도메인 객체의 영속성 처리, 트랜잭션, SMTP 클라이언트, REST 클라이언트 등
  • 도메인 영역과 응용 영역에서 인프라스트럭처의 기능을 직접 사용하는 것보다, 이 두 영역에 정의한 인터페이스를 인프라스트럭처 영역에서 구현하는 것이 시스템을 더 유연하고 테스트하기 쉽게 만들어준다.
  • 하지만, 무조건 인프라스트럭처에 대한 의존을 없애는 것이 좋은 것은 아님
    • 응용 서비스에 트랜젝션 관리를 위한 @Transactional을 추가하거나, 도메인 모델 클래스에 영속성 처리를 위한 JPA @Entity, @Table 같은 것들을 사용하는 것이 편리함
      • 구현의 편리함은 DIP가 주는 장점만큼 중요하기 때문에, DIP의 장점을 해치지 않는 범위에서 의존성을 가져가는게 현명함
  • 표현 영역은 항상 인프라스트럭처 영역과 쌍을 이룸 : 사용 프레임워크에 맞는 방법으로 구현해야함

// TODO : jyjang - TBD

태그:

카테고리:

업데이트:

댓글남기기