헥사고날 아키텍처는 비즈니스 로직을 중심으로 두고, Port와 Adapter를 통해 외부 시스템과 연결하는 구조이다.
이 구조는 아래와 같은 특징이 있다.
특징 1. 외부 기술 스택(UI, DB, 프레임워크 등)에 종속되지 않고 비즈니스 로직을 보호한다.
특징 2. 같은 비즈니스 로직을 다양한 인터페이스로 접근할 수 있어 애플리케이션을 유연하게 구성할 수 있다.
특징 3. 외부 의존성을 쉽게 Mocking 할 수 있어 테스트가 용이하다.
(1) 도메인(Domain)
헥사고날 아키텍처의 중심에는 핵심 비즈니스 로직이 있다.
이 영역은 외부 시스템과 철저히 분리되며, 주요 Entity, Domain Service, Use Case 등이 포함된다.
1. 비즈니스 규칙을 표현하는 코드만 존재하며, 외부 요소(UI, DB, 프레임워크 등)에 의존하지 않는다.
2. 도메인 로직은 어떤 특정한 DB, UI에 종속되지 않으므로 의존성이 역전된다.
(2) Port
포트는 핵심 도메인(Core)과 외부 시스템을 연결하는 추상화된 인터페이스이다.
포트에는 두 가지 유형이 있다.
1. 입력 포트(Inbound Ports) : 시스템이 외부에서 요청을 받을 때 사용되는 인터페이스
ex) REST API 컨트롤러, 메시지 큐 소비자 등
public interface OrderService {
void placeOrder(OrderRequest request);
}
2. 출력 포트(Outbound Ports) : 시스템이 외부 시스템(DB, 다른 서비스 등)과 상호작용 할 때 사용되는 인터페이스
ex) Repository, API Client
public interface PaymentGateway {
void processPayment(PaymentRequest request);
}
(3) Adapters
어댑터는 포트 인터페이스를 구현하여 실제 기능을 제공하는 클래스이다.
어뎁터는 보통 외부 시스템과 직접적으로 통신하는 역할을 한다.
1. 입력 어댑터(Inbound Adapters) : 사용자 요청을 받아서 핵심 도메인 로직을 실행하는 역할
ex) REST API 컨트롤러, CLI 인터페이스, gRPC 서비스 등
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public ResponseEntity<Void> placeOrder(@RequestBody OrderRequest request) {
orderService.placeOrder(request);
return ResponseEntity.ok().build();
}
}
2. 출력 어댑터(Outbound Adapters) : 외부 시스템과 연결하여 데이터를 주고받는 역할
ex) DB Repository, API 클라이언트, 메시지 프로듀서
@Repository
publics class PaymentGatewayImpl implements PaymentGateway {
private final RestTemplate restTemplate;
public PaymentGatewayImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public void processPayment(PaymentRequest request) {
restTemplate.postForEntity("https://payment.example.com/pay", request, Void.class);
}
}
헥사고날 아키텍처의 구조를 시각적으로 표현해보았다.
핵심 비즈니스 로직은 포트를 통해서만 외부로 연결되며, 어댑터는 포트의 구현체로 동작한다.

장점 1. 기술 독립성
: 특정 프레임워크나 데이터베이스에 의존하지 않는다. 예를 들어 MYSQL을 사용하다가 PostgreSQL로 쉽게 변경할 수 있다.
장점 2. 테스트 용이성
: 포트를 이용해 Mock을 쉽게 만들 수 있어서 단위 테스트가 용이하다.
장점 3. 유지보수 및 확장성
: 새로운 UI(ex. REST API -> gRPC)를 추가할 때 핵심 비즈니스 로직을 수정할 필요가 없다.
: 같은 로직을 다양한 환경에서 재사용할 수 있다.
장점 4. 결합도 감소
: 도메인 로직이 외부 기술에 의존하지 않으므로 명확하게 분리된다.
단점 1. 구현이 복잡하다
: 인터페이스(포트)와 구현(어댑터)을 분리해야 하므로 코드가 늘어난다.
단점 2. 초기 설계 비용이 크다
: 작은 프로젝트에서는 필요 이상으로 복잡한 구조가 될 수 있다.
단점 3. 이해하기 어렵다
: 전통적인 계층형 아키텍처보다 이해하기 어렵다.
계층형 아키텍처는 DB 주도 설계로 이어질 가능성이 크고, 암묵적인 의존성과 책임이 섞이는 문제가 있다.
계층형 아키텍처는 이러한 문제를 DIP(의존성 역전 법칙)으로 해결할 수 있으며,
모든 계층에 DIP를 적용하면 헥사고날 아키텍처 형태를 띄게 된다.
| 헥사고날 아키텍처 | 계층형 아키텍처 | |
| 비즈니스 로직 보호 | 강함 | 약함 |
| 기술 종속성 | 낮음(독립적) | 높음(DB, UI 종속) |
| 테스트 용이성 | 높음 | 낮음 |
| 확장성 | 높음 | 낮음 |
| 초기 비용 | 높음 | 낮음 |
헥사고날 아키텍처는 비즈니스 로직을 보호하고, 외부 시스템과의 결합도를 낮출 수 있다.
그러나 초기 비용이 높고 구현이 복잡하기 때문에, 확장성이 중요한 프로젝트나 복잡한 도메인에서 적합하다.
MSA와 결합했을 때도 좋은 시너지가 생길 것 같다.
| [프로젝트 노트]동시성 이슈를 해결하는 여러가지 방법 (0) | 2024.12.16 |
|---|---|
| TestSuiteExecutionException: Could not execute test class 해결방법 (0) | 2024.12.12 |
| Bean 수동 등록, Bean 주입 우선순위 - @Qualifier와 @Primary (0) | 2024.12.10 |
| ✍🏻IoC(제어의 역전), DI(의존성 주입) (0) | 2024.12.04 |
| Spring Bean (0) | 2024.12.01 |