[Hexagonal Architecture] 1. 헥사고날 아키텍처란?

2022. 11. 18. 06:23Dev/Clean Architecture

반응형

※ 해당 포스트 및 이어지는 포스트는 만들면서 배우는 클린아키텍처를 읽고 정리하였습니다.


  1. 계층형 아키텍처
  2. 의존성 역전하기
  3. 클린 아키텍처
  4. 헥사고날 아키텍처
    1. 구조
    2. 계층 구성
    3. 패키지 구성

1. 계층형 아키텍처

전통적인 아키텍처입니다.

사용자와 상호작용을 담당하는 웹(프레젠테이션) 계층과 엔티티의 영속성을 처리하는 영속성 계층를 별개의 계층으로 구분합니다.

웹 계층은 도메인 계층을 의존하고, 도메인 계층은 영속성 계층을 의존하기 때문에 도메인 계층 입장에서 의존성이 비대칭적입니다.

01-1

모든 것이 영속성 계층을 토대로 만들지기 때문에 데이터베이스에 의존적이고, 이에 의해 데이터베이스 중심으로 설계가 진행되게 됩니다.

데이터베이스 중심의 설계는 영속성 로직과 도메인 로직이 뒤섞여 있어, 각각을 개별적으로 개발하기 힘들어 협업에 에로사항이 있습니다.

영속성 로직과 도메인 로직이 뒤섞여있는 점은 프로젝트 크기가 커질수록 몇가지 문제점을 발생시킵니다.

  1. 도메인 로직을 웹 계층에서 구현하는 일이 빈번해질 경우, 애플리케이션 전반적으로 핵심 도메인 로직이 분산된다는 단점이 생긴다
    1. 도메인 로직을 작성한 코드가 어느곳에 있는지 찾기 어려워집니다.
  2. 웹 계층 테스트를 할 때, 도메인 계층 뿐만 아니라 영속성 계층도 모킹해야해서 단위 테스트 복잡도가 올라가게 된다.

2. 의존성 역전하기

계층형 아키텍처의 단점을 해소하기 위해 의존성을 역전하는 방법에 대해 알아봅니다.

2.1. 단일 책임 원칙

Single Responsibility Principal

컴포넌트를 변경하는 이유는 오직 하나일 수 있도록 구조화하기

즉, 하나의 컴포넌트에서 여러 기능을 포함하지 않도록 만들어서 의존성을 낮추는 것을 목표합니다.

각 컴포넌트는 하나의 역할을 맡고 있기 때문에 다른 컴포넌트 변경에 대해 신경쓰지 않아도 됩니다.

2.2. 의존성 역전 원칙

Dependency Inversion Principle

계층형 아키텍처에서의 계층간 의존성은 웹 → 도메인 → 영속성 방향항이고,
영속성 계층에 변화가 생길시 도메인 계층도 변경해야하는 경우가 많습니다.

영속성 계층에 변경이 생기더라도 도메인 계층의 코드를 변경하고 싶다면 어떻게 해야할까?
바로, 의존성의 방향을 바꾸는 방법으로 이를 해결할 수 있습니다.

01-2

3. 클린 아키텍쳐

비즈니스 규칙이 프레임워크, database, UI, 외부 애플리케이션으로부터 독립적으로 만들어, 테스트를 용이하게 합니다.

이미지 출처: https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg


모든 의존성이 도메인 코드를 향합니다.

서비스는 단일책임을 갖는 유스케이스로 세분화 시킵니다.

도메인 코드는 영속성 프레임워크나 UI 프레임워크로부터 철저히 분리되어 있기 때문에, 특정 프레임워크에 종속적이지 않은 코드를 개발할 수 있게 하고 비즈니스 로직에 집중할 수 있게 만듭니다.

다만, 도메인 계층이 외부 계층(영속성, UI)으로부터 철저히 분리하기 위해, 애플리케이션의 엔티티에 대한 모델을 각 계층에 맞춰 각각 생성해줘야 한다는 불편한 점이 있습니다.

영속성 계층에서 Table과 매핑될 정보를 갖는 ORM 엔티티 객체를 정의하였다 하더라도, 도메인 계층에서는 그와 별도로 엔티티를 생성해 줘야합니다.

