Hexagonal Architecture란?
Hexagonal Architecture
(헥사고널 아키텍처)는 소프트웨어 설계의 한 방법론으로, 시스템을 다양한 독립적인 모듈로 나누어 각각의 모듈이 서로 독립적으로 개발되고 테스트될 수 있도록 하는 것을 목표로 한다. 이는 클린 아키텍처(Clean Architecture)나 포트와 어댑터 아키텍처(Ports and Adapters Architecture)라고도 불린다. Alistair Cockburn에 의해 제안된 이 아키텍처는 애플리케이션의 핵심 비즈니스 로직을 외부 환경과 독립적으로 만들고, 다양한 외부 서비스나 사용자 인터페이스와의 통합을 유연하게 할 수 있도록 돕는다.
Hexagonal Architecture의 주요 구성 요소
1. 핵심 도메인 (Core Domain)
- 헥사고널 아키텍처의 중심에는 비즈니스 로직과 도메인 모델이 위치한다. 이는 애플리케이션의 핵심 기능과 규칙을 정의하며, 외부의 어떤 것도 이 핵심 도메인에 직접 접근할 수 없다.
- 예를 들어,
OrderService
는 주문 생성, 취소 등의 비즈니스 로직을 포함하고,OrderRepository
는 주문 데이터를 저장하고 불러오는 인터페이스를 제공한다.
2. 포트 (Ports)
- 포트는 시스템과 외부 세계 간의 인터페이스를 정의한다. 포트는 애플리케이션의 핵심 도메인이 외부와 어떻게 상호작용하는지를 규정하며, 이 포트를 통해 입력(입력 포트)과 출력을 처리한다.
- 예를 들어,
OrderPort
는 주문과 관련된 입력/출력 인터페이스를 정의한다.
3. 어댑터 (Adapters)
- 어댑터는 포트를 구현하여 실제로 외부 시스템과 상호작용하는 부분이다. 예를 들어, 데이터베이스 어댑터, 웹 어댑터, 메시징 시스템 어댑터 등이 있다. 어댑터는 포트에 정의된 인터페이스를 구현하여 핵심 도메인과의 통신을 담당한다.
- 예를 들어,
OrderController
는 HTTP 요청을 받아OrderService
에 전달하는 입력 어댑터이고,JpaOrderRepository
는 JPA를 사용하여 데이터베이스와 상호작용하는 출력 어댑터이다.
Hexagonal Architecture의 장점
1. 유지보수성
- 비즈니스 로직과 인프라스트럭처 코드의 분리를 통해 코드의 복잡성을 줄이고, 유지보수를 용이하게 한다.
2. 확장성
- 새로운 기능을 추가할 때 기존 코드를 최소한으로 변경하도록 돕는다.
- 다양한 외부 시스템과 쉽게 통합할 수 있다.
3. 테스트 가능성
- 비즈니스 로직이 외부 시스템과 독립적으로 동작하기 때문에, 테스트를 쉽게 작성할 수 있다.
- Mock 객체를 사용하여 포트와 어댑터를 테스트할 수 있다.
4. 유연성
- 시스템의 유연성을 극대화한다. 예를 들어, 데이터베이스를 변경하거나 새로운 외부 시스템과의 통합이 필요할 때, 핵심 도메인에 영향을 주지 않고 어댑터만 교체하거나 추가하면 된다.
5. 의존성 규칙
- 의존성은 항상 바깥쪽에서 안쪽으로 향한다. 즉, 어댑터가 포트와 핵심 도메인에 의존하지만, 포트와 핵심 도메인은 어댑터에 의존하지 않는다.
Hexagonal Architecture의 예시
Core Domain
public class Order {
private Long id;
private String description;
// getter와 setter
}
public interface OrderRepository {
void save(Order order);
Order findById(Long id);
}
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void createOrder(Order order) {
// 비즈니스 로직
orderRepository.save(order);
}
}
Ports
public interface OrderPort {
void createOrder(Order order);
}
Adapters
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
public void createOrder(@RequestBody Order order) {
orderService.createOrder(order);
}
}
@Repository
public class JpaOrderRepository implements OrderRepository {
// JPA 관련 코드
@Override
public void save(Order order) {
// 데이터베이스 저장 로직
}
@Override
public Order findById(Long id) {
// 데이터베이스 조회 로직
}
}
왜 Hexagonal Architecture를 사용하는가?
1. 복잡한 시스템의 모듈화
헥사고널 아키텍처는 복잡한 시스템을 모듈화하여 각각의 모듈이 독립적으로 개발되고 테스트될 수 있도록 한다. 이는 코드의 이해와 유지보수를 용이하게 만든다.
2. 외부 의존성의 최소화
애플리케이션의 핵심 도메인이 외부 의존성으로부터 독립적이기 때문에, 외부 시스템의 변경이 핵심 도메인에 영향을 미치지 않는다. 이는 시스템의 안정성을 높인다.
3. 변경에 유연한 대응
새로운 요구사항이나 기능 추가가 발생했을 때, 기존 코드에 최소한의 변경만 가하면 된다. 어댑터만 교체하거나 추가하여 외부 시스템과의 통합을 유연하게 처리할 수 있다.
4. 테스트 용이성
비즈니스 로직이 외부 시스템과 분리되어 있어 단위 테스트와 통합 테스트가 용이하다. 이는 품질 높은 소프트웨어를 개발하는 데 큰 도움이 된다.
SOLID 원칙과의 연관성
헥사고널 아키텍처는 SOLID 원칙과 잘 맞아떨어진다. SOLID 원칙은 객체 지향 프로그래밍에서 코드의 유지보수성과 확장성을 높이기 위한 다섯 가지 원칙을 말한다.
1. 단일 책임 원칙 (Single Responsibility Principle, SRP)
각 모듈이 하나의 책임만을 가지며, 이는 헥사고널 아키텍처에서 핵심 도메인, 포트, 어댑터의 역할을 분리하는 것과 일치한다.
2. 개방-폐쇄 원칙 (Open/Closed Principle, OCP)
시스템은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다. 새로운 기능 추가 시 기존 코드를 최소한으로 변경하도록 설계된 헥사고널 아키텍처는 이 원칙을 잘 따른다.
3. 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
서브타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다. 이는 포트와 어댑터의 인터페이스 구현에서 중요한 역할을 한다.
4. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다. 포트는 특정 기능을 위해 분리된 인터페이스를 제공함으로써 이 원칙을 따른다.
5. 의존 역전 원칙 (Dependency Inversion Principle, DIP)
고수준 모듈은 저수준 모듈에 의존해서는 안 된다. 둘 다 추상화에 의존해야 한다. 헥사고널 아키텍처에서 도메인은 포트에 의존하고, 포트는 어댑터에 의존하지 않는 구조로 이 원칙을 준수한다.
마무리
Hexagonal Architecture는 복잡한 시스템을 모듈화하고 유지보수성을 높이며, 변경 사항이 발생해도 안정적인 시스템을 유지할 수 있도록 도와준다. 비즈니스 로직을 외부 환경과 독립적으로 유지할 수 있어, 다양한 요구사항 변화에 유연하게 대응할 수 있다. 이러한 구조 덕분에 애플리케이션은 변경에 유연하게 대응할 수 있고, 다양한 외부 시스템과 쉽게 통합될 수 있다. SOLID 원칙과의 조화를 통해 헥사고널 아키텍처는 더욱 강력하고 유연한 소프트웨어 설계를 가능하게 한다.
참고
https://mesh.dev/20210910-dev-notes-007-hexagonal-architecture/