헥사고날 아키텍처(Hexagonal Architecture)는 다른 말로, 포트와 어댑터 아키텍처(Ports and Adapters Architecture)이다.
해당 아키텍처의 목표는 외부(외부 인터페이스나 인프라스트럭쳐)의 변경에 영향을 받지 않는 핵심 코드를 만들고 이를 견고하게 관리하는 것이다.
계층형 아키텍처(Layered Architecture)의 문제점
# DB 주도 설계
- 계층형 아키텍처는 DB Layer가 토대이다. 즉, DB 주도 설계로 이어질 가능성이 크다.
- 이는 비즈니스 관점에서 적합하지 않다.
- 도메인 로직과 영속성을 구분하여 설계하는 것이 어려워지며, 도메인 코드가 영속성 계층의 영향을 받을 가능성이 커진다.
# 암묵적인 의존성
- 계층형 아키텍처에서는 동일 계층의 컴포넌트나 아래 계층에 속한 컴포넌트에 접근할 수 있다.
- 이로 인해 도메인에서 영속성으로 접근하는 코드가 증가하게 되고, 둘 사이의 암묵적 의존성이 생긴다.
# 책임의 섞임
- 시스템을 개발하며 계층을 건너뛰어 접근하는 경우가 늘어나면, 도메인 로직이 전체 시스템에 퍼지게 되고 책임이 섞이게 된다.
# 해결책
- 계층형 아키텍처는 이러한 문제를 DIP(의존성 역전법칙) 로 해결했다.
- 모든 계층에 DIP를 적용하면 헥사고날 아키텍처 형태를 띄우게 된다!
DIP(의존성 역전 법칙)는 상위 수준의 모듈이 하위 수준의 모듈에 의존하지 않도록 하고, 추상화된 인터페이스에 의존하도록 설계하는 원칙이다.
계층형 아키텍처에서 헥사고날 아키텍처로 진화
# 기존의 계층형 아키텍처
아래는 기존의 계층형 아키텍처 구조이다. (웹/도메인/영속성)
도메인 계층이 영속성 계층을 참조한다는 문제점이 있다.
# DIP 적용한 계층형 아키텍처
아래는 기존의 계층형 아키텍처에서 DIP를 적용한 구조이다.
여기서 주목할 점은, 영속성 계층과 도메인 계층간의 인터페이스를 둠으로써 의존성을 역전시켰다는 점이다.
(도메인 계층이 더 이상 영속성 계층에 의존하지 않는다.)
# 헥사고날 아키텍처
아래는 헥사고날 아키텍처의 기본 구조이다.
도메인 엔티티는 다른 어떤 컴포넌트에도 의존하지 않는 순수한 객체이다.
육각형 내부에 도메인 엔티티와 상호작용하는 유즈케이스가 존재한다.
육각형에서 외부로 향하는 의존성이 없고, 모든 의존성이 코어로 향한다.
헥사고날 아키텍처의 구성 및 특징
# 내부와 외부로 구분
헥사고날 아키텍처는 내부(코어)와 외부(인프라)로 구분된다.
내부란 순수한 비즈니스 로직을 표현하며 캡슐화된 영역(핵심 코드)이며
외부란 내부 영역에서 기술을 분리하여 구성한 영역이다.
육각형에서 외부로 향하는 의존성이 없고, 모든 의존성이 코어로 향한다.
주로 기능적 요구 사항에 따라 내부 영역부터 설계하고, 이후에 외부 영역을 설계한다.
# DIP(의존성 역전 법칙)
위에서도 언급했듯, 헥사고날 아키텍처의 핵심 개념은 DIP이다.
상위 계층은 하위 계층에 직접 의존하지 않고 인터페이스를 통해 의존성을 역전시킨다.
헥사고날 아키텍처에서는 포트(인터페이스)를 통해 의존성을 역전시킨다.
(포트의 개념에 대해서는 아래에서 더 자세히 알아보도록 하자.)
# ISP(인터페이스 분리 원칙)
ISP는 클라이언트가 자신이 사용하지 않는 인터페이스에 의존하지 않아야한다는 원칙이다.
헥사고날 아키텍처에서는 UseCase를 통해 클라이언트에 필요한 인터페이스를 명확하게 분리함으로써, 인터페이스의 불필요한 의존성을 줄인다.
# SRP(단일 책임 원칙)
SRP는 한 클래스는 단 하나의 책임을 가져야한다는 원칙이다.
헥사고날 아키텍처에서는 각 계층과 모듈이 자체적으로 단일 책임을 가지고 독립적으로 변경 가능하도록 설계된다.
# DDD(도메인 주도 설계)
헥사고날 아키텍처에서는 비즈니스 도메인을 중심으로 하고 이를 다른 계층과 분리하여 도메인 모델의 복잡성을 관리한다.
# 포트와 어댑터
헥사고날 아키텍처에서는 외부 환경에 대한 의존성을 추상화된 포트와 어댑터를 통해 처리한다.
이를 통해 시스템은 외부 변화에 유연하게 대처할 수 있다.
아래에서 더 자세히 알아보도록 하자.
이러한 헥사고날의 특징들은 높은 유연성, 재사용성, 테스트 용이성을 제공하며, 시스템의 확장과 유지보수를 용이하게 한다.
포트와 어댑터란?
쉽게 말해 포트는 인터페이스이고, 어댑터는 인터페이스(포트)를 따르는 구현체이다.
포트는 인바운드(inbound) 포트와 아웃바운드(outbound) 포트로 구분된다.
인바운드 포트 | 내부 영역 사용을 위해 노출된 API ex) 유즈케이스 |
아웃바운드 포트 | 내부 영역이 외부 영역을 사용하기 위한 API ex) 영속성 어댑터 |
어댑터도 인바운드 어댑터와 아웃바운드 어댑터로 구분된다.
인바운드 어댑터 | 외부 요청을 처리하는 역할 ex) Controller |
아웃바운드 어댑터 | 외부와 연계하는 역할 ex) 영속성을 처리하는 Repository |
인바운드 포트는 인바운드 어댑터와 매칭되고
아웃바운드 포트는 아웃바운드 어댑터와 매칭된다.
인바운드 포트 및 어댑터는,
외부 영역에서 내부 비즈니스 영역을 활용하기 위한 것이고 (외부 -> 내부)
아웃바운드 포트 및 어댑터는,
내부 비즈니스 영역에서 외부 영역을 활용하기 위한 것이다. (내부 -> 외부)
'프로그래밍' 카테고리의 다른 글
JWT란? (0) | 2023.07.11 |
---|---|
OAuth2.0 란? (2) | 2023.07.07 |
[소프트웨어 아키텍처] 레이어드 아키텍처(Layered Architecture)란? (5) | 2023.04.25 |
[Java] 제너릭(Generic) - 무공변성, 공변성, 반공변성 (1) | 2023.04.16 |
[Java] LinkedHashMap (0) | 2023.03.13 |