Chapter 2: 아키텍처 개요

💡 책에서 기억하고 싶은 내용

표현 영역 (UI 영역)

  • HTTP 요청을 응용 영역이 필요로 하는 형식으로 변환해서 응용 영역에 전달하고, 응용 영역의 응답을 HTTP 응답으로 변환하여 전송한다

응용 영역

  • 도메인 모델을 이용해서 사용자에게 제공할 기능을 구현한다

  • 실제 도메인 로직 구현은 도메인 모델에 위임한다

도메인 영역

  • 도메인 모델을 구현한다

  • 도메인의 핵심 로직 을 구현한다

인프라스트럭처 영역

  • 구현 기술에 대한 것을 다룬다

    • ex) RDBMS 연동 처리, 메시징 큐에 메시지 전송 / 수신 기능 구현, 몽고DB or 레디스와의 데이터 연동 처리, SMTP를 이용한 메일 발송 기능 구현, HTTP Client를 이용해 REST API 호출하는 것을 처리

  • 논리적인 개념을 표현하기 보다는, 실제 구현을 다룬다

도메인 영역, 응용 영역, 표현 영역

  • 도메인 영역, 응용 영역, 표현 영역은 구현 기술을 사용한 코드를 직접 만들지 않는다

  • 대신 인프라스트럭처 영역에서 제공하는 기능을 사용해서 필요한 기능을 개발한다

계층 구조 아키텍처

  • 계층 구조는 그 특성상 상위 → 하위 계층으로의 의존만 존재하고, 하위 계층은 상위 계층에 의존하지 않는다

  • 표현, 응용, 도메인 계층이 상세한 구현 기술을 다루는 인프라스트럭처 계층에 종속 된다

  • 인프라스트럭처에 의존하면 테스트 어려움확장의 어려움 이라는 두 가지 문제가 발생한다

    • 이것을 해소하기위한 해답은 DIP에 있다!

DIP

  • Problem

    • 고수준 모듈이 제대로 동작하려면 저수준 모듈을 사용해야 한다

    • but, 고수준 모듈이 저수준 모듈을 사용하면 구현 변경테스트 가 어렵다는 문제가 발생한다

  • DIP의 원리

    • DIP 는 이 문제를 해결하기 위해 저수준 모듈이 고수준 모듈에 의존하도록 바꾼다

    • 추상화 한 인터페이스(고수준)를 저수준 모듈이 상속받게 하여

      • 고수준 모듈인 Service에서는 저수준 모듈에 의존하지 않고,

      • 저수준 모듈이 고수준 인터페이스를 상속 받아 구현함으로써 저수준 모듈이 고수준 모듈에 의존하게 된다

        • 상속은 의존의 다른 형태이다!

  • DIP의 장점

    • 구현 기술을 변경하더라도 고수준 모듈을 수정할 필요가 없다

      • 사용할 저수준 구현 객체를 생성하는 코드만 변경하면 된다!

        • 사용할 저수준 객체를 생성하고,

        • 생성자 방식으로 주입한다

      • 스프링 같은 의존성 주입을 지원하는 프레임워크를 사용하면, 설정 코드를 수정해서 쉽게 구현체를 바꿀 수 있다

    • 고수준 모듈을 테스트 할 때, 고수준 인터페이스의 대역 객체를 사용해서 테스트를 진행할 수 있다

      • 고수준 인터페이스의 실제 구현 클래스가 없어도 고수준 모듈을 테스트 할 수 있다!

  • DIP 주의사항

    • 저수준 모듈에서 인터페이스를 추출하면 안된다

      • DIP를 적용할 때 하위 기능을 추상화한 인터페이스는 고수준 모듈 관점에서 도출한다!

    • 하위 기능추상화한 인터페이스는 고수준 모듈에 위치한다

  • DIP와 아키텍처

    • 인프라스트럭처 영역은 구현 기술을 다루는 저수준 모듈이고, 응용 영역과 도메인 영역은 고수준 모듈이다

    • 인프라스트럭처 계층이 가장 하단에 위치하는 계층형 구조와 달리 아키텍처에 DIP를 적용하면, 인프라스트럭처 영역이 응용 영역과 도메인 영역에 의존 (상속) 하는 구조가 된다

    • DIP를 적용하면 응용 영역과 도메인 영역에 영향을 최소화하면서 구현체를 변경하거나 추가할 수 있다

도메인 영역의 주요 구성요소

  • 엔티티 (Entity)

    • 고유의 식별자를 갖는 객체로 자신의 라이프 사이클을 갖는다

    • ex) 주문, 회원, 상품과 같이 도메인의 고유한 개념을 표현한다

    • 도메인 모델의 데이터를 포함하며, 해당 데이터와 관련된 기능을 함께 제공한다

  • 밸류 (Value)

    • 고유의 식별자를 갖지 않는 객체로, 주로 개념적으로 하나인 값 을 표현할 때 사용된다

    • ex) 배송지 주소를 표현하기 위한 주소(Address)나 구매 금액을 위한 금액(Money)과 같은 타입이 밸류 타입이다

    • 엔티티의 속성으로 사용할 뿐만 아니라, 다른 밸류 타입의 속성으로도 사용할 수 있다

  • 애그리거트 (Aggregate)

    • 연관된 엔티티와 밸류 객체를 개념적으로 하나로 묶은 것이다

    • ex) 주문과 관련된 Order 엔티티, OrderLine 밸류, Orderer 밸류 객체를 ‘주문' 애그리거트로 묶을 수 있다

  • 리포지터리 (Repository)

    • 도메인 모델의 영속성을 처리한다

    • ex) DBMS 테이블에서 엔티티 객체를 로딩하거나 저장하는 기능을 제공한다

  • 도메인 서비스 (Domain Service)

    • 특정 엔티티에 속하지 않은 도메인 로직을 제공한다

    • ex) ‘할인 금액 계산' 은 상품, 쿠폰, 회원 등급, 구매 금액 등 다양한 조건을 이용해서 구현하게 되는데, 이렇게 도메인 로직이 여러 엔티티와 밸류를 필요로 하면 도메인 서비스에서 로직을 구현한다

