시스템 구성요소
3개의 큰 블록으로 구성
- User Interface
어떤 유저인터페이스 환경에서라도 실행될 수 있게 하는 것
(controller는 웹과 모바일 유저 인터페이스 환경에서 실행을 가능하게 하고, console command는 cli 유저 인터페이스 환경에서 실행을 가능하게 한다.) - Application Core
비즈니스 로직을 담고 있으며, user interface에 의해 호출됨
어떤 종류의 user interace인지에 상관 없이 모두 동일한 결과를 반환함 - Infrastrure Core
application core와 연결, db, 검색엔진, 써드파티 API와 같은 툴
Tool의 연결 : Port & Adapter
application이 사용하는 DB, 검색엔진, 웹서버, CLI 콘솔과 같은 외부 시스템을 툴이라고 한다.
[Adapter]
툴과 연결되는 코드 모음을 adapter라고 한다.
웹서버와 연결되는 컨트롤러, DB와 연결되는 repository가 adapter의 예시이다.
컨트롤러는 UserInterface 영역에서 application core를 호출하고,
repository는 Infrastructure 영역에 존재하여 application core에 의해 호출 당한다.
application core에 의한 요청의 주체/대상 여부로 adapter를 아래와 같이 분류 한다.
- Driving Adapter(Primary Adapter) : application core을 호출
- Driven Adapter(Secondary Adapter) : application core에 의해 호출 당함
[Ports]
Port는 툴을 사용하기 위한 명세서(인터페이스)이다.
Adapter와 Application Core의 연결은 모두 Port를 통해 이루어진다.
다만, Port는 툴이 무엇인지 알고 설계하는 것이 아니라 비즈니스 규칙에 필요한 명세를 가져야한다.
이는 Port로 하여금 툴에 종속적이지 않고 비즈니스 로직 수행에 필요로한 명세를 갖게 함으로써, 툴의 변경이 비즈니스 영역에 영향을 미치는 것을 방지한다.
Driving Adapter와 Driven Adapter의 설계 패턴
Driving adapter는 port를 감싸는 합성 구조를 갖는다.
Application Core의 구현체에 직접적으로 의존하지 않고 Port라는 추상화에 의존한다.
Application Core가 정의한 Port 명세에 맞게 Driving adapter가 호출을 진행해야한다.
Driven Adapters는 application core가 규정한 Port의 구현체이며 Port를 통해 호출된다.
Driven Adapter는 OCP를 통해 변경사항에 유연한 대응이 가능하다.
예를 들어 영속성 구현체의 ORM 프레임워크가 변동되더라도 application core와 port를 통해 연결되므로
application core의 비즈니스 영역에는 영향을 주지 않을 수 있다.
Port & Adapter 패턴을 통한 IOC 적용
Port & Adapter 패턴을 통해 얻을 수 있는 장점은 IOC 이다.
IOC란 프로그램의 제어권을 외부에 위임하는 개념이다.
헥사고날 아키텍쳐의 IOC는 Adapter의 제어권을 Application Core에 모두 위임하는 것이다.Adapter는 Application Core가 정의한 Port에 의해 구현된다.Port는 비즈니스 로직에 대한 명세이다.이는 Tool에 종속적인 Adpater가 변경되더라도, 또는 Tool이 변경되더라도 Application Core에 영향을 미치지 않는다는 것이다.
헥사고날 아키텍쳐는 외부 툴과 이에 종속적인 코드들이 비즈니스 규칙에 영향을 주지 않도록 하는 구조를 갖추고 있다.
Application core의 구성
Application Core는 그 어떠한 라이브러리나 adapter를 의존하지 않아 외부 변경사항에 영향을 받지 않는 구조를 갖춰야한다.
- Application Layer
비즈니스 로직을 구현한 서비스 객체들로 이루어져있으며 Port를 포함한다. - Domain Layer
도메인에 구체적인 데이터를 다루며 Application Layer가 의존하는 대상이다.
Port에 명시된 메서드의 인자값이나 반환값 같은 dto와 vo로 구성되어있다. - Domain Service
Domain 객체를 다루는 공통적인 로직이다.
만일 Domain Service의 로직을 Applilcation Layer에 구현하게 되면 다른 서비스에서 사용하기가 어렵다.
여러 서비스에서 공통적으로 사용하는 Domain 객체를 다루는 로직을 Domain Service에 둘 수 있다.
[참고] Domain Service에 대한 나의 견해
원문의 Domain Service에 대해 명확하게 이해가 되지 않는다.
내 개인적인 생각에는 정산, 결제, 회원과 같이 업무 기준으로 나뉜 서비스에서 공통적으로 사용해야하는 로직이 Domain Service가 아닌가 싶다.
예를 들어, 요일별 할인 행사를 진행하는 서비스가 있을 때,
요일별 할인율은 정산 서비스와 결제 서비스 모두 알고 있어야한다.
이를 Application Layer에 구현하게 되면 두 서비스에 코드가 중복되어 적용되기에
Domain Service Layer를 공통적으로 두고 사용하자는 것이 아닐까 싶다.
Components
레이어 단위의 분리도 중요하지만 좀 더 큰 범위에서 코드 분리도 중요하다.
우리는 Package by Component에 주목할 것이다.
컴포넌트의 예시로는 하나의 서비스를 정산, 청구, 회원, 계정(계좌)와 같은 도메인 기준으로 나눈 것을 들 수 있다.
레이어 분리처럼 컴포넌트 분리 또한 서로의 존재를 직접적으로 알지 못하기에 느슨한 결합과 높은 응집도의 장점을 준다.
허나, 컴포넌트 분리는 때론 데이터 공유나 다른 컴포넌으에 로직 수행을 요청해야하는 상황이 나올 수 있다.
이러한 경우 우리는 event나 shared kernel 등을 통해 해결할 수 있다.
이벤트를 발생시켜 다른 컴포넌트에 로직 수행을 요청할 수 있고,
Shared Kernel을 통해 반환되는 데이터를 컴포넌트에 종속적이지 않게 받을 수 있다.
Shared Kernel
모든 컴포넌트에서 의존하는 공유 자원
컴포넌트간 통신 시 각각의 컴포넌트가 Shared Kernel에 의존함으로써 컴포넌트간 분리 가능
Shared Kernel의 변경은 모든 컴포넌트에 영향을 미침
-> 따라서 Shared Kernel은 최대한 작게 구성해야함
만약 polyglot(다양한 언어로 작성)한 MSA환경이라면 Shared Kernel은 언어에 상관없이 지원되야 한다.
따라서 프로그래밍 언어가 다르기에 이벤트 클래스를 Shared Kernel로 사용하는게 아니라 이벤트에 대한 정의(이벤트명, 속성, 메서드)를 Shared Kernel로 선언하여 언어에 상관없이 JSON객체로 변환(사용)할수 있도록 하는 것이 polyglot하며 MSA환경에서 더욱 적절하다.
제어의 흐름
제어의 흐름은 user에서 application core를 거쳐 infrastructure에 도달하고 다시 이 경로로 돌아온다.
그렇다면 클래스를 어떻게 구성할 것인가, 어떤 것이 어떤 것에 의존해야하는가?
하지만 이 내용에서 강조하는 것은 DIP를 통한 Application Core를 지키는 것과
(application core가 Repository Interface에 의존하므로 구현체 변경이 영향을 끼치지 않음)
OCP를 통한 Infrastructure의 유연한 변경을 얘기하는 것이다.
결론
헥사고날 아키텍쳐의 설계 목표는 느슨한 결합과 높은 응집도이다.
Port & Adapter를 통한 DIP 적용으로 application core가 adapter에 의존하지 않도록 하고,
OCP를 적용하여 새로운 adpater로 변경 및 확장이 가능하다.
이 모든 것을 결국 application core를 외부 변경사항으로 부터 지키자는 것이다.
비즈니스 영역을 명확하게 도출해내어 application core를 설계하고 기술적인 영역은 adapter 영역에 구현함으로써, 헥사고날 아키텍쳐의 장점을 얻을 수 있다.
[참고 자료]
DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together
In my last posts I’ve been writing about many of the concepts and principles that I’ve learned and a bit about how I reason about them. But I see these as just pieces of big a puzzle. …
herbertograca.com
'Design pattern' 카테고리의 다른 글
멀티모듈로 헥사고날 구현[2] - application layer를 pojo로 구성하기 (0) | 2024.06.16 |
---|---|
멀티모듈로 헥사고날 구현[1] - 모듈 구성 (0) | 2024.03.10 |
템플릿 메서드 패턴, 전략 패턴, 템플릿/콜백 패턴 비교 분석 (1) | 2024.02.17 |