각 계층에 엔티티를 각각 만들어 줘야하면서, 두 엔티티를 서로 변환하는 과정을 거쳐야 합니다.(다른 계층에서도 마찬가지로 만들어줘야합니다.)


4. 헥사고날 아키텍처 (= 육각형 아키텍처)

도메인 중심 아키텍처 일종으로 클린 아키텍처를 일반화한 구조 중 하나입니다.

UI나 database를 비즈니스 로직으로 분리되어야하는 외부요소로 취급합니다.

비즈니스 로직(도메인 코드)이 외부요소에 의존하지 않게 만들고, 프레젠테이션 계층(controller)과 데이터 소스 계층(persistence)이 도메인 계층에 의존하도록 합니다.

도메인 계층입장에서 의존성이 대칭적입니다.

헥사고날 아키텍처는, 비즈니스 로직을 다루는 내부와, 기술적인 것을 다루는 외부로 분해될 수 있습니다.

외부에 포함된 컴포넌트를 어댑터(adapter)라 부르고, 어댑터가 내부와 상호작용하는 부분을 포트(port)라 부릅니다.

이 때문에 포트와 어댑터 아키텍처(패턴) 라고도 부릅니다.


4.1. 구조

01-3
  • 어댑터
    • driving adapter (= incoming adapter)
      • 주도하는 어댑터
      • 왼쪽에 있는 어댑터(웹 어댑터, 외부 시스템 어댑터)
      • 애플리케이션 코어를 호출합니다.
    • driven adapter (= outgoing adapter)
      • 주도되는 어댑터
      • 오른쪽에 있는 어댑터(영속성 어댑터, 외부 시스템 어댑터)
      • 애플리케이션 코어에 의해 호출됩니다.
  • 포트
    • 애플리케이션 코어와 어댑터 간에 통신을 위한 부분
    • 인터페이스로 구성
  • 애플리케이션 코어
    • 도메인 엔티티, 유스케이스
    • 외부로 향하는 의존성이 없습니다.(= 모든 의존성은 코어를 향합니다.)

4.2. 계층 구성

헥사고날 아키텍처는 아래의 3개의 계층으로 나눌 수 있습니다.

  • 어댑터 계층
    • 가장 바깥쪽에 있는 계층으로 애플리케이션과 다른 시스템간의 상호작용을 합니다.
    • web 어댑터(= 웹 컨트롤러), persistence 어댑터 등이 위치함
  • 애플리케이션 계층
    • 바깥 계층에서 호출하기 위해 반드시 거쳐야하는 곳입니다.
    • 포트와 서비스를 포함합니다
      • 포트
        • 서비스에서 구현될 인터페이스
        • input port
          • incoming adapter에 의해 불려집니다
        • output port
          • outgoing adapter를 부릅니다
      • 서비스
        • 포트 구현체(= 유스케이스 구현체)
  • 도메인 계층
    • 도메인 엔티티

4.3. 패키지 구성

헥사고날 아키텍처를 spring 프로젝트에 적용하면 기존의 계층형 아키텍쳐에서 각 레이어별로 묶었기 때문에 모든 클래스들의 접근 제어자가 public으로 되어있어 경계가 약했던 단점을 보완할 수 있습니다.

  • 어댑터 패키지
    • package-private
  • 애플리케이션 패키지
    • 어댑터에 접근해야할 포트는 public
    • 서비스는 package-private (input port를 public으로 지정했기 때문에 서비스는 오픈되지 않아도 됩니다.)
  • 도메인 패키지
    • 서비스, 어댑터를 접근해야하기 때문에 public로 지정

package-private 접근제한자

자바 패키지를 통해 클래스들을 응집적인 모듈로 만들어줍니다.
패키지 내에 있는 클래스들끼리 서로 접근가능하지만 패지키 바깥에서는 접근할 수 없도록 합니다.

패키지 내에 있는 클래스들 중 진입점으로 활용될 클래스에만 public 설정을 해주면 됩니다.
이는 잘못된 방향으로 의존성이 이용될 문제를 해결해줍니다.


+++

  • Hexagonal Architecture
728x90
반응형