엔티티와 밸류

  • DB 테이블의 엔티티와 도메인 모델의 엔티티의 차이점

    • 도메인 모델의 엔티티는 데이터 + 도메인 기능을 함께 제공한다는 점!

    • 도메인 모델의 엔티티는 두 개 이상의 데이터가 개념적으로 하나인 경우 밸류 타입 을 이용해서 표현할 수 있다

  • 밸류 타입의 데이터를 변경할 때는 새로운 객체로 교체핸다

애그리거트

  • 도메인 모델도 개별 객체뿐만 아니라 상위 수준에서 모델을 볼 수 있어야 전체 모델의 관계와 개별 모델을 이해하는데 도움이 된다

    • 도메인 모델에서 전체 구조를 이해하는 데 도움이 되는 것이 애그리거트 다!

  • 애그리거트는 관련 객체를 하나로 묶은 군집이다

    • 관련된 객체를 애그리거트로 묶으면 복잡한 도메인 모델을 관리하는 데 도움이 된다

  • 루트 엔티티

    • 애그리거트는 군집 에 속한 객체를 관리하는 루트 엔티티 를 갖는다

    • 루트 엔티티는 애그리거트가 속해 있는 엔티티와 밸류 객체를 이용해서 애그리거트가 구현해야 할 기능을 제공한다

    • 애그리거트를 사용하는 코드는 루트가 제공하는 기능을 실행하고, 루트를 통해서 간접적으로 애그리거트 내의 다른 엔티티나 밸류 객체에 접근한다

      • 이것은 애그리거트의 내부 구현 을 숨겨서 애그리거트 단위로 구현을 캡슐화 할 수 있도록 돕는다!

    • 루트 엔티티 가 구현한 도메인 로직을 항상 따르게 된다!

리포지터리

  • 도메인 객체를 지속적으로 관리하기 위한 물리적인 저장소가 필요한데, 이를 위한 도메인 모델리포지터리라고 한다

  • 리포지터리는 애그리거트 단위로 도메인 객체를 저장하고 조회하는 기능을 정의한다

  • 도메인 모델을 사용해야 하는 코드는 리포지터리를 통해서 도메인 객체를 구한 뒤에, 도메인 객체의 기능을 실행한다

  • 리파지토리 인터페이스도메인 모델 영역에 속하고, 실제 구현 클래스인프라스트럭처 영역에 속한다

    • 응용 서비스는 DIP와 같은 방식을 사용해서 실제 리포지터리 구현 객체에 접근한다

  • 응용 서비스와 리포지터리가 밀접한 연관이 있는 이유

    • 응용 서비스는 필요한 도메인 객체를 구하거나 저장할 때 리포지터리를 사용한다

    • 응용 서비스는 트랜잭션 을 관리하는데, 트랜잭션 처리는 리포지터리 구현 기술의 영향을 받는다

  • 리포지터리는 응용 서비스가 필요로 하는 method를 제공한다

    • 애그리거트를 저장하는 method

    • 애그리거트 루트 식별자 로 애그리거트를 조회하는 method

    • ex)

      public interface SomeRepository {
        void save(Some some);
        Some findById(SomeId id);
      }

인프라스트럭처

  • 인프라스트럭처는 표현 영역, 응용 영역, 도메인영역을 지원한다

  • 도메인 영역과 응용 영역에서 인프라스트럭처의 기능을 직접 사용하는 것보다, 두 영역에 정의한 인터페이스인프라스트럭처 영역에서 구현하는 것이 시스템을 더 유연하고 테스트하기 쉽게 만들어준다

    • but, 구현의 편리함을 위해 인프라스트럭처에 대한 의존을 일부 도메인에 넣는 것도 좋다!

      • ex) 스프링의 @Transactional , JPA의 @Entity

모듈 구성

  • 방법

    • 영역별로 모듈이 위치할 패키지를 구성하는 방법

    • 하위 도메인으로 나누고, 각 하위 도메인마다 별도 패키지를 구성하는 방법

    • 도메인 모델과 도메인 서비스를 별도 패키지에 위치시키는 방법

      • Ex)

        • com.myshop.order.domain.order: 애그리거트 위치

        • com.myshop.order.domain.service: 도메인 서비스 위치

  • Tips

    • 한 패키지에 가능하면 10~15개 미만으로 타입 개수를 유지하기

      • 이 개수가 넘어가면 패키지를 분리하는 시도를 해본다!

Last updated