Hexagonal Architecture
Hexagonal Architecture(헥사고날 아키텍처, Ports and Adapters 아키텍처) 심층 분석
1. 태그 정리
- Hexagonal-Architecture
- Ports-and-Adapters
- Domain-Driven-Design
- Structural-Patterns
2. 분류 구조 분석 및 개선 제안
현재 분류:
- Software Engineering > Design and Architecture > Architecture Styles and Patterns > Architecture Styles > Structural > Clean Architecture
분석 및 개선안
Hexagonal Architecture(헥사고날 아키텍처, Ports and Adapters) 는 Clean Architecture, Onion Architecture 등과 핵심 철학을 공유하지만, 본질적으로는 독립적인 아키텍처 스타일입니다. Clean Architecture 내부 하위 분류로 보다는 Structural 의 바로 아래에 위치시키는 것이 더 명확합니다.
제안:
- Software Engineering > Design and Architecture > Architecture Styles and Patterns > Architecture Styles > Structural > Hexagonal Architecture
3. 200 자 요약 설명
헥사고날 아키텍처는 비즈니스 로직과 외부 시스템 (데이터베이스, UI 등) 을 포트 (Ports) 와 어댑터 (Adapters) 구조로 분리해 느슨하게 결합된 시스템을 구현하는 아키텍처 패턴입니다. 이를 통해 핵심 로직은 외부 기술 변화에 영향을 받지 않고, 높은 유연성과 테스트 용이성이 확보됩니다 135.
4. 전체 개요 (250 자)
Hexagonal Architecture(헥사고날 아키텍처, Ports and Adapters 패턴) 는 1990 년대 Alistair Cockburn 이 제안한 소프트웨어 아키텍처 스타일로, 애플리케이션의 비즈니스 로직 (코어) 과 외부 요소 (Databases, UI, 외부 API 등) 를 확실하게 구분하고, 포트 (Port) 와 어댑터 (Adapter) 를 통해 통신함으로써 높은 모듈성, 확장성, 유지보수성을 달성합니다. 도메인 주도 설계 (DDD), 자동화 테스트, 기술 교체 등을 유연하게 적용할 수 있어 현대 개발에서 널리 활용되어 왔습니다 24.
5. 핵심 개념 및 실무 연관성
헥사고날 아키텍처 핵심 개념
- 코어 도메인 (비즈니스 로직) 보호: 핵심 로직 (Entities, Domain) 이 외부 환경과 독립적으로 존재.
- 포트 (Port): 코어와 외부를 연결하는 경계 역할, 추상 인터페이스.
- 인바운드 포트 (Inbound Port): 외부에서 코어로의 입력 통로 (API, UI 등)
- 아웃바운드 포트 (Outbound Port): 코어에서 외부로의 출력 통로 (DB, 메시징 등)
- 어댑터 (Adapter): 외부 요소 (데이터베이스, UI, API, 테스트 등) 와 포트 간 변환자 역할
- 의존성 방향: 항상 외부가 코어에 의존 (의존성 역전)
실무 연관성
- 테스트 코드가 외부 영향없이 순수하게 코어 로직만을 검증할 수 있음
- 새로운 외부 시스템 추가 또는 기존 시스템 교체 시, 어댑터만 수정/추가 → 유지보수가 용이
6. 주제별 심층 조사 분석
등장 및 발전 배경
- 객체지향 아키텍처에서 Layered Architecture 의 결합도 한계를 극복
- UI 코드, DB 코드와 비즈니스 로직이 뒤섞이며 유지보수성 저하, 테스트 자동화 어려움
- 알리스터 코크번 (Alistair Cockburn) 이 1990 년대 개념을 처음 제안, 2005 년 Ports and Adapters 라는 명칭으로 정립 1
목적 및 필요성
구분 | 목적 및 필요성 | 설명 |
---|---|---|
목적 | 도메인 로직 보호 | 기술 변화/외부 환경과 무관한 핵심 로직 보존 |
필요성 | 변화 대응성 | UI, DB, 메시징 등 외부 시스템 교체에 유연하게 대응 |
주요 기능 및 역할 (관계 구조)
- 기능
- 포트 및 어댑터를 통한 외부와 내부 (코어) 분리
- 각종 인터페이스를 통해 다양한 외부 시스템 수용
- 역할
- 도메인 로직과 인프라 구현체의 완전한 분리
- 테스트 자동화 및 기술 교체 용이성 제공
관계: 포트와 어댑터는 항상 코어 (도메인) 에만 의존
특징
- 완전한 결합도 분리 (Separation of Concerns)
- 새로운 외부시스템 (외부 API, 데이터 시스템 등) 손쉬운 도입
- 테스트 용이성, 스터빙 (Stub)·목 (Mock) 테스트 지원
- 도메인 드리븐 설계 (DDD) 와 강한 연계성
핵심 원칙
- 외부 시스템은 반드시 지정된 포트 (인터페이스) 를 통해서만 접근 가능
- 어댑터는 포트와 외부 시스템 간의 데이터/프로토콜 변환 담당
- 핵심 로직 (코어) 는 외부 컴포넌트에 절대 의존하지 않음
주요 원리 및 작동 원리 (다이어그램)
flowchart TD subgraph Core[코어(도메인/비즈니스 로직)] E[Entity/Domain] --> U[Use Case or Service] end subgraph Ports[포트] PI[Inbound Port (Input)] -.-> U PO[Outbound Port (Output)] -.-> U end subgraph Adapters[어댑터] AI[Adapter-In (예: REST API, UI)] --> PI AO[Adapter-Out (예: DB, MessageQueue)] --> PO end AI -.-> Core AO -.-> Core
설명:
- 코어 (도메인) 는 오직 자신의 규칙만 가진다.
- 포트 (Interface) 는 도메인 서비스의 입력/출력 경계
- 어댑터 (Adaptter) 는 실제 입력/출력을 담당하며, 인바운드 (사용자/외부 입력), 아웃바운드 (데이터저장/API 등) 로 구분
필수/선택 구성요소 정리
구분 | 구성요소 | 역할 | 비고 |
---|---|---|---|
필수 | 도메인 (엔티티) | 핵심 비즈니스 로직 | DB, UI, 외부 무관 |
필수 | 포트 (Port) | 경계 인터페이스 | 인바운드/아웃바운드 구분 |
필수 | 어댑터 (Adapter) | 변환자 | 실제 구현체 연결 |
선택 | 서비스계층 | 복잡한 도메인 로직 관리 | 복잡도/규모마다 필요에 따라 |
구현 기법 및 방법
- DI(Dependency Injection, 의존성 주입) 사용
- 포트 (Interface) 작성 → 어댑터에서 구현체 주입
- 언어/프레임워크에 무관하게 유연성 있는 구조 설계
- 외부 시스템 추가 (eg: GraphQL, gRPC 등) 는 포트와 어댑터만 추가
장점
구분 | 항목 | 설명 |
---|---|---|
장점 | 유지보수성 | 포트/어댑터만 수정하여 기술 교체 가능, 핵심로직 불변 4 |
테스트 용이성 | 코어만 독립테스트 가능, TDD·BDD 적용 수월 35 | |
확장·유연성 | 포트와 어댑터만 추가하면 새로운 인터페이스 수용 3 | |
개발 병렬화 | 팀별로 코어와 어댑터 개발 분담 가능 5 |
단점과 문제점 그리고 해결방안
단점
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | 초기 복잡성 | 다양한 인터페이스/어댑터 작성 | 점진적 도입, 템플릿 활용 |
러닝커브 | 설계 패턴 학습필수 | 실전 예제, 문서화 | |
작은 시스템 과적합 | 단순한 시스템에 불필요 복잡성 | 프로젝트 규모 고려 적용 |
문제점
구분 | 항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|---|
문제점 | 어댑터 관리 증가 | 외부 시스템 다양화 | 유지보수 비용 증가 | 의존성 분석 | 관심사 분리 철저 | 어댑터 통합, 도구화 |
디버깅 복잡성 | 여러 계층·추상화 | 문제 추적 어려움 | 상세 로깅 | 계층별 로깅 | 로그, 트레이싱 도구 사용 |
도전 과제 (최신 트렌드 반영)
- 마이크로서비스 (MSA) 와의 자연스러운 연동
- 다양한 외부 인터페이스 (GRPC, GraphQL, Event-Driven) 지원
- 분산 트레이싱 및 관측성 향상
- 대규모 어댑터 및 포트 관리 표준화 방안 마련
분류 기준에 따른 종류 및 유형
분류 기준 | 유형 | 설명 |
---|---|---|
적용 범위 | 전체 시스템 적용 | 모든 계층에 철저 적용 |
부분 적용 | 핵심 로직에만 적용, 일부 레거시 호환 | |
포트·어댑터 수 | 단일/복수 포트 | 단일 서비스/복합 시스템 지원 |
도메인 강조 여부 | DDD 통합형 | 도메인 주도 설계 기반 구조 |
비즈니스 단순형 | 단순 서비스에 적합 |
실무 사용 예시
적용 영역 | 사용 구조/기술 | 목적 | 효과 |
---|---|---|---|
백엔드 API | REST/GraphQL/Message | 유연한 확장, 다양한 클라이언트 호환 | 유지보수 용이/테스트 자동화 |
데이터 변환 | DB 선택 (Oracle/MySQL 등) | DB 교체/병렬 운용 | 코어 수정 없이 데이터 저장소 교체 |
외부 시스템 연결 | Adapter 통한 3rd Party 연동 | 외부 API/메시징 등 연결 | 서비스중단 없이 확장/교체 |
활용 사례
시나리오
이커머스 주문 시스템에서 REST API, 메시지 큐, 데이터베이스를 독립적으로 교체 가능한 구조로 설계함.
시스템 구성
- 도메인 (주문, 결제, 배송)
- 인바운드 어댑터 (REST API, CLI, 메시지 큐 consumer)
- 아웃바운드 어댑터 (Database, 외부 결제 API)
- 포트 정의 (각 기능 별 입력/출력 인터페이스)
시스템 구성 다이어그램
graph TD subgraph Core O[주문 도메인 서비스] P[결제 도메인 서비스] D[배송 도메인 서비스] end REST[REST API Adapter]-->O MQ[Message Queue Adapter]-->O CLI[CLI Adapter]-->O O-->|Outbound Port|DB[DB Adapter] O-->|Outbound Port|G[외부 결제 Adapter] D-->|Outbound Port|SH[Shipping API Adapter]
Workflow
- REST API/메시지 큐/CLI 등 다양한 입력 어댑터를 통해 주문 요청 수신
- 코어 도메인 (주문, 결제, 배송) 서비스에서 비즈니스 로직 실행
- 각종 아웃바운드 포트/어댑터 (DB 저장, 결제 API, 배송 API) 로 요청 전송
역할
- 인바운드 어댑터: 외부 입력 수집 및 포트 호출
- 코어 도메인: 핵심 비즈니스 로직 처리
- 아웃바운드 어댑터: 외부 시스템과의 통신 구현
유무에 따른 차이점
- 미적용 시 변경·확장 비용 증가, 테스트 어려움
- 적용 시 외부 시스템 추가/교체 용이, 자동화 테스트 가능
구현 예시 (Python)
|
|
실무에서 효과적으로 적용하기 위한 고려사항 및 권장사항
항목 | 설명 | 권장사항 |
---|---|---|
계층 (포트/어댑터) 명확화 | 추상화 인터페이스와 실제 구현 구분 | 명확한 네이밍, 코드 분리 |
테스트 전략 | 코어 단위 테스트 중심 | 모킹 (Mock), 스터빙 활용 |
문서화 및 코드리뷰 | 설계/개발 일관성 유지 | Mermaid 다이어그램 활용 |
유지보수성 | 어댑터/포트 확장성, 코드 일관성 | 모듈별 책임 명확화 |
최적화하기 위한 고려사항 및 권장사항
항목 | 설명 | 권장사항 |
---|---|---|
어댑터 성능 최적화 | I/O 나 외부 시스템 호출 최적화 | 캐싱, 비동기 처리 도입 |
DI 활용 | 객체 주입을 통한 결합도 감소 | 프레임워크 DI 컨테이너 활용 |
로깅/트레이싱 | 계층별 모니터링 | 표준화된 로그 전략, 오픈소스 도구 활용 |
주제와 관련하여 주목할 내용 정리
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
이론 | 포트와 어댑터 (Ports/Adapters) | 경계 추상화 | 도메인과 외부 결합 최소화, 규격 표준화 |
실무 | 테스트 전략 | 독립 테스트 | 어댑터 모킹 (Mock) 중심 유닛테스트 |
성능 | Adapter 최적화 | 비동기 처리 | I/O 병목 최소화, 스케일링 대응 |
확장 | DDD 조합 | 도메인 중심 | 복잡 비즈니스 로직 분할·보호 |
반드시 학습해야할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
패턴 | 디커플링 (Decoupling) | 의존성 분리 | 도메인/외부 시스템의 완전한 분리 원칙 실천 |
코딩 | DI/IoC | 의존성 주입/제어의 역전 | 프레임워크별 객체 생성, 주입 메커니즘 이해 |
아키텍처 | 계층화 설계 | 포트/어댑터, 도메인 계층 | 각 계층 역할 명확화 및 구조화 연습 |
운영 | 자동화 테스트 | 단위/통합 테스트 | 유지보수성 증진, CI/CD 연동 |
용어 정리
카테고리 | 용어 (한/영) | 설명 |
---|---|---|
아키텍처 | 포트 (Port) | 내부 (도메인) 와 외부 시스템 간 통신을 위한 인터페이스 |
아키텍처 | 어댑터 (Adapter) | 포트와 외부 시스템을 연결하는 실제 구현체 |
아키텍처 | 의존성 역전 (Dependency Inversion) | 외부 코드가 내부 (코어) 에 의존하게 설계 |
테스트 | 모킹 (Mock)/스터빙 (Stub) | 가짜 구현체를 통한 코어 테스트 |
참고 및 출처
- Hexagonal Architecture – An Introduction
- Hexagonal architecture (software) – Wikipedia
- Everything You Need to Know About Hexagonal Architecture
- Hexagonal architecture pattern - AWS Prescriptive Guidance
- Hexagonal Architecture - System Design - GeeksforGeeks
- Mastering Hexagonal Architecture
- Hexagonal Architecture/Ports And Adapters - DEV Community
Hexagonal Architecture(헥사고날 아키텍처, Ports and Adapters 아키텍처) 계속 심화 정리
헥사고날 아키텍처와 유사 아키텍처 비교
구분 | 헥사고날 아키텍처 | Layered 아키텍처 | 클린 아키텍처 (Clean Architecture) |
---|---|---|---|
구조 개념 | 코어, 포트, 어댑터로 모든 외부와 내부 분리 | (프레젠테이션 → 도메인 → 데이터) 등 계층 다중 | 엔터티, 유스케이스, 인터페이스 어댑터 등 중심 구조 |
외부 시스템 연결 방식 | 어댑터가 포트를 통해 독립적으로 연결 | 계층 간 직접 호출/의존 | 꼭 내부로만 의존성이 향하도록 설계 |
테스트 용이성 | 코어만 독립 테스트 매우 용이 | DB, UI 등 외부의존성 코드 분리 어려움 | 계층별로 단위 테스트 체계화 |
기술/환경 교체 유연성 | 매우 높음 | 낮음 | 매우 높음 |
실제 적용/확장 패턴 및 현대 트렌드
- 마이크로서비스 아키텍처 (MSA, Microservices Architecture)
각 마이크로서비스마다 헥사고날 아키텍처 적용. 서비스별 독립 배포/확장과 변경 용이성 증대. - Event Driven Architecture (이벤트 기반 아키텍처)
메시지 버스, 큐 (Queue), 이벤트 소싱 등 신규 외부 연결 도입 시 Outbound Adapter 만으로 확장이 간편. - Serverless (서버리스) 환경 통합
각 람다 (함수) 에 포트/어댑터 구조 적용, 다양한 트리거 (HTTP, 이벤트, 큐 등) 처리 일관화.
확장 적용 TIP 과 세부 실무 전략
코어 로직은 Domain Layer(도메인 계층) 만, 외부 인터페이스는 반드시 어댑터 생성
- 신규 API, 외부 공급자 (결제, 알림 등) 도입에도 핵심 비즈니스 코드에 영향 없음
Port(포트) 를 인터페이스로, Adapter(어댑터) 를 구현 클래스로 설계
Python 예시:
인바운드/아웃바운드 어댑터 의존성 분리
- 인바운드: REST, CLI, 메시지 소비자 등
- 아웃바운드: DB, 3rd party API, 큐 생산자 등
CI(Continuous Integration, 지속적 통합) 파이프라인에서 포트 - 어댑터 일관성 검증
- 계층별 Mock 테스트 (CI 파이프라인에 통합)
- 변경 감지 시 어댑터 단위로 영향 분석
추가적으로 주목할 최신 트렌드 및 도전 과제
- 클라우드 네이티브 개발: 다양한 배포 환경 (컨테이너, 클라우드 API 등) 에 어댑터 패턴 조합.
- 자동화 테스트/관측성 (Observability): 포트 기반 유닛테스트, 계층별 로깅/트레이싱으로 서비스 연계 투명성 향상.
- 대규모 시스템에서 포트/어댑터 관리 체계 표준화: 공통 어댑터, 커스텀 확장 가이드 등을 조직 차원에서 관리.
헥사고날 아키텍처를 사용해야 하는 대표적 상황
- 외부 인터페이스가 자주 변경/확장되는 상황 (예: 여러 DB, 다양한 외부 API 공급자)
- 단위 테스트/자동화 테스트가 중요한 레거시 시스템의 구조 개선
- 플러그인·모듈 구조가 필요한 대형 백엔드/엔터프라이즈/마이크로서비스 시스템
활용 시 반드시 학습해야 할 내용 (추가)
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
코딩 | Port-Driven Test | Mock 기반 테스트 | 외부 어댑터 없이 코어만 유닛테스트 실행 |
계층화 | Outbound Integration | 외부 연동 어댑터 | 다양한 외부 시스템 표준화된 방식과의 정상/오류 처리 |
관측성 | Structured Logging | 계층별 로그/트레이스 | 장애 원인 신속 탐색, 운영 자동화에 필수 |
용어 정리 (보완)
카테고리 | 용어 (한/영) | 설명 |
---|---|---|
아키텍처 | 인바운드 포트 (Inbound Port) | 외부 입력 (사용자, 타 시스템) 신호가 코어로 전달되는 진입 인터페이스 |
아키텍처 | 아웃바운드 포트 (Outbound Port) | 코어가 외부 시스템 (DB, API 등) 과 통신할 때 사용되는 출력 인터페이스 |
아키텍처 | 인바운드 어댑터 (Inbound Adapter) | 인바운드 포트에 맞춰 외부 입력을 변환하여 전달하는 구현체 |
아키텍처 | 아웃바운드 어댑터 (Outbound Adapter) | 아웃바운드 포트를 통해 외부 시스템 (데이터베이스, 외부 API 등) 에 연결하는 클래스 |
코딩 | 의존성 주입 (Dependency Injection, DI) | 외부 객체를 코드 내부가 아닌 어플리케이션 외부 (프레임워크 등) 에서 주입하는 방식 |
참고 및 출처
- Hexagonal Architecture – An Introduction
- Hexagonal architecture (software) – Wikipedia
- Everything You Need to Know About Hexagonal Architecture
- Hexagonal architecture pattern - AWS Prescriptive Guidance
- Hexagonal Architecture/Ports And Adapters - DEV Community
Hexagonal Architecture(헥사고날 아키텍처, Ports and Adapters 아키텍처)—마무리 및 종합 정리
확장 적용 시 실제 사례 중심 종합
1. 마이크로서비스 (MSA) 적용에서의 실제 예시
- 각 마이크로서비스는 자신의 도메인 (코어) 과 포트/어댑터만을 갖고 배포, 관리.
- 예: 결제 서비스, 주문 서비스, 배송 서비스가 각각 별도 프로젝트·서버로 동작, 어댑터 추가로 외부 결제 게이트웨이 또는 메시지 큐 연동만 변경.
2. 이벤트 기반 비동기 구조와 연계
- 아웃바운드 어댑터로 메시지 큐 (Kafka, RabbitMQ 등) 연동 시, 핵심 도메인 로직 변경 없이 새로운 메시지 타입이나 소비자 확장.
- 이벤트 기반 배포 또는 트랜잭션 관리 특화된 도메인 경계 구현에도 효과적.
3. 레거시 시스템 모던화
- 기존 시스템을 단계적으로 헥사고날 구조로 마이그레이션 시, 코어 로직 침범 없이 UI, 데이터베이스, API 등을 점진적으로 교체.
- 인바운드/아웃바운드 어댑터 구현을 통해 레거시 자산의 영향 최소화.
4. 클라우드 및 배포 환경 최적화
- 어댑터 추가로 클라우드 스토리지, 외부 클라우드 API, 서버리스 함수와 연계.
- 테스트용 어댑터로 통합테스트, QA 환경 셋업 단순화.
헥사고날 아키텍처와 기타 구조 패턴 적용 전략 요약
적용 방식 | 특징 | 상황별 추천 포인트 |
---|---|---|
헥사고날 아키텍처 | 포트/어댑터로 독립성 유지, 외부기술 교체 용이 | 기술 변화 잦음, 자동화 테스트 중시, API 확장 잦은 시스템 |
클린 아키텍처 (Clean Architecture) | 계층·도메인 중심 설계, 의존성은 내부 방향으로만 | 대규모 복합 도메인, 유지보수성 중시 |
Layered 구조 | 전통적 3 계층 (프레젠테이션/비즈니스/데이터), 직접 접근 구조 | 단순한 시스템, 소규모 레거시 개선 등에 적합 |
현업 개발 관점 주의 및 리더십 전략
- 모든 외부 의존성은 반드시 포트 (인터페이스) 로 정의하고, 새 외부 시스템 도입 시 새 어댑터 추가만으로 메인 코어 (비즈니스 로직) 의 변경 최소화.
- 설계 문서화: Mermaid, PlantUML 등으로 포트/어댑터/코어 다이어그램 지속 관리.
- 코드리뷰/CI 에서 계층간 규약 (인터페이스, 의존성 방향 등) 자동화 검사.
- 통합 및 유닛테스트에서 Mock 과 Stub 어댑터 전략 병행 (실제 서비스와 테스트 이원화).
현장 적용성 한줄 요약
헥사고날 아키텍처 (Ports and Adapters) 는 핵심 도메인 독립, 변화에 강한 플러그형 소프트웨어 설계 구조로서, 지속적 변화와 확장을 요구하는 시스템에 매우 실용적입니다.
용어 정리 (종합)
카테고리 | 용어 (한글/영어) | 설명 |
---|---|---|
아키텍처 | 헥사고날 아키텍처 (Hexagonal Architecture) | 포트/어댑터로 도메인과 외부 시스템 완전 분리 |
아키텍처 | 포트 (Port) | 코어 로직과 외부시스템 연결하는 추상 인터페이스 |
아키텍처 | 어댑터 (Adapter) | 포트에 맞춰 외부 입출력을 구체 구현하는 클래스/모듈 |
아키텍처 | 인바운드/아웃바운드 어댑터 (Inbound/Outbound Adapter) | 입력 (사용자/API)/출력 (DB/외부 API) 별 실제 어댑터 구현체 |
테스트 | 목 (Mock)/스터빙 (Stub) | 외부 시스템 없이 단위 테스트를 위해 사용되는 가짜 객체 |
아키텍처 | 의존성 주입 (Dependency Injection, DI) | 외부 객체를 내부가 아닌 외부 환경에서 주입해주는 설계 기법 |
참고 및 출처
- Hexagonal Architecture – An Introduction
- Hexagonal architecture (software) – Wikipedia
- Everything You Need to Know About Hexagonal Architecture
- Hexagonal architecture pattern - AWS Prescriptive Guidance
- Hexagonal Architecture/Ports And Adapters - DEV Community
Hexagonal Architecture (헥사고날 아키텍처) 심화 분석
1. 태그 정리
- Ports-and-Adapters
- Clean-Architecture
- Domain-Driven-Design
- Software-Architecture-Patterns
2. 분류 구조 검증
현재 분류: “Software Engineering > Design and Architecture > Architecture Styles and Patterns > Architecture Styles > Structural > Clean Architecture”
제안하는 더 적절한 분류:
|
|
근거: Hexagonal Architecture 는 Clean Architecture 의 하위 개념이 아니라, Domain-Driven Design 과 밀접한 관련이 있는 독립적인 아키텍처 패턴으로, 도메인 중심 아키텍처로 분류하는 것이 더 적절합니다.
3. 주제 요약 설명 (200 자 내외)
Hexagonal Architecture (헥사고날 아키텍처) 는 Alistair Cockburn 이 2005 년에 제안한 “Ports and Adapters” 패턴으로, 비즈니스 로직을 외부 의존성으로부터 격리하여 테스트 가능하고 유지보수가 용이한 소프트웨어를 설계하는 아키텍처 패턴입니다. 포트와 어댑터를 통해 핵심 도메인과 외부 시스템 간의 결합도를 낮춥니다.
4. 전체 개요 (250 자 내외)
Hexagonal Architecture 는 소프트웨어의 핵심 비즈니스 로직을 중앙에 배치하고, 외부 시스템과의 상호작용을 포트 (Port) 와 어댑터 (Adapter) 를 통해 관리하는 아키텍처 패턴입니다. 이를 통해 데이터베이스, UI, 외부 API 등의 기술적 세부사항 변경 시에도 비즈니스 로직의 수정 없이 대응할 수 있어 시스템의 유연성과 테스트 용이성을 크게 향상시킵니다.
5. 핵심 개념
5.1 핵심 개념
- 도메인 코어 (Domain Core): 비즈니스 규칙과 로직이 구현된 애플리케이션의 중심부
- 포트 (Port): 도메인과 외부 세계 간의 인터페이스로, 의도를 나타내는 계약
- 어댑터 (Adapter): 포트의 구현체로, 특정 기술과 도메인을 연결하는 역할
- 의존성 역전 (Dependency Inversion): 외부 계층이 내부 계층에 의존하도록 하는 원칙
- Primary Port/Adapter: 시스템을 구동하는 입력 측면 (Driving Side)
- Secondary Port/Adapter: 시스템이 구동하는 출력 측면 (Driven Side)
5.2 실무 구현을 위한 연관성
- 도메인 모델링: Domain-Driven Design (DDD) 와 연계하여 엔티티, 값 객체, 애그리게이트 설계
- 인터페이스 설계: SOLID 원칙 중 인터페이스 분리 원칙과 의존성 역전 원칙 적용
- 의존성 주입: Spring Framework, Guice 등을 활용한 런타임 의존성 주입 구현
- 테스트 전략: 단위 테스트, 통합 테스트, 모킹 전략 수립
- 프로젝트 구조: 패키지 구조와 모듈화를 통한 아키텍처 경계 강화
6. 세부 조사 내용
등장 및 발전 배경
Hexagonal Architecture 는 2005 년 Alistair Cockburn 에 의해 제안되었습니다. 전통적인 계층형 아키텍처의 한계를 극복하기 위해 개발되었으며, 특히 다음과 같은 문제들을 해결하고자 했습니다:
- 계층 간 원치 않는 의존성 문제
- UI 코드와 비즈니스 로직의 혼재
- 테스트의 어려움
- 외부 시스템 변경 시 전체 시스템에 미치는 영향
2005 년 처음 “Hexagonal Architecture” 로 명명되었다가 “Ports and Adapters” 로 개명되었으며, 2024 년 Cockburn 은 Juan Manuel Garrido de Paz 와 함께 이에 대한 종합적인 책을 출간했습니다.
목적 및 필요성
달성하고자 하는 목적:
- 비즈니스 로직의 기술적 세부사항으로부터의 격리
- 높은 테스트 가능성 확보
- 외부 시스템 변경에 대한 유연한 대응
- 애플리케이션의 장기적 유지보수성 향상
필요성:
- 빠르게 변화하는 기술 환경에 대한 적응 필요
- 복잡한 비즈니스 로직의 체계적 관리 요구
- 마이크로서비스 아키텍처로의 전환 준비
- 지속적인 통합/배포 환경에서의 테스트 자동화 필요
주요 기능 및 역할
기능:
- 비즈니스 로직 격리: 핵심 도메인을 외부 의존성으로부터 분리
- 인터페이스 정의: 명확한 계약을 통한 시스템 간 상호작용
- 기술 교체 지원: 어댑터 교체를 통한 기술 스택 변경 지원
- 테스트 지원: 모킹과 더미 구현을 통한 테스트 용이성 제공
역할:
- 아키텍처 패턴: 시스템 전체 구조를 정의하는 청사진 역할
- 설계 원칙: 개발팀의 의사결정 가이드라인 제공
- 품질 보증: 코드 품질과 시스템 안정성 확보
- 변화 관리: 요구사항 변경과 기술 진화에 대한 대응 체계
특징
- 중심부 격리: 비즈니스 로직이 기술적 세부사항과 완전히 분리됨
- 대칭적 구조: 모든 외부 인터페이스를 동일한 방식으로 처리
- 계약 기반: 인터페이스를 통한 명확한 의존성 정의
- 테스트 용이성: 각 계층의 독립적 테스트 가능
- 기술 독립성: 특정 프레임워크나 라이브러리에 종속되지 않음
핵심 원칙
- 의존성 방향 제어: 모든 의존성은 중심부를 향해야 함
- 인터페이스 우선: 구현체가 아닌 인터페이스에 의존
- 단일 책임: 각 어댑터는 하나의 외부 시스템만 담당
- 관심사 분리: 비즈니스 로직과 기술적 관심사 완전 분리
- 설정 외부화: 어댑터 연결은 설정을 통해 런타임에 결정
주요 원리 및 작동 원리
graph TB subgraph "External Systems" UI[User Interface] CLI[Command Line] API[REST API] DB[(Database)] EXT[External Services] FILE[File System] end subgraph "Adapters Layer" PA1[Primary Adapter<br/>Web Controller] PA2[Primary Adapter<br/>CLI Handler] SA1[Secondary Adapter<br/>DB Repository] SA2[Secondary Adapter<br/>File Storage] SA3[Secondary Adapter<br/>HTTP Client] end subgraph "Ports Layer" PP1[Primary Port<br/>Use Case Interface] SP1[Secondary Port<br/>Repository Interface] SP2[Secondary Port<br/>Storage Interface] SP3[Secondary Port<br/>External API Interface] end subgraph "Domain Core" UC[Use Cases<br/>Application Services] ENT[Entities] VO[Value Objects] BR[Business Rules] end UI --> PA1 CLI --> PA2 API --> PA1 PA1 --> PP1 PA2 --> PP1 PP1 --> UC UC --> SP1 UC --> SP2 UC --> SP3 SP1 --> SA1 SP2 --> SA2 SP3 --> SA3 SA1 --> DB SA2 --> FILE SA3 --> EXT UC --> ENT UC --> VO UC --> BR
작동 원리:
- 요청 입력: Primary Adapter 가 외부 요청을 받아 Primary Port 를 통해 전달
- 비즈니스 처리: Use Case 가 비즈니스 로직을 실행하며 필요시 Secondary Port 호출
- 외부 연동: Secondary Adapter 가 실제 외부 시스템과 통신하여 결과 반환
- 응답 반환: 처리 결과가 역순으로 Primary Adapter 를 통해 외부로 전달
구조 및 아키텍처
Hexagonal Architecture 는 다음과 같은 핵심 구성요소로 이루어집니다:
필수 구성요소
1. Domain Core (도메인 코어)
- 기능: 비즈니스 규칙과 로직의 구현
- 역할: 애플리케이션의 핵심 가치 제공
- 특징: 외부 의존성이 전혀 없는 순수한 비즈니스 로직
2. Primary Ports (주 포트)
- 기능: 외부에서 시스템으로의 입력 인터페이스 정의
- 역할: Use Case 실행을 위한 계약 명세
- 특징: 의도를 표현하는 인터페이스
3. Secondary Ports (부 포트)
- 기능: 시스템에서 외부로의 출력 인터페이스 정의
- 역할: 외부 시스템과의 통신 계약 명세
- 특징: 필요한 서비스에 대한 추상화
4. Primary Adapters (주 어댑터)
- 기능: 외부 입력을 도메인이 이해할 수 있는 형태로 변환
- 역할: 다양한 입력 채널 지원
- 특징: 특정 기술 (HTTP, CLI, GUI 등) 에 특화
5. Secondary Adapters (부 어댑터)
- 기능: 도메인의 요청을 외부 시스템 호출로 변환
- 역할: 외부 시스템과의 실제 통신 담당
- 특징: 특정 기술 (DB, HTTP Client 등) 에 특화
선택 구성요소
1. Application Services (애플리케이션 서비스)
- 기능: Use Case 의 오케스트레이션
- 역할: 비즈니스 워크플로우 관리
- 특징: 트랜잭션과 보안 등 횡단 관심사 처리
2. Domain Events (도메인 이벤트)
- 기능: 도메인 내 중요한 비즈니스 이벤트 표현
- 역할: 시스템 간 느슨한 결합 지원
- 특징: 비동기 처리와 이벤트 소싱 지원
구현 기법 및 방법
1. 의존성 주입 (Dependency Injection)
정의: 런타임에 의존성을 외부에서 주입하는 기법
구성:
- IoC Container (Spring, CDI)
- 설정 파일 또는 어노테이션
- 생성자 주입, 세터 주입, 필드 주입
목적:
- 어댑터의 동적 교체 지원
- 테스트 시 Mock 객체 주입
- 설정 기반 시스템 구성
실제 예시:
|
|
2. 포트 인터페이스 설계 (Port Interface Design)
정의: 도메인과 외부 세계 간의 계약을 정의하는 기법
구성:
- 인터페이스 정의
- 데이터 전송 객체 (DTO)
- 예외 정의
목적:
- 명확한 계약 정의
- 구현체 교체 가능성
- 테스트 용이성
실제 예시:
|
|
3. 어댑터 패턴 구현 (Adapter Pattern Implementation)
정의: 서로 다른 인터페이스를 연결하는 구조적 디자인 패턴
구성:
- Target Interface (포트)
- Adaptee (외부 시스템)
- Adapter (포트와 외부 시스템 연결)
목적:
- 기존 시스템과의 호환성
- 제 3 자 라이브러리 통합
- 기술 교체 용이성
실제 예시:
|
|
장점
구분 | 항목 | 설명 |
---|---|---|
장점 | 높은 테스트 가능성 | 의존성 격리로 인해 단위 테스트와 통합 테스트가 용이하며, Mock 객체를 통한 독립적 테스트 가능 |
장점 | 기술 독립성 | 비즈니스 로직이 특정 프레임워크나 데이터베이스에 종속되지 않아 기술 교체 시 영향 최소화 |
장점 | 유연한 확장성 | 새로운 어댑터 추가만으로 다양한 외부 시스템과 통합 가능 |
장점 | 명확한 관심사 분리 | 도메인 로직과 기술적 세부사항이 명확히 분리되어 코드 이해와 유지보수 용이 |
장점 | 병렬 개발 지원 | 인터페이스 정의 후 팀별로 독립적인 개발 진행 가능 |
장점 | 재사용성 향상 | 핵심 비즈니스 로직을 다양한 컨텍스트에서 재사용 가능 |
단점과 문제점 그리고 해결방안
단점
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | 초기 복잡성 증가 | 포트와 어댑터 설계로 인한 초기 개발 복잡도 상승 | 점진적 적용과 팀 교육을 통한 학습 곡선 완화 |
단점 | 코드량 증가 | 인터페이스와 구현체 분리로 인한 보일러플레이트 코드 증가 | 코드 생성 도구와 템플릿 활용 |
단점 | 성능 오버헤드 | 추상화 계층으로 인한 미미한 성능 저하 | 크리티컬한 부분에 대한 성능 프로파일링과 최적화 |
단점 | 과도한 추상화 위험 | 불필요한 추상화로 인한 시스템 복잡성 증가 | YAGNI 원칙 적용과 적절한 추상화 수준 유지 |
문제점
구분 | 항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|---|
문제점 | 인터페이스 불일치 | 포트와 어댑터 간 계약 불일치 | 런타임 오류 발생 | 계약 테스트, 타입 검사 | 강타입 언어 사용, API 문서화 | 계약 테스트 도입, Consumer-Driven Contract |
문제점 | 순환 의존성 | 잘못된 의존성 방향 설정 | 시스템 결합도 증가 | 의존성 분석 도구 | 의존성 방향 규칙 정의 | 아키텍처 테스트, 리팩토링 |
문제점 | 어댑터 누수 | 도메인에 기술적 세부사항 침투 | 아키텍처 일관성 훼손 | 정적 분석, 코드 리뷰 | 아키텍처 가이드라인 수립 | 리팩토링, 교육 강화 |
도전 과제
1. 아키텍처 경계 유지
원인: 개발자의 편의나 일정 압박으로 인한 경계 침범
영향: 아키텍처 일관성 훼손과 장기적 유지보수 비용 증가
해결 방법:
- ArchUnit 과 같은 아키텍처 테스트 도구 도입
- 지속적인 코드 리뷰와 정적 분석
- 명확한 아키텍처 가이드라인 수립
2. 마이크로서비스 전환
원인: 모놀리스에서 마이크로서비스로의 분해 과정에서 발생하는 복잡성
영향: 서비스 간 통신 복잡도 증가와 데이터 일관성 문제
해결 방법:
- Domain-Driven Design 의 Bounded Context 활용
- Event Sourcing 과 CQRS 패턴 적용
- 단계적 분해 전략 수립
3. 성능 최적화
원인: 추상화 계층으로 인한 성능 오버헤드
영향: 응답시간 증가와 처리량 감소
해결 방법:
- 성능 크리티컬한 부분의 직접 최적화
- 캐싱 전략 도입
- 비동기 처리 패턴 적용
분류 기준에 따른 종류 및 유형
분류 기준 | 종류/유형 | 설명 |
---|---|---|
적용 범위 | 단일 바운디드 컨텍스트 | 하나의 도메인 영역에만 적용 |
적용 범위 | 멀티 바운디드 컨텍스트 | 여러 도메인 영역에 걸쳐 적용 |
시스템 규모 | 모놀리식 헥사고날 | 단일 배포 단위로 구성 |
시스템 규모 | 마이크로서비스 헥사고날 | 각 서비스별로 헥사고날 구조 적용 |
기술 스택 | 순수 헥사고날 | 프레임워크 의존성 최소화 |
기술 스택 | 프레임워크 기반 | Spring, Quarkus 등 프레임워크 활용 |
복잡도 | 단순 헥사고날 | 기본적인 포트와 어댑터만 사용 |
복잡도 | 고급 헥사고날 | 이벤트 소싱, CQRS 등 고급 패턴 결합 |
실무 사용 예시
사용 목적 | 함께 사용되는 기술 | 효과 |
---|---|---|
웹 애플리케이션 개발 | Spring Boot, JPA, REST API | 비즈니스 로직과 웹 계층 분리로 테스트 용이성 증대 |
마이크로서비스 구축 | Docker, Kubernetes, Event Bus | 서비스 간 결합도 감소와 독립적 배포 가능 |
레거시 시스템 현대화 | API Gateway, Message Queue | 점진적 마이그레이션과 리스크 최소화 |
도메인 주도 설계 | DDD, Event Sourcing, CQRS | 복잡한 비즈니스 로직의 체계적 관리 |
클라우드 네이티브 앱 | AWS Lambda, Cloud Functions | 서버리스 환경에서의 비즈니스 로직 재사용 |
활용 사례
시나리오: 전자상거래 주문 관리 시스템
시스템 구성:
- 주문 도메인 서비스 (Order Domain Service)
- 재고 관리 시스템 (Inventory Management)
- 결제 게이트웨이 (Payment Gateway)
- 알림 서비스 (Notification Service)
- 웹 UI 및 모바일 앱
시스템 구성 다이어그램:
graph TB subgraph "External Systems" WEB[Web UI] MOBILE[Mobile App] ADMIN[Admin Panel] PG[Payment Gateway] EMAIL[Email Service] SMS[SMS Service] INVENTORY[(Inventory DB)] ORDER_DB[(Order DB)] end subgraph "Primary Adapters" REST[REST Controller] GRAPHQL[GraphQL Resolver] CLI[CLI Handler] end subgraph "Primary Ports" CREATE_ORDER[Create Order Port] CANCEL_ORDER[Cancel Order Port] VIEW_ORDER[View Order Port] end subgraph "Order Domain Core" ORDER_SERVICE[Order Service] ORDER_ENTITY[Order Entity] ORDER_RULES[Business Rules] end subgraph "Secondary Ports" PAYMENT_PORT[Payment Port] INVENTORY_PORT[Inventory Port] NOTIFICATION_PORT[Notification Port] PERSISTENCE_PORT[Order Repository Port] end subgraph "Secondary Adapters" PAYMENT_ADAPTER[Payment Adapter] INVENTORY_ADAPTER[Inventory Adapter] EMAIL_ADAPTER[Email Adapter] SMS_ADAPTER[SMS Adapter] DB_ADAPTER[Database Adapter] end WEB --> REST MOBILE --> REST ADMIN --> GRAPHQL REST --> CREATE_ORDER REST --> CANCEL_ORDER GRAPHQL --> VIEW_ORDER CLI --> CREATE_ORDER CREATE_ORDER --> ORDER_SERVICE CANCEL_ORDER --> ORDER_SERVICE VIEW_ORDER --> ORDER_SERVICE ORDER_SERVICE --> ORDER_ENTITY ORDER_SERVICE --> ORDER_RULES ORDER_SERVICE --> PAYMENT_PORT ORDER_SERVICE --> INVENTORY_PORT ORDER_SERVICE --> NOTIFICATION_PORT ORDER_SERVICE --> PERSISTENCE_PORT PAYMENT_PORT --> PAYMENT_ADAPTER INVENTORY_PORT --> INVENTORY_ADAPTER NOTIFICATION_PORT --> EMAIL_ADAPTER NOTIFICATION_PORT --> SMS_ADAPTER PERSISTENCE_PORT --> DB_ADAPTER PAYMENT_ADAPTER --> PG INVENTORY_ADAPTER --> INVENTORY EMAIL_ADAPTER --> EMAIL SMS_ADAPTER --> SMS DB_ADAPTER --> ORDER_DB
Workflow:
- 고객이 웹/모바일 앱에서 주문 생성 요청
- REST Controller 가 요청을 받아 Create Order Port 호출
- Order Service 가 비즈니스 규칙 검증 및 주문 엔티티 생성
- 재고 확인을 위해 Inventory Port 를 통해 재고 시스템 조회
- 결제 처리를 위해 Payment Port 를 통해 결제 게이트웨이 호출
- 주문 정보를 Order Repository Port 를 통해 데이터베이스에 저장
- 주문 확인 알림을 Notification Port 를 통해 이메일/SMS 발송
역할:
- Primary Adapters: 다양한 클라이언트 요청을 도메인이 이해할 수 있는 형태로 변환
- Domain Core: 주문 생성, 검증, 상태 관리 등 핵심 비즈니스 로직 처리
- Secondary Adapters: 외부 시스템과의 실제 통신 담당
유무에 따른 차이점:
- 적용 전: 비즈니스 로직이 컨트롤러와 데이터 접근 계층에 분산되어 테스트와 유지보수 어려움
- 적용 후: 명확한 책임 분리로 테스트 용이성 증대, 외부 시스템 변경 시 영향 최소화
구현 예시:
|
|
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 주의할 점 | 권장사항 |
---|---|---|---|
설계 단계 | 도메인 경계 명확화 | 과도한 세분화 방지 | DDD 의 Bounded Context 활용 |
개발 단계 | 인터페이스 우선 설계 | 구현 세부사항 노출 금지 | 계약 기반 개발 적용 |
테스트 단계 | 계층별 테스트 전략 수립 | 과도한 Mock 사용 주의 | Test Pyramid 원칙 준수 |
배포 단계 | 설정 외부화 | 하드코딩된 의존성 방지 | 환경별 설정 분리 |
유지보수 | 아키텍처 문서화 | 문서와 코드 불일치 | 코드로서의 문서화 |
팀 협업 | 개발팀 교육 | 학습 곡선 무시 | 점진적 도입과 멘토링 |
최적화하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 주의할 점 | 권장사항 |
---|---|---|---|
성능 | 추상화 오버헤드 최소화 | 불필요한 계층 추가 방지 | 성능 크리티컬 부분 직접 최적화 |
메모리 | 객체 생성 비용 관리 | 과도한 객체 래핑 주의 | 객체 풀링과 캐싱 활용 |
네트워크 | 외부 호출 최적화 | 동기 호출의 성능 영향 | 비동기 처리와 배치 처리 |
데이터베이스 | 쿼리 최적화 | N+1 문제 발생 주의 | 배치 로딩과 쿼리 튜닝 |
확장성 | 수평적 확장 고려 | 상태 공유 문제 | 무상태 설계와 이벤트 기반 아키텍처 |
모니터링 | 관찰 가능성 확보 | 디버깅 복잡성 증가 | 분산 추적과 로깅 전략 |
9. 주제와 관련하여 주목할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
아키텍처 패턴 | Clean Architecture | 계층형 구조 | Uncle Bob 의 Clean Architecture 와의 관계 및 차이점 |
아키텍처 패턴 | Onion Architecture | 동심원 구조 | Jeffrey Palermo 의 양파 아키텍처와의 유사점 |
설계 원칙 | SOLID 원칙 | 의존성 역전 | DIP 를 통한 의존성 방향 제어 |
설계 원칙 | Domain-Driven Design | 전술적 설계 | 엔티티, 값 객체, 애그리게이트와의 연계 |
테스트 전략 | Test Pyramid | 계층별 테스트 | 단위/통합/E2E 테스트 분배 전략 |
테스트 전략 | Contract Testing | API 계약 | 포트 인터페이스의 계약 검증 |
개발 방법론 | TDD | 테스트 주도 개발 | 포트 인터페이스 우선 설계 |
개발 방법론 | BDD | 행동 주도 개발 | Use Case 중심의 개발 접근법 |
기술 트렌드 | Microservices | 서비스 분해 | 마이크로서비스 아키텍처로의 진화 |
기술 트렌드 | Event Sourcing | 이벤트 기반 | 도메인 이벤트와 이벤트 소싱 |
기술 트렌드 | CQRS | 명령 - 쿼리 분리 | 읽기/쓰기 모델 분리 |
구현 기술 | Dependency Injection | IoC Container | Spring, CDI 등 DI 프레임워크 |
구현 기술 | API Gateway | 서비스 메시 | 마이크로서비스 간 통신 관리 |
구현 기술 | Message Queue | 비동기 통신 | RabbitMQ, Apache Kafka 등 |
10. 반드시 학습해야할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
기초 개념 | 소프트웨어 아키텍처 | 아키텍처 원칙 | 관심사 분리, 결합도/응집도, 추상화 |
기초 개념 | 객체지향 설계 | SOLID 원칙 | 특히 의존성 역전 원칙과 인터페이스 분리 원칙 |
설계 패턴 | GoF 디자인 패턴 | Adapter Pattern | 포트와 어댑터 구현의 핵심 패턴 |
설계 패턴 | GoF 디자인 패턴 | Strategy Pattern | 다양한 구현체 교체를 위한 패턴 |
설계 패턴 | Enterprise Patterns | Repository Pattern | 데이터 접근 계층 추상화 |
설계 패턴 | Enterprise Patterns | Service Layer Pattern | 비즈니스 로직 계층 구성 |
도메인 설계 | Domain-Driven Design | 전략적 설계 | Bounded Context, Context Map |
도메인 설계 | Domain-Driven Design | 전술적 설계 | Entity, Value Object, Aggregate |
테스트 기법 | Test-Driven Development | 단위 테스트 | Mock, Stub 을 활용한 격리된 테스트 |
테스트 기법 | Integration Testing | 계약 테스트 | 포트 인터페이스 계약 검증 |
구현 기술 | Dependency Injection | IoC 원리 | 의존성 주입과 제어 역전 |
구현 기술 | Clean Code | 코드 품질 | 가독성, 유지보수성, 테스트 가능성 |
용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
아키텍처 개념 | Port (포트) | 도메인과 외부 세계 간의 인터페이스로, 의도를 나타내는 계약 |
아키텍처 개념 | Adapter (어댑터) | 포트의 구현체로, 특정 기술과 도메인을 연결하는 구성요소 |
아키텍처 개념 | Primary Port | 외부에서 시스템으로의 입력을 정의하는 인터페이스 (Driving Port) |
아키텍처 개념 | Secondary Port | 시스템에서 외부로의 출력을 정의하는 인터페이스 (Driven Port) |
아키텍처 개념 | Domain Core | 비즈니스 규칙과 로직이 구현된 애플리케이션의 핵심부 |
아키텍처 개념 | Use Case | 시스템이 제공하는 특정 기능을 나타내는 비즈니스 시나리오 |
설계 원칙 | Dependency Inversion | 고수준 모듈이 저수준 모듈에 의존하지 않도록 하는 원칙 |
설계 원칙 | Separation of Concerns | 서로 다른 관심사를 별도의 모듈로 분리하는 설계 원칙 |
설계 원칙 | Interface Segregation | 클라이언트가 사용하지 않는 인터페이스에 의존하지 않도록 하는 원칙 |
구현 패턴 | Repository Pattern | 데이터 접근 로직을 캡슐화하여 도메인 객체와 분리하는 패턴 |
구현 패턴 | Service Layer | 비즈니스 로직을 구성하고 트랜잭션을 관리하는 계층 |
구현 패턴 | DTO (Data Transfer Object) | 계층 간 데이터 전송을 위한 객체 |
테스트 개념 | Mock Object | 테스트 시 실제 객체를 대체하는 가짜 객체 |
테스트 개념 | Stub | 미리 정해진 응답을 반환하는 테스트 더블 |
테스트 개념 | Contract Test | 서비스 간 API 계약을 검증하는 테스트 |
DDD 개념 | Bounded Context | 특정 도메인 모델이 적용되는 명시적 경계 |
DDD 개념 | Aggregate | 데이터 변경의 단위로 취급되는 연관된 객체들의 클러스터 |
DDD 개념 | Domain Event | 도메인 전문가가 관심을 갖는 비즈니스 이벤트 |
참고 및 출처
- Hexagonal Architecture (Ports and Adapters) - Alistair Cockburn
- Clean Architecture by Uncle Bob - The Clean Code Blog
- Ready for changes with Hexagonal Architecture - Netflix Tech Blog
- Hexagonal Architecture – What Is It? Why Use It? - Happy Coders
- Hexagonal Architecture and Clean Architecture (with examples) - DEV Community
- Hexagonal Architecture, DDD, and Spring - Baeldung
- Domain-Driven Design (DDD) and Hexagonal Architecture in Java - Vaadin
- Hexagonal Architecture Example: digging a Spring Boot implementation - Beyond xScratch
- Designing Microservices Components using Hexagonal Architecture - Sennder
- Hexagonal Architecture - System Design - GeeksforGeeks
알겠습니다! “Hexagonal Architecture (Ports and Adapters Architecture)” 에 대한 단계별 9 번 작업을 수행한 내용을 아래와 같이 제공합니다.
1️⃣ 태그 (3‑4 개, 영어, 빈칸→‑로 표시)
- Hexagonal‑Architecture
- Ports‑and‑Adapters
- Domain‑Driven‑Design
- Testable‑Architecture
2️⃣ 분류 구조 검토 및 제안
현재 구조:
Software Engineering > Design and Architecture > Architecture Styles and Patterns > Architecture Styles > Structural > Clean Architecture
→ 적절하며, Hexagonal Architecture 는 Clean Architecture 와 동급의 패턴입니다. 더욱 정확히 표현하자면:
즉, Clean Architecture 옆에 Hexagonal Architecture 를 배치하는 것이 계층적 정확성을 높입니다.
3️⃣ 요약 (≈ 200 자)
Hexagonal Architecture 는 알리스터 콕번 (Alistair Cockburn) 이 제안한 패턴으로, 애플리케이션의 핵심 로직 (Domain) 이 외부 시스템 (UI, DB, 메시징 등) 과 분리되도록 Ports(인터페이스) 와 Adapters(구현) 를 사용해 구조화합니다. 이 방식은 느슨한 결합, 높은 응집, 테스트 용이성, 기술 독립성을 제공하며 Clean Architecture 및 DDD 와 자연스럽게 결합됩니다.(위키백과, AWS Documentation, softengbook.org, DEV Community)
4️⃣ 전체 개요 (≈ 250 자)
Hexagonal Architecture(Ports and Adapters Architecture) 는 애플리케이션 핵심인 도메인 로직을 외부 의존성 (입력, 출력, 저장소 등) 으로부터 분리하는 아키텍처 패턴입니다. 내부 핵심은 Domain 클래스와 Use Case(또는 Interactor) 로 구성되며, 외부 시스템과는 Ports(추상 인터페이스) 를 통해 통신합니다. 外部와 통신하는 구체 구현은 Adapters 가 담당하여 핵심 로직을 보호합니다. 이를 통해 기술 스택 변경, 테스트 격리, 병렬 개발이 용이해지며 Clean Architecture, Onion Architecture 와 유사한 철학을 공유합니다.(위키백과, softengbook.org, bitloops.com)
5️⃣ 핵심 개념
- Domain / Application Core: 핵심 비즈니스 로직과 엔티티, Use Case 등
- Ports (Inbound / Outbound): 도메인과 외부 간 인터페이스 규격 정의
- Inbound Port: 사용자나 시스템에서 들어오는 입력 (예: Use Case 인터페이스)
- Outbound Port: 도메인이 외부 시스템에 요청할 때 사용 (예: Repository 포트)
- Adapters: Ports 를 구현해 외부 시스템과 연결 (예: REST 컨트롤러, DB 리포지토리, 메시지 큐)
- Dependencies inward: 핵심 도메인은 외부 adapter 나 프레임워크에 의존하지 않음
- Loose coupling & high cohesion: Ports/Adapters 분리로 구조 단순화 유지
- Testable core: 외부 시스템과 무관하게 단위 테스트 가능 (tsh.io, Medium, GeeksforGeeks, 위키백과, Medium)
5.1 실무 구현 연관성
- 도메인 중심 설계: DDD 에 적합하며 모델 중심 개발 촉진
- 모듈화: Ports 와 Adapters 별 테스트 및 교체 가능
- CI 환경: adapter 구현을 mock/stub 하여 core 테스트 용이
- 기술 독립성: 외부 기술 변경 시 adapter 만 교체, domain 유지
6️⃣ 추가 조사된 주요 항목 정리
- 등장 및 발전 배경: 1990 년대 중반 Cockburn 이 UI·데이터 종속성 제거 목적으로 제안. 2005 년 “Ports & Adapters” 명칭으로 정형화.(Scalastic, bitloops.com, GeeksforGeeks, 위키백과)
- 목적 및 필요성: 핵심 비즈니스 로직을 외부 종속성으로부터 보호하고, 테스트·확장·기술 교체에 자유로운 아키텍처를 만들기 위함
- 작동 원리 및 방식: 핵심 로직은 Ports 인터페이스 정의, 외부 시스템은 Adapters 로 구현. Ports 를 통해 inbound/outbound 방향으로 통신.(AWS Documentation)
7️⃣ 실무 사용 예시 / 비교
비교: Hexagonal Vs Clean Vs Layered
항목 | Hexagonal Architecture | Clean Architecture | 전통적 Layered Architecture |
---|---|---|---|
구조 방식 | 중심 도메인 + Ports/Adapters | concentric rings, adapter 외부에 위치 | 계층별 Presentation→Business→Data |
의존성 흐름 | Adapters → Ports → Domain | 외부 → 내부 중심 도메인으로 방향성 강함 | 상위 계층이 하위 계층에 의존 |
기술 독립성 | 높음: adapter 교체 가능 | 매우 높음 | 상대적으로 낮음 |
테스트 용이성 | Core 단위 테스트 가능 | Core + Use Cases 테스트 유리 | UI / DB mocking 필요 복잡 |
8️⃣ 실무 활용 사례 (요약)
- Shopify: 결제 게이트웨이, 배송 서비스 등 다양한 외부 서비스와 Ports/Adapters 기반 연결 (softengbook.org, GeeksforGeeks)
- Netflix, PayPal 등: 마이크로서비스마다 core 로직 보호 및 외부 API adapter 통해 통신 (GeeksforGeeks, GeeksforGeeks)
(자세한 사례 중심 활용 사례는 요청 시 머지 가능)
9️⃣ 도전 과제 / 고려사항
단점 (표)
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | Adapter 수 증가 및 복잡도 증가 | Ports 수 & adapter 구현 증가 | Port 과다 설계 방지, 단순화 전략 |
Latency 및 Overhead | adapter 추상화로 인한 호출 지연 | profiling 기반 병목 파악 및 최적화 (AWS Documentation) | |
작은 프로젝트에는 과도 설계 | 단순 프로젝트에 불필요한 구조 | 핵심 기능 중심 최소한 구조 적용 |
문제점 (표)
구분 | 항목 | 원인 | 영향 | 탐지·진단 | 예방 방법 | 해결 방법 |
---|---|---|---|---|---|---|
문제점 | Ports 정의 과도 | 포트 granularity 과다 설계 | 관리 난이도 증가 | 코드 리뷰, 설계 점검 | 최소 단위 port 적극 고민 | refactor 또는 통합 |
문제점 | Adapter 구현 중복 | 동일 포트에 다수 adapter 존재 | 유지보수 복잡화 | adapter 모듈 분석 | adapter 템플릿 활용 | 공통 로직 추출, 테스트 생성 |
🔍 계속 진행 가능 항목
- 특정 언어 기반 예시 (Python / JavaScript / Go)
- 실무 활용 사례 세부화: 시스템 구성, mermaid 다이어그램, Workflow, 코드 예시 포함
- 도메인·Ports·Adapters 구조 설계 구체화
필요하신 방향을 알려주시면 해당 항목 추가로 정리해드립니다.
좋습니다! 이제 “Hexagonal Architecture (Ports & Adapters)” 에 대한 심화 활용 사례, 워크플로우, 코드 예시, 그리고 도메인‑포트‑어댑터 구조 설계를 포함한 내용을 이어서 정리해드리겠습니다. 이번에는 Python 기반 사례 중심으로 구성했습니다.
✅ 활용 사례: Python 기반 사용자 관리 시스템
시나리오:
REST API 를 통해 사용자 CRUD 기능을 제공하며, 도메인 로직은 데이터베이스나 웹 프레임워크에 독립적으로 설계.
시스템 구성:
- Domain (코어): User 엔티티, UserUseCase 인터페이스, 도메인 서비스
- Ports:
- Inbound Port:
UserServicePort
(예:create_user
,get_user
) - Outbound Port:
UserRepositoryPort
(예: 저장/조회 인터페이스)
- Inbound Port:
- Adapters:
- Inbound Adapter: Flask/FastAPI 컨트롤러
- Outbound Adapter: SQLAlchemy 리포지토리 구현
- Configurator: 앱 초기화 시 port 와 adapter 를 연결하고 인스턴싱
시스템 구성 다이어그램:
graph LR Controller[Inbound Adapter\n(e.g. Flask)] -->|calls| ServicePort(Inbound Port) ServicePort --> Domain[Domain Logic / Use Cases] Domain --> RepoPort(Outbound Port) RepoPort --> DBAdapter[Outbound Adapter\n(SQLAlchemy)]
🛠 Workflow 및 역할
- 유저 요청 (HTTP POST /users) → Controller (Inbound Adapter)
- Controller 는 DTO → domain 파라미터로 변환 후
UserServicePort
호출 UserUseCase
(domain core) 내부에서 비즈니스 로직 실행 (중복 여부 확인 등)- 필요한 경우
UserRepositoryPort
통해 저장 또는 조회 - 도메인이 반환한 정보를 Controller 는 응답 DTO 로 변환 후 클라이언트 응답
- Controller (Inbound Adapter)
- HTTP 요청을 domain 호출용 구조로 변환
- Ports
- Core 로직과 외부 시스템 사이의 계약 (interface) 정의
- Use Case / Domain
- 핵심 비즈니스 로직 실행 (framework‑free)
- Adapters
- Port 계약을 기술에 의존하는 방식으로 구현하며 core 보호
- Configurator
- 도메인 Port 인터페이스와 Adapter 구현 결합 (DI 역할)
🧱 구현 예시 (Python 스타일, Flask + SQLAlchemy)
|
|
🔍 주목할 내용 요약 (표)
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
설계 원칙 | Driver vs Driven Port 구분 | Inbound/Outbound Ports | 명확한 역할 구분 |
테스트 전략 | Core 테스트 독립성 유지 | Port mocking | 도메인 테스트 시 외부 의존 배제 |
적용 범위 | 모듈 단위 Hexagon 설계 가능 | Modular Hexagon | 기능별 모듈 단위 독립 아키텍처 |
스케일 확장 | 마이크로서비스 전환 용이성 | Adapter 교체 중심 분리 | 기술 독립성 유지하며 분리 가능 |
📚 반드시 학습해야 할 내용 (보완)
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
구조 설계 | Modular Hexagon Pattern | 기능별 독립 도메인 모듈 | 확장성 및 독립성 확보 전략 |
아키텍처 검증 | Architecture Fitness Tests | CI 환경에서 의존성 규칙 검사 | Ports‑Adapters 경계 유지 |
Port 설계 | Port Granularity | 포트 과다 또는 과소 설계 | 단순성과 명확성 고려 설계 |
🧩 기타 사항
- 모듈 단위 Hexagon 구조는 대규모 코드베이스에서 모듈화 및 유지보수 용이 → 모듈 단위로 entry‑point, domain, adapters 구성 가능합니다. (gist.github.com, miladezzat.medium.com, 8thlight.com, dev.to, workflows.guru, dev.to, dev.to, docs.aws.amazon.com, douwevandermeij.medium.com)
- AWS Lambda + Python 기반 서버리스에서도 hexagonal 구조 활용 사례가 있으며, domain/core 는 Lambda 핸들러와 분리하여 테스트, 재사용성을 유지할 수 있습니다. (docs.aws.amazon.com)
필요하시면 이 구조에 기반한 테스트 코드, CI 구성 예시, adapter 교체 전략, 또는 언어나 플랫폼을 넘은 설계 예시도 제공해드릴 수 있습니다. 추가 요청 방향을 알려주세요!
다음은 **Hexagonal Architecture (Ports & Adapters)** 의 도전 과제 보완, 테스트 및 아키텍처 검증 전략, 최적화 고려사항, 그리고 CI 기반 설계 검증에 대한 심화 내용입니다.
🧪 아키텍처 검증 및 테스트 전략
아키텍처 피트니스 테스트 (Architecture Fitness Test)
CI 에서 구조적 규칙 (예: 포트와 어댑터 분리, 순환 의존 금지) 을 자동으로 검사하는 방법으로, Java 의 ArchUnit 처럼 테스트 프레임워크와 연계해 아키텍처 위반을 조기에 감지합니다. (Medium)테스트 구조
- 단위 테스트 (Unit Test): 도메인 코어 로직을 포트 의존 없이 테스트 (테스트 더블 사용) (AWS Documentation, n14n.dev)
- 통합 어댑터 테스트 (Integration Test): 어댑터가 외부 시스템과 의도대로 상호작용하는지 확인 (AWS Documentation)
- 컨트랙트 테스트 (Contract Test) 및 E2E 테스트: 어댑터 간 인터페이스 계약을 기반으로 외부 시스템과의 호환성 검증 (qwan.eu, AWS Documentation)
테스트 더블과 TDD
테스트 더블 (fakes, mocks, stubs) 을 이용해 도메인 테스트를 외부 시스템 없이 빠르게 실행. TDD 접근법으로 도메인 스펙을 먼저 정의한 후 adapter 구현. (qwan.eu)성능 및 구조 검증 자동화
Fitness 함수 기반 테스트로 CI 에 통합하여 아키텍처 일관성과 테스트 통과 여부를 지속 검증. (Apiumhub)
⚙️ 성능 최적화 고려사항
항목 | 위험 요소 | 대책 |
---|---|---|
추상화 호출 | 포트 - 어댑터 오버헤드로 CPU/메모리 지연 가능 | profiling 도구 활용, critical path inline 최적화 |
포트 granularity | 포트 인터페이스가 너무 세분화되면 구현복잡도 상승 | 포트 설계 시 단순함과 재사용성 균형 조정 |
adapter 수 증가 | 다수 adapter 관리 부담 | 공통 코드 추출, 템플릿 또는 베이스 클래스 활용 |
- 단순 시스템에는 과도한 추상화보다 실용적 단순 구조 적용 고려. (AWS Documentation)
- 중요한 요청 경로에서 latency 문제가 발생할 경우 adapter 호출 경로 최적화 필수. (AWS Documentation, pratikpandey.substack.com)
🎯 CI 기반 Hexagonal Architecture 검증 예시 (Python Workflow)
|
|
tests/test_architecture_fitness.py
: Architecture Fitness Test 구현 (예: 포트 디렉토리에만 의존하는지, adapter 가 domain 에 의존하는지) (Medium)- unit 테스트는 도메인 로직만, integration 테스트는 어댑터와 외부 시스템 연계 확인
🔍 도전 과제 보완: 사용자 경험 고려
Adapter 관리 복잡성: adapter 가 많아질 경우 어댑터 생성/등록, DI wiring 복잡도 증가
→ 공통 adapter base class 및 자동 등록 메커니즘 채택추상 레이어로 인한 코드 읽기 난이도 증가: 호출 흐름이 여러 계층으로 분산
→ 명확한 문서화, 이름 명명 규칙, 예제 코드 제공
(Reddit, DEV Community)과도한 포트 정의로 테스트 및 유지보수 어려움
→ 최소 단위 port 설계 원칙 기반 리팩토링 필요 (DEV Community, alexhyett.com)
핵심 요약
- Hexagonal Architecture 는 도메인 중심, 느슨한 결합, 기술 독립성을 제공하는 구조로, testability 와 확장성을 지원합니다. (AWS Documentation)
- CI 기반 Architecture Fitness Test 및 Unit / Integration / Contract 테스트 전략으로 구조 일관성 유지 가능.
- 작은 프로젝트에는 과도 설계 주의, 핵심 도메인 중심 최소 구조로 도입 권장.
- 성능 병목 발생 시 profiling 기반 병목 분석 후 adapter 호출 경로 최적화 권고.
필요하시면 위 내용을 기반으로 fitness test 코드 예시, CI pipeline 전체 구성, 다른 언어 프로젝트 구조, 도메인별 adapter 분리 전략 등 더 심화된 자료도 제공 가능합니다. 요청 주시면 이어서 정리해드리겠습니다!
알겠습니다! 다음은 **Hexagonal Architecture (Ports & Adapters)** 의 구성요소별 심화 구조 설계와 architecture fitness test 코드 예시, 단위 테스트 & 통합 테스트 사례, 다른 언어 플랫폼 구성 구조 요약입니다.
1️⃣ 모듈 단위 Hexagon 설계: 구조 설계 다이어그램
각 기능 (예: 사용자 (user), 주문 (order), 상품 (product)) 마다 독립된 hexagon 모듈로 설계할 수 있습니다.
graph TD subgraph UserModule UI1[UserController] UPo(UserServicePort) UseCasesUC(UserService) UPo --> UseCasesUC UseCasesUC --> URepPort(UserRepositoryPort) URepPort --> UDBAdapter(SQLAdapter) end subgraph OrderModule UI2[OrderController] OrPo(OrderServicePort) OrUC(OrderService) OrUC --> ORepPort(OrderRepositoryPort) ORepPort --> ODBAdapter(SQLAdapter) end subgraph CommonAdapters SQLAdapter((Shared DB Adapter)) EmailAdapter((Email Sender Adapter)) end
설명:
- 각각의 모듈이 자체 Ports/Domain/Adapters 구조를 갖고, 인프라 (adapter) 는 재활용 가능
- 확장 시 모듈 추가만으로 기능 면에서 독립 가능
2️⃣ Architecture Fitness Test 예시 (Python, pytest)
|
|
- 첫 번째 테스트: adapter 코드에서는 도메인 (
domain
) 을 직접 임포트하면 안 됨 - 두 번째 테스트: ports 는 adapters 와 domain 만 참조 가능, adapter 가 ports 외 다른 참조는 제한
3️⃣ 테스트 구조 및 예시
✅ Unit Test (domain 핵심 로직만 검증)
|
|
🧪 Integration Test (Adapter 와 외부 시스템 연동)
|
|
4️⃣ 다른 언어 및 플랫폼 구성 요약
언어/플랫폼 | 구조 방식 | 특징 |
---|---|---|
Node.js + Express | domain/core, ports, controllers, repositories | adapter 는 Express 라우터, ORM 등으로 구현 |
Go (Golang) | pkg/domain , pkg/ports , pkg/adapters 구조 | 정적 타입, 인터페이스 설계에 적합, DI 없이 간단 구조 |
Kotlin + Spring Boot | port 인터페이스, adapter 구현, configuration 클래스 | Spring DI 및 bean 방식으로 adapter 연결 구조 유지 |
Python + AWS Lambda | handler 와 domain 분리, adapter 는 이식 가능한 모듈 형식 | serverless 환경에서 변경 없이 domain 재사용 가능 |
🧭 요약: 지금까지 정리한 아키텍처 검증 요건
- 모듈 단위 Hexagon 설계로 기능별 독립성과 재사용성 확보
- Architecture Fitness Test로 adapter 와 domain 간 의존성 규칙 자동 검사
- 단위 테스트로 domain 코어 로직 검증, 통합 테스트로 adapter 와 외부 연동 검증
- 다양한 언어 플랫폼에 적용 가능한 일관된 Ports/Adapters 구조 사용
- CI 기반 pipeline 내 테스트 및 구조 검증 절차 통합
더 구체적인 Go 구현 예시, Node.js 프로젝트 구조, Kotlin Spring Boot DI 설정, AWS Lambda 서버리스 Hexagon 설계 등이 필요하시면 말씀해 주세요! 강조하고 싶은 특정 부분 요청하시면 해당 방향으로 정리해드리겠습니다.
다음은 요청하신 Hexagonal Architecture 에 대한 미작성된 항목 중 다음 세 가지를 보완하여 작성한 표입니다.
✅ 분류 기준에 따른 종류 및 유형
분류 기준 | 유형 | 설명 |
---|---|---|
포트 방향 | Inbound Port / Outbound Port | 애플리케이션 진입 및 외부 리소스 호출을 위한 구분 |
아키텍처 구성 방식 | 모듈형 Hexagon / 모놀리식 Hexagon | 각 기능을 모듈로 분리할지 여부에 따라 구조 결정 |
기술 스택 대응 | HTTP / gRPC / Event Adapter | Inbound/Outbound 어댑터의 기술적 채택 방식에 따라 구분 |
✅ 실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
포트 과잉 정의 | 각 use case 마다 포트를 분리하면 유지보수 어려움 | 기능 단위 묶음 기준으로 port 설계 |
모듈화 전략 | 단일 Hexagon 구조는 기능 확장에 어려움 | 기능 기반 모듈로 Hexagon 분리 |
의존성 주입 | DI 설정이 복잡해질 수 있음 | 프레임워크 기능 또는 수동 주입으로 간단화 |
테스트 구성 | 테스트 범위 혼동 가능 | Domain 은 Unit Test, Adapter 는 Integration Test 분리 |
✅ 최적화하기 위한 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
Adapter 호출 최적화 | 지나친 추상화는 성능 저하 원인 | 핵심 경로는 인라인 최적화 및 직접 호출 병행 |
I/O 집중 영역 | DB, API 등 Outbound Adapter 병목 발생 가능 | 비동기 처리, 캐싱, 리트라이 패턴 적용 |
테스트 병목 | 전체 테스트 수행 시간이 길어짐 | 도메인 테스트는 병렬, 어댑터 테스트는 주기적 수행 |
코드 난이도 증가 | 추상 레이어가 많아 가독성 저하 | 문서화, 코드 템플릿, 표준 명명 규칙 운영 |
좋습니다. 이어서 다음 항목들을 작성하겠습니다.
📌 기타 사항
- Hexagonal Architecture 는 ’ 의존성 흐름 ’ 을 설계의 핵심 개념으로 채택
→ 모든 어댑터는 도메인을 " 사용 " 할 수 있지만 도메인은 어떤 구현에도 의존하지 않아야 함 - 테스트 중심 설계 (TDD) 와 궁합이 뛰어남
→ Domain 과 Use Case 는 순수 로직으로서 mocking 만으로 테스트 가능 - Framework 탈피 가능성 확보
→ 프레임워크 변경 시 adapter 만 교체하면 전체 시스템 변경 없이 유지 가능 - 마이크로서비스 이전 구조로 이상적
→ 모듈별 hexagon 은 서비스 단위로 분리 가능하며, 이후 독립 배포 및 운영이 쉬움 - 도메인 주도 설계 (DDD) 와 자연스럽게 결합
→ Entity, Value Object, Service 등의 도메인 개념을 core 에 포함시킬 수 있음
📚 용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
아키텍처 | Hexagonal Architecture | Ports 와 Adapters 를 통해 핵심 로직과 외부 요소를 분리하는 아키텍처 스타일 |
아키텍처 | Port | 도메인 계층이 외부와 소통하기 위해 정의하는 추상화된 인터페이스 |
아키텍처 | Adapter | 포트를 구현하여 외부 기술 요소 (UI, DB 등) 와 연결하는 계층 |
아키텍처 | Inbound Port | 외부 요청이 도메인 로직으로 진입하기 위한 인터페이스 |
아키텍처 | Outbound Port | 도메인이 외부 시스템에 의존을 표현할 때 사용하는 인터페이스 |
설계 원칙 | Dependency Rule | 외부는 내부를 알 수 있지만, 내부는 외부를 알 수 없음 (의존 방향성 규칙) |
개발 기법 | TDD (Test-Driven Development) | 테스트를 먼저 작성하고 그에 맞는 기능을 구현하는 개발 방식 |
테스트 | Architecture Fitness Test | 아키텍처 구조적 규칙을 코드 수준에서 검증하는 테스트 기법 |
🔗 참고 및 출처
- Hexagonal Architecture 공식 소개 - Wikipedia
- AWS Prescriptive Guide - Hexagonal Architecture
- ThoughtWorks Technology Radar - Ports and Adapters
- Alistair Cockburn 원 저자 설명
- Clean vs Hexagonal vs Onion 비교
- Apiumhub - Clean Architecture, Hexagonal, Onion 비교
- Architecture Fitness Testing Example
✅ 마무리 요약
Hexagonal Architecture 는 소프트웨어의 핵심 로직을 외부 시스템으로부터 분리함으로써 유지보수성과 테스트 용이성을 극대화합니다. 기술 독립성과 도메인 중심 설계를 가능하게 하며, 마이크로서비스 또는 서버리스 이전에 기반이 되는 유연한 구조입니다. Ports(인터페이스) 와 Adapters(구현체) 를 통해 의존성을 명확하게 분리하고, 아키텍처 규칙을 검증할 수 있는 테스트와 자동화 전략까지 지원할 수 있는 강력한 패턴입니다.
이제 전체 항목이 모두 작성 완료되었습니다. 추가로 특정 플랫폼의 예제, 언어별 프로젝트 구조 예시, 또는 비교 기반 문서가 필요하시다면 요청해 주세요.
1. 태그
- Hexagonal-Architecture
- Ports-and-Adapters
- Domain-Driven-Design
- Software-Design-Patterns
2. 분류 구조 분석
계층 구조:
Computer Science and Engineering > Software Engineering > Design and Architecture > Architecture Styles and Patterns > Architecture Patterns
분석 및 근거:
Hexagonal Architecture 는 소프트웨어 엔지니어링의 설계 및 아키텍처 분야에서 널리 사용되는 아키텍처 패턴 (Architecture Pattern) 으로, 의존성 역전과 인터페이스 기반 설계를 통해 결합도를 낮추고 유지보수성과 확장성을 높이는 것이 목적입니다. 이는 “Architecture Styles and Patterns” 하위의 “Architecture Patterns” 에 적합하게 분류됩니다. 실제로 Hexagonal Architecture 는 레이어드 (Layered) 구조와 도메인 중심 설계 (Domain-Driven Design) 의 영향을 받아 설계되었으며, 소프트웨어 엔지니어링의 핵심 설계 원칙에 부합합니다 13.
3. 요약 (200 자 내외)
Hexagonal Architecture 는 비즈니스 로직을 중심에 두고, 포트와 어댑터를 통해 외부 기술 의존성을 분리해 결합도를 낮추고 유지보수성과 테스트 용이성을 높이는 아키텍처 패턴이다 14.
4. 개요 (250 자 내외)
Hexagonal Architecture(포트와 어댑터 아키텍처) 는 애플리케이션의 핵심 비즈니스 로직을 외부 프레임워크, 데이터베이스, UI 등과 분리해, 변경에 유연하고 테스트가 용이하며, 장기적으로 유지보수와 확장이 쉬운 구조를 제공하는 설계 방법론이다 14.
5. 핵심 개념
- 비즈니스 로직 중심 설계: 도메인 (비즈니스 엔티티와 규칙) 을 시스템의 중심에 배치하여, 외부 기술 변화에 영향을 받지 않도록 설계 56.
- 포트 (Port): 외부와의 상호작용을 위한 인터페이스. 인바운드 (외부→내부), 아웃바운드 (내부→외부) 로 구분 57.
- 어댑터 (Adapter): 외부 시스템과의 통합을 담당하는 구현체. 인바운드 (입력), 아웃바운드 (출력) 어댑터로 구분 57.
- 의존성 역전 (Dependency Inversion): 내부 (코어) 가 외부 (인프라) 에 의존하지 않고, 인터페이스를 통해 느슨하게 결합 6.
- 테스트 용이성: 도메인과 유스케이스는 외부 의존 없이 독립적으로 테스트 가능 18.
실무 구현 요소
- 도메인 (Domain): 비즈니스 엔티티와 규칙
- 포트 (Port): 외부와의 상호작용 인터페이스
- 어댑터 (Adapter): 외부 시스템과의 통합 구현체
- 서비스 (Service): 비즈니스 로직 구현 (유스케이스)
6. 조사 내용 (주요 항목별 정리)
배경
Hexagonal Architecture 는 계층형 아키텍처의 단점 (계층 간 의존성, 비즈니스 로직과 UI/DB 코드 혼재) 을 보완하기 위해 Alistair Cockburn 이 2005 년에 제안한 아키텍처 패턴입니다 13.
목적 및 필요성
- 비즈니스 로직의 독립성: 외부 기술 변화에 영향을 받지 않도록 도메인을 격리 16.
- 유지보수성과 확장성: 변경이 용이하고, 장기적으로 관리가 쉬움 17.
- 테스트 용이성: 도메인과 유스케이스가 독립적으로 테스트 가능 18.
주요 기능 및 역할
- 도메인: 비즈니스 엔티티와 규칙 정의
- 포트: 외부와의 상호작용 인터페이스
- 어댑터: 외부 시스템과의 통합 구현체
- 서비스: 비즈니스 로직 구현
특징
핵심 원칙
- DIP(의존성 역전 원칙): 상위 계층이 하위 계층에 직접 의존하지 않고 인터페이스를 통해 의존성 역전 6.
- ISP(인터페이스 분리 원칙): 클라이언트가 필요 없는 인터페이스에 의존하지 않음 6.
- SRP(단일 책임 원칙): 각 계층과 모듈이 단일 책임을 가짐 6.
주요 원리 및 작동 원리
|
|
의존성은 항상 안쪽 (코어) 으로 향함. 외부 (어댑터) 가 내부 (코어) 를 호출하거나 사용할 수 있지만, 내부 (코어) 는 외부 (어댑터) 를 알지 못함 56.
구조 및 아키텍처
- Core(코어): 비즈니스 엔티티와 규칙 (필수)
- Port(포트): 외부와의 상호작용 인터페이스 (필수)
- Adapter(어댑터): 외부 시스템과의 통합 구현체 (필수)
- Service(서비스): 비즈니스 로직 구현 (필수, 코어 내 포함)
각 계층의 역할
- Core: 도메인 모델, 비즈니스 규칙
- Port: 외부와의 상호작용 인터페이스
- Adapter: 외부 시스템과의 통합 구현체
- Service: 비즈니스 로직 구현 (유스케이스)
구현 기법
- 의존성 역전 (Dependency Inversion): 인터페이스를 통해 내부가 외부를 모르도록 설계 6.
- 포트와 어댑터 분리: 인바운드/아웃바운드 포트와 어댑터로 명확히 분리 57.
- 테스트 코드 작성: 도메인과 유스케이스는 외부 의존 없이 테스트 가능 18.
- 모듈화: 각 계층별로 모듈화하여 유지보수성 향상 10.
장점
구분 | 항목 | 설명 | 특성 원인 |
---|---|---|---|
장점 | 유지보수성 | 변경이 용이하고, 장기적으로 관리가 쉬움 | 도메인과 기술 분리, 포트 - 어댑터 구조 |
장점 | 확장성 | 새로운 기능 추가 및 기술 교체가 쉬움 | 인터페이스 활용, 의존성 역전 |
장점 | 테스트 용이성 | 도메인과 유스케이스가 독립적으로 테스트 가능 | 외부 의존성 분리 |
장점 | 프레임워크 독립성 | 프레임워크 교체가 쉬움 | 도메인과 기술 분리 |
단점과 문제점 그리고 해결방안
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | 초기 설계 복잡성 | 계층 구조 설계와 인터페이스 정의가 필요 | 경험 축적, 도메인 중심 설계 강조 |
단점 | 학습 곡선 | 개발자 교육 및 이해 필요 | 교육, 문서화, 예시 코드 제공 |
구분 | 항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|---|
문제점 | 계층 간 혼합 | 설계 미흡, 인터페이스 미정의 | 테스트 및 유지보수 어려움 | 코드 리뷰, 정적 분석 | 인터페이스 명확화, 계층 분리 | 리팩토링, 계층 재설계 |
문제점 | 포트 경계 모호 | 도메인 정의 미흡, 포트 설계 부족 | 비즈니스 로직과 인프라 혼재 | 코드 리뷰, 아키텍처 리뷰 | 도메인 모델링 강화, 포트 설계 명확화 | 도메인 중심 설계, 포트 리팩토링 |
도전 과제
구분 | 항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|---|
도전 과제 | 대규모 시스템 적용 | 계층 간 통신 및 관리 복잡 | 성능 저하, 유지보수 어려움 | 모니터링, 프로파일링 | 모듈화, 마이크로서비스 전환 | 마이크로서비스 아키텍처 적용, 도메인 분리 |
도전 과제 | 팀 온보딩 및 코드 관리 | 아키텍처 복잡성, 팀원 이해 부족 | 개발 비용 증가, 일관성 저하 | 코드 리뷰, 문서화 | 교육, 예시 코드 제공, 멘토링 | 문서화, 코드 리뷰, 멘토링 |
분류 기준에 따른 종류 및 유형
분류 기준 | 종류/유형 | 설명 |
---|---|---|
계층 구조 | 포트 - 어댑터 구조 | 외부와의 연결을 포트와 어댑터로 분리 |
설계 패턴 | Hexagonal Architecture | 의존성 역전, 인터페이스 활용, 도메인 중심 설계 |
적용 범위 | 모노리틱, 마이크로서비스 | 둘 다 적용 가능, 마이크로서비스에 적합 |
실무 사용 예시
사용 목적 | 함께 사용하는 기술 | 효과 |
---|---|---|
유지보수성 향상 | Spring, Django, Node.js | 도메인과 기술 분리로 변경 용이 |
테스트 용이성 | JUnit, pytest | 도메인과 유스케이스 독립 테스트 |
프레임워크 교체 | 다양한 ORM, DB | 인터페이스 활용으로 교체 용이 |
활용 사례
카카오페이 홈 서버:
Hexagonal Architecture 를 적용해 도메인과 기술을 분리하여, 다양한 연동 API 에 유연하게 대응. 시스템 구성은 도메인 (비즈니스 규칙), 포트 (외부와의 인터페이스), 어댑터 (외부 시스템 통합) 로 계층화됨.
Workflow:
외부 API → 인바운드 어댑터 → 인바운드 포트 → 코어 (도메인) → 아웃바운드 포트 → 아웃바운드 어댑터 → 외부 시스템
역할:
도메인: 비즈니스 규칙, 포트: 외부와의 인터페이스, 어댑터: 외부 시스템 통합 11.
구현 예시 (Python)
|
|
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
도메인 중심 설계 | 도메인을 명확히 정의하고, 비즈니스 규칙을 도메인에 포함 | 도메인 모델링 강화 |
인터페이스 활용 | 계층 간 인터페이스로 결합도 낮춤 | 인터페이스 명확화 |
테스트 코드 작성 | 도메인과 유스케이스는 독립적으로 테스트 | TDD, 단위 테스트 강화 |
계층 분리 | 계층 간 혼합 방지 | 코드 리뷰, 정적 분석 |
최적화하기 위한 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
모듈화 | 각 계층별로 모듈화하여 관리 | 마이크로서비스 적용 고려 |
성능 최적화 | 계층 간 통신 최소화, 캐싱 활용 | 프로파일링, 캐싱 전략 적용 |
유지보수성 | 문서화, 코드 리뷰 | 문서화, 코드 리뷰 강화 |
기타 사항
- 마이크로서비스와의 연계: Hexagonal Architecture 는 모노리틱과 마이크로서비스 모두에 적용 가능하며, 마이크로서비스 환경에서 도메인 분리와 독립적 배포에 유리 12.
- 도메인 주도 설계 (DDD) 와의 연계: Hexagonal Architecture 는 DDD 와 상호보완적이며, 도메인 모델링과 계층 분리에 강점이 있음 9.
- 실무 적용 시: 초기 설계 복잡성과 학습 곡선이 있으나, 장기적으로 유지보수성과 확장성이 뛰어남 [11][13].
7. 추가 조사 내용
- Hexagonal Architecture 와 Layered Architecture, Clean Architecture 비교:
- Layered Architecture: 계층 간 직접 의존, 비즈니스 로직과 인프라 혼재 [9]14.
- Clean Architecture: 의존성 규칙, 인터페이스 활용, 도메인 중심 설계. Hexagonal 과 유사하지만 용어와 계층 구조가 다름 [9][15].
- 실무 적용 시 마이크로서비스와의 연계:
- Hexagonal Architecture 는 마이크로서비스 환경에서 서비스 간 통신과 독립적 배포에 적합 12.
- 테스트 전략:
8. 주목할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
설계 패턴 | Hexagonal Architecture | 도메인 중심 | 비즈니스 로직을 중심에 두고 외부 기술과 분리 |
설계 패턴 | Hexagonal Architecture | 포트 - 어댑터 구조 | 외부와의 연결을 인터페이스와 구현체로 분리 |
실무 적용 | Hexagonal Architecture | 마이크로서비스 | 도메인 분리, 독립적 배포에 유리 |
실무 적용 | Hexagonal Architecture | 테스트 전략 | 도메인과 유스케이스 독립 테스트 |
9. 반드시 학습해야 할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
설계 원칙 | Hexagonal Architecture | 의존성 역전 | 내부가 외부에 의존하지 않음 |
설계 원칙 | Hexagonal Architecture | 인터페이스 활용 | 계층 간 결합도 낮춤 |
실무 적용 | Hexagonal Architecture | 도메인 모델링 | 비즈니스 규칙 명확화 |
실무 적용 | Hexagonal Architecture | 테스트 코드 작성 | 도메인과 유스케이스 독립 테스트 |
10. 용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
설계 패턴 | Hexagonal Architecture | 도메인 중심, 포트 - 어댑터 구조, 의존성 역전을 갖춘 소프트웨어 설계 패턴 |
설계 원칙 | 의존성 역전 (Dependency Inversion) | 고수준 모듈이 저수준 모듈에 의존하지 않도록 인터페이스 활용 |
설계 원칙 | 인터페이스 분리 (Interface Segregation) | 인터페이스를 최소 단위로 분리하여 결합도 낮춤 |
실무 적용 | 도메인 (Domain) | 비즈니스 엔티티와 규칙을 포함하는 계층 |
실무 적용 | 포트 (Port) | 외부와의 상호작용을 위한 인터페이스 |
실무 적용 | 어댑터 (Adapter) | 외부 시스템과의 통합을 담당하는 구현체 |
11. 참고 및 출처
- 헥사고날 아키텍처란 - 코딩못하는사람
- 헥사고날 아키텍처(Hexagonal Architecture) 란? - bettershinny
- Hexagonal Architecture이란? 기본 개발 방법 - unit-15
- 육각형 아키텍쳐(Hexagonal Architecture) - velog
- Hexagonal Architecture 정리 - Space pioneer
- 헥사고날 아키텍처(Hexagonal Architecture) - 어제보다 더 나은 개발자
- Hexagonal Architecture, 진짜 하실 건가요? - 카카오페이 기술 블로그
- Hexagonal Architecture 소개 - 혼새미로의 블로그
아래는 Hexagonal Architecture 주제에 대한 1 차 정리입니다.
1. 태그
|
|
2. 분류 계층 적절성 분석
현재 분류:
Computer Science and Engineering → Software Engineering → Design and Architecture → Architecture Styles and Patterns → Architecture Patterns
분석: 적절합니다.
- Hexagonal Architecture 는 계층 패턴의 일종으로 외부와 내부를 추상화 계층 (Port/Adapter) 로 분리하므로, ‘Architecture Patterns’ 범주에 가장 잘 부합합니다. (en.wikipedia.org)
3. 200 자 요약
Hexagonal Architecture(Ports & Adapters) 는 비즈니스 로직을 핵심 (Core) 에 집중시키고, 외부 입출력 모듈 (UI, DB, 메시징 등) 을 포트와 어댑터로 연결해 의존성을 내부로만 흐르게 하는 아키텍처입니다. 테스트 용이성과 유연성을 확보하고, 외부 기술 변화에도 핵심 코드 비침투성을 보장합니다.
4. 250 자 개요
Hexagonal Architecture 는 Alistair Cockburn(2005) 가 제안한 Ports & Adapters 패턴으로, 핵심 비즈니스 로직 (Core) 을 중심에 두고 외부 시스템 (UI, DB, API, 메시지 등) 을 포트 (인터페이스) 와 어댑터 (구현) 로 분리하는 구조입니다. 내부 코드는 외부 기술에 전혀 의존하지 않으며, 외부 변화에도 유연하게 대응 가능한 설계가 가능합니다. 또한, 단위 테스트가 쉽고 마이크로서비스 설계 시 모듈화가 용이합니다. 테스트 및 기술 변경 비용을 크게 줄이는 동시에, 비즈니스 중심 개발을 촉진합니다. (en.wikipedia.org)
5. 핵심 개념 및 실무 구현 요소
핵심 개념
- Core/Domain: 비즈니스 로직, 도메인 모델이 위치하며 외부 종속 없음
- Port: 핵심이 외부와 소통하기 위한 추상 인터페이스 (Primary/Secondary) (dev.to)
- Adapter: Port 를 구현하는 외부 계층의 컴포넌트 (REST API, DB 구현, 메시징 등) (dev.to)
- Dependency Inversion Principle (DIP): 내부는 외부 모듈 정보를 몰라야 하며, 흐름은 외부 → 내부 방향으로만 (reddit.com)
5.1 실무 구현 요소
- 인터페이스 정의: 사용자가 핵심에 접근하고, 인프라가 핵심에 동작할 수 있도록 Port 정의
- Adapter 구현: HTTP 컨트롤러, CLI, 메시지 소비자 등 Primary Adapter, DB Access, 외부 API 호출 등 Secondary Adapter 구현
- 의존성 주입 (DI): 런타임에 Adapter 구현체를 Core 에 주입, DIP 보장
- 유닛 테스트: Core(Use Case, 도메인) 를 Adapter 없이 Port 만으로 테스트
- 구성 레이어 분리: 모듈/패키지 구조 (core, ports, adapters, infrastructure 등)
다음 단계로:
- " 구조 및 아키텍처 + 구성 요소 "
- " 주요 원리 & 작동 원리 " 다이어그램
등 원하시는 방향을 선택해주시면 이어서 작성합니다.
5. 구조 및 아키텍처 (Structure & Architecture)
Hexagonal Architecture 는 Ports & Adapters라 불리며, 내부 비즈니스 로직 (Core/Domain) 을 ** 포트 (Ports)** 라는 인터페이스로 외부와 연결하고, 실제 구현은 어댑터 (Adapters) 형태로 외부에 둡니다 (en.wikipedia.org).
계층 구조 및 구성 요소
|
|
- Domain/Core: 비즈니스 규칙을 구현한 순수 코드—외부 종속 없음
- Inbound Ports (Primary): UI, API, 메시지 및 테스트 호출을 위한 인터페이스 (marcolenzo.eu)
- Outbound Ports (Secondary): DB, 메시징, API 호출 등 외부 연동을 위한 인터페이스 (marcolenzo.eu)
- Primary Adapters: REST 엔드포인트, 콘솔, 테스트 스크립트 등 Port 구현체이며, Port 호출을 Core 에 전달 (marcolenzo.eu)
- Secondary Adapters: DB 드라이버, HTTP 클라이언트, 메시지 큐 클라이언트 등 Core 호출에 대응하는 구현체 (geeksforgeeks.org)
필수 구성요소 Vs 선택 구성요소
- 필수: Domain Core, Ports (Inbound/Outbound), 최소 하나의 Primary 및 Secondary Adapter
- 선택: 다양한 Adapter (예: 테스트 스크립트), 커뮤니케이션 인터셉터 (로깅, 인증), 메시지 브로커 어댑터 등
6. 주요 원리 & 작동 원리
핵심 원리
- 의존성 역전 규칙 (DIP): 내부 (Core) 는 추상인 Port 만 알고, 구현체 (Adapter) 는 외부에서 주입 (medium.com)
- 포트와 어댑터 분리: 핵심은 외부와 계약만 정의하며, 실제 기술 구현은 어댑터에 위임
- 단방향 의존성: 어느 구현체도 Core 로직을 직접 참조하지 않음—테스트, 유연성, 확장성 확보 (scalastic.io)
작동 흐름 (다이어그램)
flowchart LR A[UI/API/CLI/Test] -->|calls| IP[Input Port Interface] IP -->|executes| UA[Use Case in Core] UA -->|calls| OP[Output Port Interface] OP -->|delegates to| SA[Secondary Adapter] SA -->|interacts with| Svc[DB / MQ / External API] UA -->|returns result| IP IP -->|responds to| A
- 입력 흐름: Primary Adapter → Input Port → Core → Output Port → Secondary Adapter → 외부 시스템
- 출력 흐름: Core → Output Port → Adapter → 외부 시스템 의 결과 반영
- 테스트 흐름: 어댑터 없이 Port 만으로 Core 유닛 테스트 가능
7. 구성 요소 기능 요약
구성 요소 | 역할 |
---|---|
Domain/Core | 순수 비즈니스 로직, 상태 유지 |
Input Port | 외부 요청 인터페이스 계약 |
Use Cases | 유스케이스 단위 실행 로직 |
Output Port | 외부 호출 인터페이스 계약 |
Primary Adapter | 사용자 요청을 Port 호출로 변환 |
Secondary Adapter | Port 호출을 외부 기술로 실행 |
DI 구성 모듈 | Port 와 Adapter 를 연결하는 설정 코드 |
아래는 8. 구현 기법, 9. 장단점 분석, 10. 문제점 상세 분석, 11. 실무 사용 예시, 12. 활용 사례, 13. 구현 예시 섹션입니다.
8. 구현 기법 (Implementation Techniques)
Port 인터페이스 정의
- Inbound Port:
OrderService.createOrder(request)
- Outbound Port:
PaymentGateway.process(paymentInfo)
- Inbound Port:
Use Case Interactor 작성
- 예:
CreateOrderInteractor implements OrderService
, 내부에서 도메인 엔티티 생성 및 포트 호출
- 예:
Primary Adapter 구현
- 예: REST 컨트롤러 (
Spring @RestController
,FastAPI router
) 에서 Inbound Port 호출
- 예: REST 컨트롤러 (
Secondary Adapter 구현
- JDBC, JPA, HTTP 클라이언트 사용하여 Outbound Port 구현 (예:
RestPaymentAdapter
)
- JDBC, JPA, HTTP 클라이언트 사용하여 Outbound Port 구현 (예:
의존성 주입 설정
- Spring, NestJS, Dagger, Spring DI 설정 또는 FastAPI
Depends()
사용
- Spring, NestJS, Dagger, Spring DI 설정 또는 FastAPI
테스트 작성
- Core 테스트: Port 만 Mock 사용 → Logic 검증
- Adapter 테스트: 실제 DB/API 외부 호출 모킹 혹은 인메모리 구현
모듈화 구조
9. 장단점 분석
✅ 장점
구분 | 항목 | 설명 |
---|---|---|
장점 | 독립성 | 핵심 로직은 외부 기술에 무관함 |
테스트 용이성 | 핵심 Port 만 모킹하면 유닛 테스트 가능 | |
유연성 | 새로운 Adapter 추가 시 Core 무변경 가능 | |
모듈화 | 마이크로서비스 설계 및 배포 단위 조직 가능 | |
유지보수성 | 의존성 흐름 내부 집중, 외부 변화 영향 최소화 |
❌ 단점
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | 초기 복잡도 | Port/Adapter 계층 도입으로 구조 복잡 | 단순 프로젝트엔 Vertical Slice 방식 or Onion 구조 병행 |
설정·DI 오버헤드 | DI 설정 및 모듈 구성 필요 | DI 컨테이너 자동 구성 기능 or 컴파일타임 DI 사용 | |
Boilerplate 증가 | Port/Adapter 양식 반복 | 코드 생성 스크립트, 공통 유틸 라이브러리 활용 |
10. 문제점 상세 분석
구분 | 항목 | 원인 | 영향 | 탐지 | 예방 | 해결 |
---|---|---|---|---|---|---|
문제점 | Port 남발 | 핵심이 아닌 부분까지 추상화 대상 설정 | Adapter 수 급증, 이해도 저하 | 코드 리뷰 시 계층 과도 확인 | 필요 부분만 Port 도입, 가이드 정책 설정 | Port 정리 리팩토링, 코드 컨벤션 강화 |
문제점 | Adapter 복잡도 | 외부 API 변화로 복잡한 매핑 처리 | Core 호출 실패, 버그 증가 | 테스트 실패, 예외 로그 증가 | Adapter Unit 테스트 자동화 | Mapper 추상 추출, 오류 대응 유틸 추가 |
11. 실무 사용 예시
기술 조합 | 목적 | 효과 |
---|---|---|
Java Spring + Hexagonal | 레거시 Monolith → 모듈화 리팩토링 | 도메인 분리, Adapter 변경 용이 |
NestJS + TypeORM + RabbitMQ | 이벤트 기반 주문 처리 | Handler/test 분리, 비동기 기능 개선 |
Python FastAPI + SQLAlchemy | API 서버에서 비즈니스 모듈 분리 | Port 테스트 가능, DB 교체 쉬움 |
12. 활용 사례: 이벤트 기반 주문 시스템
🏗️ 시스템 구성
graph LR REST[REST API] --> IP[OrderService Port] IP --> UC[CreateOrderInteractor] UC --> OP1[PaymentGateway Port] OP1 --> Adp1[StripeAdapter] UC --> OP2[OrderRepo Port] OP2 --> Adp2[PostgresAdapter] UC --> OP3[EventPort] OP3 --> Adp3[RabbitMQAdapter]
- Use Case: 주문 생성, 결제 처리, DB 저장, 이벤트 발행
- Adapter: HTTP 컨트롤러, Stripe API, PostgreSQL, RabbitMQ
📋 워크플로우
REST 요청 → Inbound Port 호출
Interactor:
- 결제 처리 via StripeAdapter
- 주문 저장 via PostgresAdapter
- 이벤트 발행 via RabbitMQAdapter
각 Adapter 는 해당 외부 서비스를 호출
Core 로직은 Port 만 참고하여 외부 독립적
13. 구현 예시 (Python + FastAPI)
|
|
- Port 정의 → UseCase 구현
- Adapter 를 주입하여 외부 연동 구성
- Core 테스트 용이, Adapter 만 단위 테스트 가능
다음은 요청하신 도전 과제, 실무 적용 고려사항 및 주의할 점, 최적화 고려사항 및 주의할 점, 용어 정리, 참고 및 출처 항목입니다.
🎯 14. 도전 과제 (Challenges)
빈약한 도메인 모델 (Anemic Domain Model)
- 원인: 도메인 로직이 Core 가 아닌 Adapter 에 흩어짐
- 영향: 객체지향 설계 원칙 위반, 유지보수성 저하 (medium.com, geeksforgeeks.org)
- 탐지/진단: 도메인 객체가 단순 데이터 보관자일 경우
- 예방:
Tell‑don’t‑ask
패턴, 도메인 내 로직 집중 - 해결: 도메인 모델로 비즈니스 로직 이동 후 테스트 자동화
ORM 및 데이터 피그 (Primitive Obsession)
- 원인: 도메인 모델과 DB 간 불일치, ORM 직접 노출
- 영향: 테스트 어려움, 데이터 중복 변환 발생 (medium.com, medium.com)
- 탐지: DB 전용 속성 증가
- 예방: Value Object 사용, domain‑first 디자인
- 해결: Adapter 에서 DTO 매핑, 도메인 순수성 유지
Adapter 간 상호 호출 (오용)
- 원인: Adapter 들 간 직접 종속 생김
- 영향: 계층 분리 무너짐, 복잡도 상승 (reddit.com, tpierrain.blogspot.com, softwareengineering.stackexchange.com, netflixtechblog.com)
- 탐지: 코드 리뷰 및 의존성 분석
- 예방: " 항상 중심 (Core) 을 통해 통신 " 원칙 고수
- 해결: refactoring, 코드 리뷰 기반 개선
프로젝트 스캐터 (분산화 복잡도)
- 원인: 과도한 패키지 및 계층 분리
- 영향: 초기 진입 장벽, 코드 탐색 어려움
- 탐지: 디렉터리 및 모듈 수 증가
- 예방: 패키징 정책 수립, 문서화, 코드 템플릿 활용
- 해결: 리팩토링, 템플릿 기반 초기세팅
📋 15. 실무 적용 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
프로젝트 규모 | 단순한 CRUD 에는 과도한 구조 | Vertical Slice 또는 Onion 구조 사용 |
개발 역량 | 패턴 숙지 부족 시 오남용 위험 | 코드 컨벤션, 설계 리뷰, 교육 병행 |
포트 정립 | 과도한 Port 정의 | 핵심 흐름만 Port 로 정의, 모듈화 |
DI 도구 선택 | 무거운 DI 도구 사용 시 개념 복잡 | 컴파일타임 DI(AOT) 또는 경량 프레임워크 |
테스트 범위 | 유닛 테스트만으로는 부족 | End-to-end, Contract Testing 병행 |
문서화 | 구조 복잡으로 팀 혼돈 발생 | 다이어그램 + 코드 매핑 문서화 필수 |
⚙️ 16. 최적화 고려사항 및 주의할 점
항목 | 설명 | 권장사항 |
---|---|---|
성능 오버헤드 | DI 호출, 포트/어댑터 레이어 소모 | AOT DI, 캐싱, 매핑 최소화 |
빌드 병목 | 모듈 분리 많으면 CI 빌드 지연 | 병렬/증분 빌드 지원, 모노레포 구조 고려 |
코드량 증가 | Port/Adapter 코드 반복 | 코드 생성 도구, 템플릿 활용 |
테스트 유지관리 | Adapter 불일치로 유지 단점 발생 | Contract Tests + Mock 자동화 |
패키징 복잡도 | 모듈 수 증가로 탐색 어려움 | 기능 기반 패키징, 의존 관계 문서화 |
📌 용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
패턴 | Anemic Domain Model | 도메인 객체가 로직 없이 데이터만 보관 |
패턴 | Tell‑don’t‑ask | 도메인 메시징 중심 설계, 로직 캡슐화 강화 |
개념 | Value Object | 불변 객제로 도메인 속성 표현 |
설계원칙 | Contract Testing | 사용자의 기대 인터페이스를 테스트로 보증 |
도구 | AOT DI | 컴파일 시점 의존성 주입 구조 생성 |
📚 참고 및 출처
- Albert Llousas, Hexagonal Architecture: Common pitfalls (softwareengineering.stackexchange.com, en.wikipedia.org, reddit.com, netflixtechblog.com, medium.com, medium.com)
- StackOverflow 답변 Drawbacks of Hexagonal Architecture (stackoverflow.com)
- Morteza Tavakoli, Practical Review of Hexagonal Architecture (medium.com)
- Pedro Torres, Benefits and Drawbacks (medium.com)
- JDriven Blog, Why you should NOT use Hexagonal architecture (jdriven.com)
- Netflix Tech Blog, Ready for changes with Hexagonal Architecture (netflixtechblog.com)
- Wikipedia, Hexagonal architecture (software) (en.wikipedia.org)
해당 내용까지 Hexagonal Architecture 의 이론과 실무 가이드라인을 모두 정리했습니다. 추가 코드 최적화, 프레임워크별 적용 전략 등을 원하시면 언제든 요청해 주세요!
안녕하세요! 헥사고날 아키텍처 (Hexagonal Architecture) 에 대한 포괄적인 조사를 시작하겠습니다. 먼저 관련 정보를 웹에서 검색하여 최신 자료를 수집한 후 체계적으로 정리해드리겠습니다.
헥사고날 아키텍처 (Hexagonal Architecture) 포괄적 조사
1. 태그 및 분류
태그: Hexagonal-Architecture
, Ports-and-Adapters
, Software-Design-Pattern
, Domain-Driven-Design
주제 분류 적절성 검토:
제시된 분류 “Computer Science and Engineering” > “Software Engineering” > “Design and Architecture” > “Architecture Styles and Patterns” > “Architecture Patterns” 는 매우 적절합니다. 헥사고날 아키텍처는 소프트웨어 설계 패턴 중 아키텍처 패턴에 해당하며, 시스템의 전체적인 구조를 정의하는 설계 원칙이므로 이 분류가 정확합니다.
요약 문장: 헥사고날 아키텍처는 비즈니스 로직을 외부 의존성으로부터 격리하여 포트 (Ports) 와 어댑터 (Adapters) 를 통해 통신하게 함으로써 테스트 용이성, 유연성, 유지보수성을 향상시키는 소프트웨어 설계 패턴입니다.
개요: 2005 년 Alistair Cockburn 이 창안한 헥사고날 아키텍처는 포트와 어댑터 패턴이라고도 불리며, 애플리케이션의 핵심 비즈니스 로직을 중앙에 배치하고 외부 시스템과의 상호작용을 표준화된 인터페이스를 통해 처리합니다. 이를 통해 기술적 의존성 없이 독립적으로 테스트 가능하고 유연한 시스템을 구축할 수 있습니다.
2. 첫 번째 부분: 기본 개념 및 배경
핵심 개념
헥사고날 아키텍처의 핵심 개념들과 실무 구현 요소:
이론적 핵심 개념:
- 포트 (Ports): 애플리케이션과 외부 세계 간의 통신을 위한 인터페이스 또는 계약
- 어댑터 (Adapters): 포트의 구체적인 구현체로 특정 기술과 도메인을 연결
- 도메인 (Domain): 비즈니스 로직이 포함된 애플리케이션의 핵심 부분
- 의존성 역전 (Dependency Inversion): 고수준 모듈이 저수준 모듈에 의존하지 않도록 하는 원칙
실무 구현 요소:
- Primary Ports (Driver Ports): 애플리케이션을 구동하는 외부 액터용 인터페이스 (REST API, CLI 등)
- Secondary Ports (Driven Ports): 애플리케이션이 외부 서비스를 호출하기 위한 인터페이스 (데이터베이스, 외부 API 등)
- Primary Adapters (Driver Adapters): 웹 컨트롤러, CLI 인터페이스 등
- Secondary Adapters (Driven Adapters): 데이터베이스 리포지토리, 외부 API 클라이언트 등
- 컨피규레이터 (Configurator): 의존성 주입을 통해 포트와 어댑터를 연결하는 구성 요소
배경
헥사고날 아키텍처는 2005 년 Alistair Cockburn 에 의해 창안되었습니다. 전통적인 계층형 아키텍처 (Layered Architecture) 의 한계를 극복하기 위해 개발되었으며, 특히 다음과 같은 문제점들을 해결하고자 했습니다:
- 계층 간 원하지 않는 의존성
- 사용자 인터페이스 코드와 비즈니스 로직의 오염
- 데이터베이스 중심의 설계로 인한 비즈니스 로직 경시
- 테스트의 어려움과 기술적 종속성
목적 및 필요성
주요 목적:
- 비즈니스 로직을 외부 기술적 관심사로부터 격리
- 애플리케이션이 사용자, 프로그램, 자동화된 테스트, 배치 스크립트에 의해 동등하게 구동될 수 있도록 함
- 런타임 디바이스와 데이터베이스로부터 독립적으로 개발 및 테스트 가능하도록 함
필요성:
- 기술적 유연성: 기술 스택 변경 시 비즈니스 로직에 영향을 주지 않음
- 테스트 용이성: 외부 의존성 없이 단위 테스트 가능
- 병렬 개발: 팀별로 독립적인 개발 가능
- 유지보수성: 명확한 책임 분리로 코드 이해도 향상
주요 기능 및 역할
헥사고날 아키텍처의 주요 기능:
- 비즈니스 로직 보호: 도메인 계층을 외부 변화로부터 보호
- 인터페이스 표준화: 포트를 통한 일관된 통신 방식 제공
- 기술적 독립성: 특정 프레임워크나 라이브러리에 종속되지 않음
- 확장성: 새로운 어댑터 추가로 기능 확장 용이
- 테스트 격리: 목 (Mock) 객체를 통한 독립적 테스트
특징
핵심 특징:
- 대칭성과 비대칭성의 공존: 모든 어댑터가 헥사곤에 의존하지만, 의존성 방향은 다름
- 기술 무관성: 도메인 계층은 특정 기술에 의존하지 않음
- 포트의 다중성: 하나의 포트에 여러 어댑터 연결 가능
- 계층 없는 구조: 전통적인 상하 계층 구조 대신 중심 - 외곽 구조
3. 두 번째 부분: 원칙과 구조
핵심 원칙
- 의존성 역전 원칙 (Dependency Inversion Principle): 고수준 모듈이 저수준 모듈에 의존하지 않음
- 단일 책임 원칙 (Single Responsibility Principle): 각 구성 요소는 하나의 명확한 책임을 가짐
- 인터페이스 분리 원칙 (Interface Segregation Principle): 클라이언트가 사용하지 않는 인터페이스에 의존하지 않음
- 개방 - 폐쇄 원칙 (Open-Closed Principle): 확장에는 열려있고 수정에는 닫혀있음
주요 원리
작동 원리 다이어그램:
graph TD subgraph "External World" UI[User Interface] CLI[Command Line] Test[Test Scripts] DB[(Database)] API[External API] Queue[Message Queue] end subgraph "Hexagonal Architecture" subgraph "Adapters Layer" PA1[Primary Adapter<br/>Web Controller] PA2[Primary Adapter<br/>CLI Handler] SA1[Secondary Adapter<br/>DB Repository] SA2[Secondary Adapter<br/>API Client] end subgraph "Ports Layer" PP1[Primary Port<br/>UseCase Interface] PP2[Primary Port<br/>Command Interface] SP1[Secondary Port<br/>Repository Interface] SP2[Secondary Port<br/>External Service Interface] end subgraph "Core Domain" BL[Business Logic<br/>Domain Services<br/>Entities<br/>Value Objects] end end UI --> PA1 CLI --> PA2 Test --> PA1 PA1 --> PP1 PA2 --> PP2 PP1 --> BL PP2 --> BL BL --> SP1 BL --> SP2 SP1 --> SA1 SP2 --> SA2 SA1 --> DB SA2 --> API SA1 --> Queue
구조 및 아키텍처
구조 다이어그램:
graph LR subgraph "Outside World" subgraph "Left Side (Drivers)" W[Web UI] M[Mobile App] T[Tests] C[CLI] end subgraph "Right Side (Driven)" D[(Database)] E[External API] F[File System] Q[Message Queue] end end subgraph "Hexagon" subgraph "Primary Adapters" WA[Web Adapter] MA[Mobile Adapter] TA[Test Adapter] CA[CLI Adapter] end subgraph "Ports" PP[Primary Ports<br/>Inbound] SP[Secondary Ports<br/>Outbound] end subgraph "Application Core" AC[Application Services<br/>Use Cases<br/>Domain Logic<br/>Business Rules] end subgraph "Secondary Adapters" DA[Database Adapter] EA[API Adapter] FA[File Adapter] QA[Queue Adapter] end end W --> WA M --> MA T --> TA C --> CA WA --> PP MA --> PP TA --> PP CA --> PP PP --> AC AC --> SP SP --> DA SP --> EA SP --> FA SP --> QA DA --> D EA --> E FA --> F QA --> Q
필수 구성요소:
애플리케이션 코어 (Application Core)
- 기능: 비즈니스 로직과 도메인 규칙 포함
- 역할: 애플리케이션의 핵심 기능 수행
- 특징: 외부 기술에 의존하지 않는 순수한 비즈니스 로직
포트 (Ports)
Primary Ports (Inbound Ports)
- 기능: 외부에서 애플리케이션으로의 진입점 정의
- 역할: 사용 사례 인터페이스 제공
- 특징: 애플리케이션이 제공하는 서비스 명세
Secondary Ports (Outbound Ports)
- 기능: 애플리케이션에서 외부 시스템으로의 진출점 정의
- 역할: 외부 서비스 의존성 추상화
- 특징: 애플리케이션이 필요로 하는 외부 서비스 계약
어댑터 (Adapters)
Primary Adapters (Driver Adapters)
- 기능: 외부 시스템의 요청을 애플리케이션 포트로 변환
- 역할: 사용자 인터페이스, API 엔드포인트 등 제공
- 특징: 포트에 의존하며 외부 기술과 결합
Secondary Adapters (Driven Adapters)
- 기능: 애플리케이션의 요청을 외부 시스템 호출로 변환
- 역할: 데이터베이스 접근, 외부 API 호출 등 수행
- 특징: 포트를 구현하며 구체적인 기술 사용
선택 구성요소:
- 컨피규레이터 (Configurator)
- 기능: 의존성 주입을 통한 포트와 어댑터 연결
- 역할: 애플리케이션 시작 시 구성 요소들을 조립
- 특징: 보통 프레임워크나 DI 컨테이너가 담당
구현 기법
1. 의존성 주입 (Dependency Injection)
- 정의: 객체의 의존성을 외부에서 주입하는 기법
- 구성: 인터페이스 정의, 구현체 작성, 주입 설정
- 목적: 느슨한 결합과 테스트 용이성 확보
- 실제 예시: Spring Framework 의 @Autowired, 생성자 주입
2. 인터페이스 분리 (Interface Segregation)
- 정의: 클라이언트별로 특화된 인터페이스 제공
- 구성: 기능별 인터페이스 분리, 역할별 포트 정의
- 목적: 불필요한 의존성 제거 및 결합도 감소
- 실제 예시: UserRepository, OrderService 등 도메인별 인터페이스
3. 어댑터 패턴 (Adapter Pattern)
- 정의: 호환되지 않는 인터페이스를 연결하는 패턴
- 구성: 타겟 인터페이스, 어댑티, 어댑터 클래스
- 목적: 기존 시스템과의 호환성 확보
- 실제 예시: 레거시 시스템 연동, 서드파티 라이브러리 통합
4. 세 번째 부분: 장단점 및 활용
장점
구분 | 항목 | 설명 |
---|---|---|
장점 | 테스트 용이성 | 포트 인터페이스를 통한 목 객체 사용으로 독립적인 단위 테스트 가능 |
장점 | 기술적 유연성 | 어댑터 교체만으로 다른 기술 스택 사용 가능 |
장점 | 비즈니스 로직 보호 | 도메인 계층의 격리로 비즈니스 규칙 안정성 확보 |
장점 | 병렬 개발 지원 | 포트 정의 후 팀별 독립적 개발 가능 |
장점 | 유지보수성 향상 | 명확한 책임 분리로 코드 이해도 및 수정 용이성 증대 |
장점 | 확장성 | 새로운 어댑터 추가로 기능 확장 시 기존 코드 영향 최소화 |
단점과 문제점 그리고 해결방안
단점
구분 | 항목 | 설명 | 해결책 |
---|---|---|---|
단점 | 초기 복잡성 증가 | 포트와 어댑터 계층 추가로 인한 구조 복잡성 | 단계적 도입, 명확한 문서화, 팀 교육 |
단점 | 학습 곡선 | 새로운 패턴 적용을 위한 팀의 학습 시간 필요 | 실습 위주 교육, 멘토링, 점진적 적용 |
단점 | 과도한 추상화 | 단순한 애플리케이션에 대한 불필요한 복잡성 | 프로젝트 규모 및 복잡성 평가 후 적용 여부 결정 |
단점 | 성능 오버헤드 | 간접 호출 계층으로 인한 약간의 성능 저하 | 성능 크리티컬 부분 식별 및 최적화 |
문제점
구분 | 항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|---|
문제점 | 매핑 복잡성 | 도메인 모델과 외부 모델 간 변환 | 개발 시간 증가, 버그 발생 위험 | 코드 리뷰, 자동화 테스트 | 명확한 매핑 규칙 정의 | 매핑 라이브러리 사용, 자동 생성 도구 |
문제점 | 디버깅 어려움 | 간접 호출로 인한 호출 스택 추적 복잡성 | 문제 해결 시간 증가 | 로깅 강화, 트레이싱 도구 | 적절한 로깅 전략 수립 | 분산 트레이싱, 상세 로깅 |
문제점 | 포트 설계 오류 | 부적절한 인터페이스 설계 | 잦은 인터페이스 변경, 결합도 증가 | 인터페이스 안정성 분석 | 도메인 이해 선행, 프로토타이핑 | 리팩토링, 인터페이스 재설계 |
도전 과제
기술적 도전 과제:
마이크로서비스 환경에서의 적용
- 원인: 분산 시스템의 복잡성과 네트워크 통신
- 영향: 트랜잭션 관리, 데이터 일관성 문제
- 해결 방법: 이벤트 소싱, CQRS 패턴, 분산 트레이싱
레거시 시스템 통합
- 원인: 기존 시스템의 구조적 제약
- 영향: 어댑터 복잡성 증가, 성능 저하
- 해결 방법: 단계적 마이그레이션, Anti-Corruption Layer 패턴
실시간 성능 요구사항
- 원인: 간접 호출로 인한 지연시간
- 영향: 응답 시간 증가, 처리량 감소
- 해결 방법: 성능 프로파일링, 캐싱 전략, 비동기 처리
분류 기준에 따른 종류 및 유형
분류 기준 | 종류/유형 | 설명 |
---|---|---|
포트 방향성 | Primary Ports (Inbound) | 외부에서 애플리케이션으로의 진입점 |
포트 방향성 | Secondary Ports (Outbound) | 애플리케이션에서 외부로의 진출점 |
어댑터 유형 | Driver Adapters | 애플리케이션을 구동하는 외부 시스템용 |
어댑터 유형 | Driven Adapters | 애플리케이션이 구동하는 외부 시스템용 |
기술별 분류 | Web Adapters | HTTP, REST API, GraphQL 등 |
기술별 분류 | Database Adapters | RDBMS, NoSQL, 파일 시스템 등 |
기술별 분류 | Messaging Adapters | 메시지 큐, 이벤트 스트림 등 |
실무 사용 예시
구분 | 사용 목적 | 함께 사용되는 기술 | 효과 |
---|---|---|---|
E-commerce | 결제 시스템 독립성 | 다양한 결제 게이트웨이 API | 결제 업체 변경 시 비즈니스 로직 무변경 |
금융 서비스 | 규제 대응 | 레거시 시스템, 신규 API | 규제 변경 시 빠른 대응, 시스템 안정성 |
콘텐츠 관리 | 다중 채널 지원 | 웹, 모바일, API | 채널별 독립적 개발, 일관된 비즈니스 로직 |
IoT 플랫폼 | 디바이스 다양성 | 다양한 통신 프로토콜 | 새로운 디바이스 추가 시 유연성 |
활용 사례
사례: E-commerce 주문 관리 시스템
시스템 구성:
- Primary Adapters: REST API Controller, 관리자 웹 인터페이스
- Secondary Adapters: MySQL Database, Redis Cache, 결제 API, 배송 API
- Core Domain: 주문 처리 로직, 재고 관리, 가격 계산
시스템 구성 다이어그램:
graph TB subgraph "External Systems" Customer[고객 웹사이트] Admin[관리자 대시보드] PaymentGW[결제 게이트웨이] ShippingAPI[배송 API] InventoryDB[(재고 DB)] end subgraph "Primary Adapters" WebAPI[Web API Controller] AdminWeb[Admin Web Controller] end subgraph "Application Core" OrderService[주문 서비스] InventoryService[재고 서비스] PricingService[가격 서비스] end subgraph "Secondary Adapters" PaymentAdapter[결제 어댑터] ShippingAdapter[배송 어댑터] DBAdapter[데이터베이스 어댑터] end Customer --> WebAPI Admin --> AdminWeb WebAPI --> OrderService AdminWeb --> OrderService OrderService --> InventoryService OrderService --> PricingService OrderService --> PaymentAdapter OrderService --> ShippingAdapter OrderService --> DBAdapter PaymentAdapter --> PaymentGW ShippingAdapter --> ShippingAPI DBAdapter --> InventoryDB
Workflow:
- 고객이 웹사이트에서 주문 요청
- Web API Controller 가 요청을 받아 주문 서비스 호출
- 주문 서비스가 재고 확인, 가격 계산 수행
- 주문 서비스가 결제 어댑터를 통해 결제 처리
- 결제 완료 후 배송 어댑터를 통해 배송 요청
- 데이터베이스 어댑터를 통해 주문 정보 저장
- 고객에게 주문 완료 응답 반환
기존 시스템과의 차이점:
- 전통적 계층형: 컨트롤러 → 서비스 → 리포지토리 순차적 의존
- 헥사고날: 도메인 중심으로 포트를 통한 양방향 통신
- 유연성: 결제 업체 변경 시 어댑터만 교체하면 됨
- 테스트: 모든 외부 의존성을 목 객체로 대체 가능
구현 예시
Python 을 사용한 주문 관리 시스템 구현:
|
|
4. 네 번째 부분: 최적화 및 고려사항
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
구분 | 고려사항 | 주의할 점 | 권장사항 |
---|---|---|---|
프로젝트 규모 | 복잡한 비즈니스 로직이 있는 중대형 프로젝트 | 단순한 CRUD 애플리케이션에는 과도한 복잡성 | 프로젝트 복잡도 평가 후 적용 결정 |
팀 역량 | 팀의 아키텍처 패턴 이해도와 경험 | 학습 곡선으로 인한 초기 생산성 저하 | 점진적 도입과 지속적인 교육 |
포트 설계 | 안정적이고 명확한 인터페이스 정의 | 잦은 인터페이스 변경으로 인한 불안정성 | 도메인 모델링 선행, 프로토타이핑 |
테스트 전략 | 포트별 독립적인 테스트 케이스 작성 | 통합 테스트 복잡성 증가 | 테스트 피라미드 전략 수립 |
성능 최적화 | 간접 호출로 인한 성능 오버헤드 | 과도한 추상화로 인한 성능 저하 | 성능 크리티컬 경로 식별 및 최적화 |
최적화하기 위한 고려사항 및 주의할 점
구분 | 최적화 방법 | 주의사항 | 권장사항 |
---|---|---|---|
포트 설계 | 응집도 높은 인터페이스 설계 | 너무 세분화된 포트로 인한 복잡성 | 단일 책임 원칙 기반 포트 설계 |
어댑터 관리 | 어댑터별 독립적인 라이프사이클 관리 | 어댑터 간 의존성 발생 | 컨테이너 기반 의존성 관리 |
매핑 최적화 | 도메인 -DTO 간 효율적인 매핑 | 과도한 매핑 로직으로 인한 성능 저하 | 매핑 라이브러리 활용, 자동화 |
캐싱 전략 | 포트 레벨에서의 캐싱 적용 | 캐시 무효화 복잡성 증가 | 적절한 캐시 범위 설정 |
모니터링 | 포트별 성능 및 에러 모니터링 | 분산된 로깅으로 인한 추적 어려움 | 통합 모니터링 시스템 구축 |
주제와 관련하여 주목할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
관련 패턴 | Clean Architecture | 동심원 구조 | 헥사고날 아키텍처를 확장한 계층형 구조 |
관련 패턴 | Onion Architecture | 계층 분리 | 의존성 역전을 통한 도메인 중심 설계 |
관련 패턴 | Domain-Driven Design | 도메인 모델링 | 비즈니스 도메인 중심의 소프트웨어 설계 |
구현 기술 | Dependency Injection | 의존성 주입 | 느슨한 결합을 위한 핵심 기술 |
구현 기술 | Interface Segregation | 인터페이스 분리 | 클라이언트별 특화된 인터페이스 제공 |
테스트 전략 | Test Doubles | 목 객체 | 외부 의존성 격리를 위한 테스트 기법 |
마이크로서비스 | Anti-Corruption Layer | 부패 방지 계층 | 레거시 시스템과의 통합 패턴 |
마이크로서비스 | Event-Driven Architecture | 이벤트 기반 통신 | 비동기 메시징을 통한 서비스 간 통신 |
주제와 관련하여 반드시 학습해야할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
설계 원칙 | SOLID 원칙 | 객체지향 설계 | 의존성 역전, 단일 책임 등 핵심 원칙 |
설계 패턴 | Adapter Pattern | 구조 패턴 | 호환되지 않는 인터페이스 연결 |
설계 패턴 | Strategy Pattern | 행위 패턴 | 알고리즘 캡슐화 및 교체 가능성 |
아키텍처 | Layered Architecture | 계층형 구조 | 전통적 아키텍처와의 차이점 이해 |
구현 기술 | Dependency Injection | IoC 컨테이너 | Spring, Guice 등 DI 프레임워크 |
테스트 기법 | Unit Testing | 단위 테스트 | Mock, Stub 을 활용한 독립적 테스트 |
도메인 모델링 | Domain-Driven Design | 전술적 설계 | Entity, Value Object, Aggregate |
마이크로서비스 | Service Decomposition | 서비스 분할 | 비즈니스 역량 기반 서비스 경계 설정 |
기타 사항
최신 트렌드와 발전 방향:
클라우드 네이티브 환경
- 컨테이너화를 통한 어댑터 독립적 배포
- 서비스 메시와의 통합
- FaaS (Function as a Service) 환경에서의 적용
마이크로서비스 아키텍처와의 융합
- 서비스별 헥사고날 아키텍처 적용
- API 게이트웨이를 통한 포트 표준화
- 이벤트 소싱과의 결합
AI/ML 시스템과의 통합
- 머신러닝 모델을 어댑터로 활용
- 데이터 파이프라인과의 연동
- 실시간 추론 시스템 구축
도구 및 프레임워크 지원:
- Java: Spring Boot, Quarkus, Micronaut
- Python: FastAPI, Django, Flask
- JavaScript/TypeScript: NestJS, Express
- C#:.NET Core, ASP.NET
성공적인 도입을 위한 체크리스트:
- 명확한 도메인 모델 정의
- 안정적인 포트 인터페이스 설계
- 효율적인 어댑터 구현
- 포괄적인 테스트 전략
- 지속적인 리팩토링 및 개선
용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
핵심 개념 | Port (포트) | 애플리케이션과 외부 세계 간의 통신 인터페이스 |
핵심 개념 | Adapter (어댑터) | 포트의 구체적인 구현체로 특정 기술과 연결 |
핵심 개념 | Hexagon (헥사곤) | 비즈니스 로직이 포함된 애플리케이션 핵심 부분 |
포트 유형 | Primary Port | 외부에서 애플리케이션으로의 진입점 인터페이스 |
포트 유형 | Secondary Port | 애플리케이션에서 외부 시스템으로의 진출점 인터페이스 |
어댑터 유형 | Driver Adapter | 애플리케이션을 구동하는 외부 시스템용 어댑터 |
어댑터 유형 | Driven Adapter | 애플리케이션이 구동하는 외부 시스템용 어댑터 |
설계 원칙 | Dependency Inversion | 고수준 모듈이 저수준 모듈에 의존하지 않는 원칙 |
설계 원칙 | Separation of Concerns | 관심사의 분리를 통한 모듈화 원칙 |
구현 기법 | Dependency Injection | 객체의 의존성을 외부에서 주입하는 기법 |
테스트 기법 | Test Double | 테스트 목적으로 실제 객체를 대체하는 객체 |
테스트 기법 | Mock Object | 호출을 검증하기 위한 테스트 더블 |
참고 및 출처
- Hexagonal Architecture (Wikipedia)
- Original Hexagonal Architecture Article by Alistair Cockburn
- Ports & Adapters Architecture Explained – Code Soapbox
- AWS Prescriptive Guidance - Hexagonal Architecture Pattern
- A Color Coded Guide to Ports and Adapters | 8th Light
- Hexagonal Architecture by example - Allegro Tech Blog
- Hexagonal Architecture in Python Implementation Examples
- Everything You Need to Know About Hexagonal Architecture - Scalastic
- Hexagonal Architecture - System Design - GeeksforGeeks
- Hexagonal Architecture: What is it and why should you use it? - Cardo AI
헥사고날 아키텍처 (Hexagonal Architecture) 는 소프트웨어 설계 패턴 중 하나로, 애플리케이션의 핵심 비즈니스 로직을 외부 요소로부터 분리하여 유연하고 유지보수가 용이한 시스템을 구축하는 것을 목표로 한다.
이 아키텍처는 Alistair Cockburn 에 의해 제안되었으며, ’ 포트와 어댑터 아키텍처 (Ports and Adapters Architecture)’ 라고도 불린다.
핵심 원칙
- 의존성 방향:
모든 의존성은 도메인 계층을 향해 안쪽으로 흐른다.
도메인 계층은 외부를 전혀 알지 못하며, 포트를 통해서만 통신한다. - 관심사의 분리:
각 계층은 명확한 책임을 가지며, 다른 계층의 구현 세부사항을 알지 못한다.
이는 시스템의 유지보수성과 테스트 용이성을 향상시킨다. - 인터페이스 기반 설계:
포트는 인터페이스로 정의되어, 구체적인 구현체 (어댑터) 를 쉽게 교체할 수 있게 한다.
주요 구성 요소
헥사고날 아키텍처는 크게 세 가지 주요 구성 요소로 이루어져 있다:
핵심 비즈니스 로직 (도메인): 애플리케이션의 중심에 위치하며, 순수한 비즈니스 로직을 포함한다.
포트 (Ports): 애플리케이션의 내부와 외부를 연결하는 인터페이스 역할을 한다. 두 가지 유형이 있다:
- 인바운드 포트: 외부에서 애플리케이션을 사용하기 위한 API
- 아웃바운드 포트: 애플리케이션이 외부 시스템을 사용하기 위한 API
어댑터 (Adapters): 포트를 구현하여 실제로 외부 시스템과 통신하는 역할을 한다. 두 가지 유형이 있다:
- Primary (Driving) Adapters: 애플리케이션을 구동하는 역할 (예: UI)
- Secondary (Driven) Adapters: 애플리케이션에 의해 구동되는 역할 (예: 데이터베이스)
작동 방식
- 외부 요청이 Primary Adapter 를 통해 들어온다.
- Primary Adapter 는 인바운드 포트를 통해 핵심 비즈니스 로직과 통신한다.
- 비즈니스 로직은 필요한 경우 아웃바운드 포트를 통해 Secondary Adapter 와 통신한다.
- Secondary Adapter 는 외부 시스템 (예: 데이터베이스) 과 상호작용한다.
장점
- 유연성: 외부 시스템이나 인프라와의 의존성이 낮아 구성요소를 쉽게 교체하거나 업데이트할 수 있다.
- 테스트 용이성: 비즈니스 로직을 독립적으로 테스트할 수 있어 테스트가 더 안정적이고 쉬워진다.
- 유지보수성: 책임이 명확히 분리되어 있어 코드의 이해와 수정이 용이하다.
- 확장성: 새로운 어댑터를 추가하거나 기존 어댑터를 수정하여 시스템을 쉽게 확장할 수 있다.
단점
- 구현 복잡성: 포트와 어댑터를 구성하고 관리하는 데 복잡성이 증가할 수 있다.
- 초기 개발 시간 증가: 아키텍처를 설계하고 구현하는 데 초기에 더 많은 시간이 소요될 수 있다.
구현 예시
|
|
용어 정리
용어 | 설명 |
---|---|