해당 포스트는 도메인 주도 설계로 시작하는 마이크로 서비스 개발 책을 개인적으로 요약한 내용입니다.
마이크로서비스 애플리케이션 아키텍쳐
로버트 C. 마틴은 소프트웨어의 가치는 행위 가치와 구조 가치로 나뉜다고 말한다. 여기서 행위 가치는 소프트웨어의 기능을 말하며, 구조 가치는 소프트웨어 아키텍처를 말한다.
3.1 비즈니스 로직은 어디에? - 관심사의 분리
소프트웨어의 핵심은 비즈니스 로직이라는 말을 많이 들어봤을 것이다. 비즈니스 로직이란 규칙(rule), 흐름(flow), 개념(concept)을 표현하는 용어다. 개발자의 역할은 문제 영역의 비즈니스 로직을 분석 및 이해하고 프로그래밍 언어라는 도구로 잘 표현하는 일이다.
설계 원칙 중 관심사의 분리9separation of concerns)라는 원칙이 있다. 이것은 시스템의 각 영역이 처리하는 관심사가 분리되어 잘 관리돼야 한다는 의미이고, 이 원칙은 시스템을 이해하고 변경하기 쉽게 만든다.
기술과 비즈니스 로직을 분리했을 때 복잡성이 낮아지고 유지보수성도 높아진다. 특히 객체지향 분석설계에서는 비즈니스 로직을 누가 봐도 이해하기 쉽게 구조화하는 객체 모델로 표현하는 것을 강조했다.
기존 국내의 개발 방식은 객체지향 분석설계랑은 거리가 멀었고 서비스에 SQL로 처리하는 방식이였다. 그러다보니 SQL문이 길어지고 프로시저로 처리하는 일들이 많게 되었다. 이런 경우 디비 시스템에 과부화를 일으키게 되었고 유지보수에도 어려움이 왔다.
3.1.1 데이터베이스 중심 아키텍처의 문제점
데이터베이스 중심 아키텍처란 비즈니스의 개념이 데이터베이스에서 처리되는 것을 의미한다. 이 구조는 간단 처리 로직의 경우에는 편하지만 업무가 복잡해지면 점점 복잡성을 제어할 수 없게 된다는 단점이 있다. 또한 업무 개념이 특정 저장 기술인 관계형 데이터베시으 테이브로 표현되고 업무가 복잡해질수록 업무 규칙이 데이터 질의 언어인 SQL과 섞여 표현된다.
데이터베이스 중심의 아키텍처는 데이터베이스 서버의 성능에 따라 서비스의 품질이 좌우가 된다. 그리고 데이터가 쌓이게 될 경우 점점 처리하는데 느려지게 된다. 클라우드 시대에는 필요한 만큼 인프라를 유연하게 이용할 수 있고, 저장 기술 또한 선택지가 다양하다. 웹, 모바일, 명령형 인터페이스(CLI), IOT 기기 등 여러 디바이스의 입출력을 지원해야 하고, 관계형 데이터베이스, 메모리 데이터베이스, NoSQL, 메시지 큐까지 다양한 저장소와의 연계가 필요하다.
3.2 헥사고날 아키텍쳐와 클린 아키텍처
전통적으로 많이 사용되는 레이어드 아키텍처를 설명하고 최근의 마이크로 서비스 설계자들이 마이크로서비스 내부 구조를 유연하게 구성하기 위해 적용하는 헥사고날 아키텍처와 클린 아키텍처를 살펴보자
3.2.1 레이어드 아키텍처
레이어드 아키텍처는 물리적인 티어의 개념과 달리 논리적인 개념이다.
그림과 같이 물리적인 서버 티어의 레이어를 프리젠테이션, 비즈니스 로직, 데이터 액세스의 놀리적인 게층으로 구분할 수 있다.
플레젠테이션 층의 관심사는 화면 표현 및 전환 처리이고, 비즈니스 로직 층의 관심사는 비즈니스 개념 및 규칙, 흐림 제어이며, 데이터 액세스 층의 관심사는 데이터 처리이다.
레이어드 아키텍처 레이어 규칙
- 상위 계층이 하위 계층을 호출하는 단방향성을 유지한다.
- 상위 게층은 하위의 여러 게층을 모두 알 필요 없이 바로 밑의 근접 계층만 활용한다.
- 상위 게층이 하위 계층에 영향을 받지 않게 구성해야 한다.
- 하위 계층은 자신을 사용하는 상위 계층을 알지 못하게 구성해야 한다.
- 계층 간의 호출은 인터페이스를 통해 호출하는 것이 바람직하다.(구현 클래스에 직접 의존하지 않음으로써 약한 결합을 유지해야 한다)
이러한 방식은 로버트 C. 마틴이 정의한 객체지향 설계 원칙 중 하나인 의존성 역전 원칙을 만족하는 것처럼 보인다. 하지만 개방 폐쇄의 원칙까지 살펴본다면 문제가 있다.
개방 폐쇄 원칙은 소프트웨어 개체는 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다는 원칙이다. 상위 계층에서 하위 계층으로 흐르게 되고, 이에 따른 소스코드의 의존성은 제어 흐름의 방향대로 따를 수밖에 없다.
여기서 의존성 역전 웍칙이 필요한대. 데이터 액세스 게층에서 정의한 인터페이스를 경게를 넘어 비즈니스 로직 계층으로 옮긴다. 그러며 데이터 액세스 계층의 구현체는 비즈니스 로직의 게층의 인터페이스를 바라볼 수 밖에 없다.
3.2.2 헥사고날 아키텍처
헥사고날 아키텍처는 앨리스테어 콕번이 제시한 아키텍처로서 '포트 앤드 어댑터 아키텍처'라고도 한다.
아래 그림을 간단히 살펴보면 헥사고날 아키텍처에는 고수준의 비즈니스 로직을 표현하는 내부 영역과 인터페이스 처리를 담당하는 저수준의 외부 영역으로 나뉜다. 외부영역은 외부에서 들어오는 요청을 처리하는 인바운드 어댑터와 비즈니스 로직에 의해 호출되어 외부와 연계되는 아웃바운드 어댑터로 구성된다.
헥사고날 아키텍처의 가장 큰 특징은 고수준의 내부 영역이 외부의 구체 어댑터에 전혀 의존하지 않게 한다는 것이다.
포트는 인바운드/아웃바운드 포트로 구분되는데, 인바운드 포트는 내부 영역의 사용을 위해 표출된 API이며, 외부 영역은 인바운드 어댑터가 호출한다. 여기서 의존관계역전 원칙과 같이 아웃바운드 포트가 외부의 아웃바운드 어댑터를 호출해서 외부 시스템과 연계하는 것이 아니라 아웃바운드 어댑터가 아웃바운드 포트에 의존해서 구현된다.
외부 영역에 존재하는 어댑터의 종류는 REST API, 커맨드 핸들러, MVC컽느롤러, 이벤트 메시지 구독 핸들러 등이 될 수 있고, 아웃바운드 어댑터로는 데이터 액세스 처리를 담당하는 DAO, 이벤트 메시지를 발행하는 클래스, 외부 서비스를 호출하는 프락시 등이 될 수 있다.
3.2.3 클린 아키텍처
소프트웨어를 부드럽게 유지하는 방법은 무엇일까? 즉, 구조 중에서 선택할 쑤 있는 것을 가능한 한 오랫동안 열어두는 것이다.
정중앙에는 엔티티가 있다. 업무 규칙은 사업적으로 수익을 얻거나 비용을 줄일 수 있는 규칙 혹인 절차를 말한다.
그 다음으로는 엔티티를 감싸는 객체는 유스케이스다. 유스케이스는 자동화된 시스템을 사용하는 처리 절차를 기술한다. 애플리케이션에 특화된 업무 규칙을 표현하며 시스템을 사용하는 흐름을 담는다.
나머지 모든 여역이 세부사항이다. 세부사항으로는 입출력 장치, 저장소, 웹 시스템, 서버, 프레임워크, 통신 프로토콜이 될 수 있으며, 세부 사항과 유스케이스의 관계를 의존 관계 역전의 원칙을 이용해 플러그인처럼 유연하게 처리해야한다.
3.3 마이크로서비스의 내부 구조 정의
앞에서 본 레이어드 아키텍처와 헥사고날, 클린 아키텍처가 강조되는 이유는 마이크로서비스가 지향하는 유연성, 확장성을 지원하는 구조이기 때문이다.
3.3.1 바람직한 마이크로서비스의 내부 아키텍처: 클린 마이크로서비스
- 지향하는 관심사에 따라 응집성을 높이고 관심사가 다른 영여과는 의존도를 낮추게 해야 한다.
- 업무 규칙으 정의하는 비즈니스 로직 영역을 다른 기술 기반 영역으로부터 분리하기 위해 노력한다.
- 세부 기술 중심, 저수준의 외부 영역과 핵심 업무규칙이 정의된 고수준의 내부 영역으로 구분한다.
- 고수준 영역은 저수준 영역에 의존하지 않게 해야 하며, 이 같은 변화가 고수준 영역에 영향을 줘서는 안된다.
- 자바처럼 인터페이스 및 추상 클래스를 지원하는 언어의 경우 저수준 영역의 구체 클래그사 고수준 영역의 추상 인터페이스에 의존하게 하는 의존성 역전의 원칙을 적용한다.
- 인터페이스는 고수준의 안정된 영역에 존재해야 하며, 저수준의 어댑터가 이를 구현한다.
3.3.2 내부 영역 - 업무 규칙
트랜잭션 스크립트 패턴
트랜잭션 스크립트 패턴에서는 비즈니스 개념을 표현하는 도메인 객체가 행위를 가지고 있지 않다.
모든 비즈니스 행위, 즉 무엇인가를 수행하는 책임은 서비스에 있다.
또한 서비스는 유스케이스 처리의 단위이고 대부분의 비즈니스 로직 처리가 서비스에서 이뤄지므로 비슷한 유스케이스의 경우 서비스에 중복되는 코드가 계속 생겨날 수 있다.
이 패턴은 비즈니스가 간단한 경우에는 쉽게 적용할 수 있다. 그렇지만 비즈니스가 복잡해질 경우 서비스 코드의 양이 점점 증가하는 등 데이터베이스 중심 아키텍처에서 겪었던 문제점이 발생할 여지가 크다. 따라서 간단한 비즈니스를 처리할 때 적용하는 것이 좋다.
도메인 모델 패턴
도메인 모델 패턴은 도메인 객체가 데이터뿐만 아니라 비즈니스 행위를 가지고 있으며, 도메인 객체가 소유한 데이터는 도메인 객체가 제공하는 행위에 의해 은닉된다.
도메인 객체는 각비즈니스 개념 및 행위에 대한 책임을 수행하고, 서비스는 비즈니스 유스케이스를 구현하기 위해 서비스의 행위를 도메인 객체에 일부분 위임해서 처리한다.
서비스의 책임들이 도메인으로 적절히 분산되기 때문에 서비스가 비대해지지 않고 서비스 메서드는 단순해진다.
여기서 더 진화해서 도메인 주도 설계의 애그리거트 패턴을 적용할 수 있는 구조이다.
도메인 주도 설계의 애그리거트 패턴
도메인 모델링을 하다 보면 객체 간의 관게를 참조로 표현하게 되는데, 참조로 정의할 경우 일대다 관계의 객체를 쉽게 사용할 수 있다는 장점이 있다. 그렇지만 업무가 복잡해지면 참조로 인한 다단계 계층 구조가 생기고 점점 참조 관께가 복잡해지고 무거워질 수 있다.
애그리거트 패턴은 이처럼 한 단위로 일관되게 처리하기 위해 다음과 같은 규칙을 부여한다.
- 애그리거트 루트만 참조한다.
- 애그리거트 내 상세 클래스를 바로 참조하지 않고 루트를 통해 참조해야 한다. 수정할 때도 마찬가지다.
- 애그리거트 간의 참조는 객체를 직접 참조하는 대신 기본 키를 사용한다.
- 기본 키를 사용하면 느슨하게 연관되고 수정이 필요하지 않은 애그리거트를 함께 수정하는 실수를 방지한다.
- 하나의 트랜잭션으로 하나의 애그리거트만 생성 및 수정한다.
3.3.3 외부 영역 - 세부사항
외부 영역은 내부 영역의 서비스 인터페이스를 사용하는 인바운드 어댑터와 내부 영역에서 선언한 아웃바운드 인터페이스를 구현하는 다양한 어댑터로 구성한다.
어뎁터는 플러그인처럼 언제든지 교체되거나 확장될 수 있어야 한다.
API 퍼블리싱 어댑터
REST API를 발행하는 인바운드 어댑터다. 엔티티를 직접 제공하지 않고 API의 필요에 맞는 DTO를 생성해서 엔티티를 변환 및 매핑해서 전달하는 것이 바람직하다.
API 프락시 어댑터
API프락시 어댑터는 다른 서비스의 API를 호출하는 아웃바운드 어댑터다. 다른 서비스의 REST API가 될 수도 있고 소켓이나 SOAP 프로토콜을 사용하는 API일 수도 있다.
저장소 처리 어댑터
저장소 처리 어댑터를 구현할 때는 데이터 처리 매커니즘을 선택할 필요가 있다. OR 매핑 방식과 SQL매핑 방식을 사용할 수 있으며, 내부 영역에서 어떤 구조를 선택하든 둘 다 사용할 수 있다. 일반적으로는 트랜잭션 스크립트 패턴을 사용할 경우 SQL 매핑 방식을 사용하고, 도메인 모델 패턴을 사용할 경우 OR 매핑 방식을 많이 선택한다.
SQL 맵핑 : mybatis
OR 맵핑 : JPA
도메인 이벤트 발행 어댑터
도메인 이벤트는 어떤 사건에 따른 상태의 변경사항을 말하는데, '주문됨', '주문 취소됨' 등의 명칭을 갖는 클래스로 구현되며, 컨슈머에게 전달 되기 위해 도메인 이벤트 발행 어댑터를 통해 발행된다. 애그리거트 패턴을 적용할 경우 도메인 이벤트는 애그리거트에서 발생한 사건이 된다.
도메인 이벤트 핸들러
도메인 이벤트 발행 어댑터가 있다면 당연히 수신할 수 있는 인바운드 어댑터도 필요하다. 도메인 이벤트 핸들러는 외부에서 발행도니 도메인 이벤트를 구독해서 내부 영역으로 전달하는 일을 수행한다.
3.4 정리
- 국내 현장에서 광범위하게 사용되고 있는 데이터베이스 중심 아키텍처는 모든 성능 측면을 데이터베이스에 의존시킴으로써 문제가 있으며, MSA의 장점을 퇴색시킬 수 있다.
- MSA 외부 아키텍처뿐마 ㄴ아니라 서비스 내부 구조도 유연하게 구조화하는 것이 필요하며 유연성과 확장성을 지원하는 대표적인 애플리케이션 아키텍처로 레이어드 아키텍처, 헥사고날 아키텍처, 클린 아키텍처가 있다.
- 일반적인 레이어드 아키텍처의 적용은 한계가 있으며, 의존성 역적의 원칙을 활용해 레이어드 아키텍처를 개선할 수 있다.
- 헥사고날 아키텍처를 활용한 마이크로서비스 아키텍처의 내부 구조에서는 외부 영역이 내부 영역에 의존하도록 구성하는 것이 바람직하다.
- 마이크로서비스의 내부 영역을 정의할 때 비즈니스가 단순한 경우에는 트랜잭션 스크립트 패턴을 활용하고, 비즈니스가 복잡한 경우에는 트랜잭션 스크립트 패턴보다는 도메인 모델 패턴을 적용한 것이 바람직하다.
본 포스팅은 여기까지 입니다.
읽어주셔서 감사합니다.
'MSA' 카테고리의 다른 글
[MSA] BackEnd For FrontEnd 패턴에 대해서 (0) | 2025.01.02 |
---|---|
[도메인 주도 설계로 시작하는 마이크로서비스개발] 2. MSA의 이해 (0) | 2021.11.08 |
[도메인 주도 설계로 시작하는 마이크로서비스개발] 1. 아마존 비즈니스 민첩성의 비밀 (0) | 2021.10.29 |