소프트웨어 아키텍처 101 - CHAPTER 17 마이크로 서비스 아키텍처 스타일
17.1 역사
마이크로서비스 아키텍처는 사용 초기부터 이름이 붙여졌고 2014년 3월 마틴 파울러(Martin Fowler)와 제임스 루이스(James Lewis)가 쓴 ‘Microservices’라는 유명한 블로그 게시글 덕분에 널리 퍼지게 되었죠.
마이크로서비스는 소프트웨어 프로젝트의 논리적 설계 프로세스를 강조한 도메인 주도 설계(domain-driven design, DDD) 사상의 영향을 많이 받았습니다. 특히, 디커플링 스타일을 나타낸 경계 콘텍스트(bounded context) 개념은 마이크로서비스에 결정적인 영향을 미쳤습니다.
경계 콘텍스트 내부에서는 코드, 데이터 스키마 같은 내부 요소들이 함께 연결되어 작동되지만, 경계 콘텍스트 외부에 있는 것들(예: 데이터베이스, 클래스 정의)은 전혀 커플링되지 않습니다. 이로써 각 콘텍스트는 다른 구성원을 수용하지 않고 자신이 필요한 것들만 정의할 수 있습니다.
재사용은 유익하지만 커플링이 문제입니다(소프트웨어 아키텍처 제1법칙). 재사용을 선호하는 시스템을 설계하다 보면 결국 상속이나 조합을 이용하여 재사용하기 위해 커플링이 맺어지게 됩니다.
그러나 고도의 디커플링이 아키텍트의 목표라면 재사용보다 중복을 우선할 것입니다. 마이크로서비스의 주요 목표는 경계 콘텍스트의 논리적 개념을 물리적으로 모델링하는 고도의 디커플링입니다.
17.2 토폴로지
마이크로서비스는 단일 목적만 가지기 때문에 오케스트레이션 기반의 서비스 지향 아키텍처와 같은 다른 분산 아키텍처보다 서비스 규모가 훨씬 작습니다.
17.3 분산
마이크로서비스는 분산 아키텍처를 형성합니다. 서비스는 자체 프로세스로 실행됩니다. 서비스를 이 정도로 디커플링하면 애플리케이션을 호스트하는 육중한 멀티테넌트 인프라 아키텍처에서 자주 발생하는 문제들을 간단히 해결할 수 있습니다. 그러나 지원해야 할 애플리케이션이 계속 늘어나면 결국 일부 리소스는 공유 인프라의 제약을 받을 수밖에 없습니다. 또 공유 애플리케이션 간의 잘못된 분리도 문제가 되는 경우가 있습니다.
이처럼 뭔가 공유함으로써 불거지는 문제들은 각 서비스를 자체 프로세스로 분리하면 모두 자연스럽게 해소됩니다.
마이크로서비스의 분산 속성 탓에 성능은 다소 부정적입니다.
마이크로서비스는 분산 아키텍처의 일종이므로 숙련된 아키텍트라면 개발자가 서비스 경계를 넘나드는 트랜잭션을 사용하지 않도록 권고해야 합니다. 이 아키텍처는 서비스를 얼마나 세분화할 것인가를 잘 결정하는 것이 성공의 관건입니다.
17.4 경계 콘텍스트
마이크로서비스의 근본 철학은 경계 콘텍스트(bounded context) 개념입니다. 마이크로서비스는 커플링을 가급적 삼가므로 이 아키텍처 스타일을 구축하는 아키텍트는 커플링보다는 차라리 중복이 낫다고 생각합니다.
마이크로서비스는 도메인 분할 아키텍처의 개념을 극도로 우려낸 결과물입니다. 각각의 마이크로서비스는 어느 한 도메인이나 그 서브도메일을 나타냅니다. 여러 면에서 마이크로서비스는 도메인 주도 설계의 논리적인 개념을 물리적으로 구현한 것입니다.
17.4.1 세분도
아키텍트는 마이크로서비스의 알맞은 세분도를 찾기 위해 고심하다가 종종 서비스를 너무 잘게 나누는 실수를 저지르곤 합니다.
‘마이크로서비스’라는 용어는 명칭(label)이지, 명세(description)가 아니다. - 마틴 파울러
마이크로서비스라는 용어를 창시한 사람은 새로운 스타일을 뭐라고 부르긴 해야 했고, 당시 지배적이었던 스타일인 서비스 지향 아키텍처의 ‘거대 서비스(gigantic service)’에 대비되는 용어로 ‘작은 서비스(micro service)’라는 의미를 나타내고 싶었습니다.
서비스 경계(service boundary)는 도메인이나 워크플로를 캡처하는 것이 목표입니다. 어느 애플리케이션에서 당연하게 여겨지는 경계가 다른 시스템 파트에서는 너무 단위가 클 수도 있고, 어떤 비즈니스 프로세스는 다른 비즈니스 프로세스보다 더 단단히 커플링되어 있을지 모릅니다.
목적
가장 확실한 경계는 바로 이 아키텍처 스타일의 본래 의도인 도메인입니다. 각 마이크로서비스가 기능적으로 매우 응집되어 있고 전체 애플리케이션을 대표하여 하나의 핵심 기능을 제공하는 것이 가장 이상적인 모습입니다.
트랜잭션
여러 엔티티가 함께 개입하여 작동되는 트랜잭션은 아키텍트에게 좋은 서비스 경계 후보입니다. 분산 아키텍처에서 트랜잭션은 문제가 될 소지가 있으므로 그런 문제를 방지할 수 있도록 설계하는 것이 바람직합니다.
코레오그래피
도메인 격리는 아주 잘 되어 있지만 서로 광범위한 통신을 해야 제대로 작동되는 서비스 세트를 구축할 경우, 아키텍트는 통신 오버헤드를 줄이기 위해 더 큰 서비스로 다시 뭉치는 것을 고려해야 할 수도 있습니다.
좋은 서비스 설계안을 도출하는 유일한 방법은 이터레이션입니다. 처음 한 번 시도로는 완벽한 세분도, 데이터 종속성, 통신 스타일을 찾아내기 어렵지만, 여러 가지 옵션을 반복해서 적용해보면 좋은 방향으로 설계를 다듬어갈 수 있습니다.
17.4.2 데이터 격리
마이크로서비스는 경계 콘텍스트 개념에 따라 데이터를 격리해야 합니다.
마이크로서비스 아키텍처는 어떻게 하면 아키텍처 전체에 데이터를 분산시킬 수 있을지 결정해야 합니다.
17.5 API 레이어
API 레이어는 다양한 용도로 활용할 수 있지만 이 아키텍처의 기본 철학에 충실하려면 API 레이어를 중재자나 오케스트레이션 도구로 사용하지 말아야 합니다. 모든 비즈니스 로직은 경계 콘텍스트 내부에서 일어나야 하며, 오케스트레이션 등의 다른 로직을 중재자에 넣는 것은 규칙 위반입니다.
17.6 운영 재사용
전통적인 서비스 지향 아키텍처의 철학에 따르면 도메인이든 운영이든 가급적 많은 기능을 재사용하는 것이 좋습니다. 마이크로서비스 아키텍트는 이 두 가지 관심사를 분리하고자 합니다.
여러 마이크로서비스를 구축한 이후에 잘 살펴보면 각 마이크로서비스에 공통적인 요소가 있고 그 유사성을 활용하면 더 유리한 부분이 있음을 알게 됩니다.
이 문제를 해결하는 방법이 바로 사이드카 패턴(sidecar pattern) 입니다.
사이드카 패턴(Sidecar Pattern)
사이드카 패턴은 마이크로서비스 아키텍처에서 주 애플리케이션의 기능을 확장하거나 보조하기 위해 별도의 프로세스나 컨테이너를 함께 배치하는 디자인 패턴입니다. 이 패턴은 오토바이에 부착된 사이드카에 비유되며, 주 애플리케이션과 독립적으로 동작하면서도 동일한 수명 주기를 공유합니다.
주요 특징:
- 독립성: 사이드카는 주 애플리케이션과 별도의 프로세스나 컨테이너로 실행되므로, 서로 다른 프로그래밍 언어나 프레임워크를 사용할 수 있습니다.
- 수명 주기 공유: 사이드카는 주 애플리케이션과 함께 생성되고 종료되며, 동일한 배포 단위로 관리됩니다.
- 기능 확장: 로깅, 모니터링, 구성 관리, 네트워킹 등 주 애플리케이션의 부가 기능을 사이드카를 통해 구현할 수 있습니다.
장점:
- 유연성: 주 애플리케이션의 코드를 수정하지 않고도 새로운 기능을 추가하거나 변경할 수 있습니다.
- 격리성: 사이드카의 오류나 장애가 주 애플리케이션에 직접적인 영향을 미치지 않으므로 안정성이 향상됩니다.
- 재사용성: 공통 기능을 수행하는 사이드카를 여러 서비스에 재사용하여 개발 효율을 높일 수 있습니다.
적용 사례:
- 로깅 및 모니터링: 사이드카를 통해 애플리케이션의 로그를 수집하고 외부 시스템으로 전송하거나, 성능 모니터링을 수행할 수 있습니다.
- 프록시 및 보안: 사이드카를 사용하여 주 애플리케이션 앞단에 프록시를 배치하거나, 보안 관련 기능을 추가할 수 있습니다.
사이드카 패턴은 특히 컨테이너 기반 환경에서 널리 사용되며, 주 애플리케이션의 기능을 유연하게 확장하고 관리하는 데 유용한 방법입니다.
ref. https://learn.microsoft.com/ko-kr/azure/architecture/patterns/sidecar
공통 운영 관심사를 각 서비스마다 별도의 컴포넌트에 두고, 해당 팀이나 공유 인프라팀이 소유할 수 있도록 합니다. 사이트카 컴포넌트는 팀이 서로 커플링되면 더 유리한 모든 운영 관심사를 도맡아 처리합니다.
각 서비스에는 공통 사이드카가 포함되어 있으므로 서비스 메시(service mesh)를 구축하면 로깅, 모니터링 등의 관심사를 아키텍처 전체적으로 일원화하여 제어할 수 있습니다. 공통 사이드카 컴포넌트는 모든 마이크로서비스에 대해 일관된 운영 인터페이스를 제공합니다.
각 사이드카는 서비스 플레인에 연결되어 자신의 서비스에 일관괸 인터페이스를 제공합니다.
각 사이드카는 서비스 플레인에 연결되어 자신의 서비스에 일관된 인터페이스를 제공합니다.
서비스 플레인(Service Plane)
서비스 플레인은 일반적으로 서비스 메쉬의 구성 요소로, 데이터 플레인(Data Plane)과 컨트롤 플레인(Control Plane)으로 나뉩니다.
- 데이터 플레인(Data Plane): 실제 서비스 간의 트래픽을 처리하는 프록시들의 집합으로, 서비스 간의 요청을 가로채고 라우팅하며, 보안 및 모니터링 기능을 수행합니다.
- 컨트롤 플레인(Control Plane): 데이터 플레인을 관리하고 구성하는 역할을 하며, 서비스 디스커버리, 정책 설정, 인증서 관리 등의 기능을 제공합니다.
서비스 메시 자체는 개발자가 서비스를 전체적으로 액세스할 수 있는 콘솔 역할을 합니다.
서비스 메쉬(Service Mesh)
서비스 메쉬는 마이크로서비스 간의 통신을 제어하고 관리하는 전용 인프라 계층입니다. 각 서비스에 사이드카 패턴을 사용하여 프록시를 추가함으로써 서비스 간 통신을 처리합니다. 이를 통해 서비스 디스커버리, 로드 밸런싱, 보안, 모니터링 등의 기능을 제공하여 서비스 간의 복잡한 통신을 효율적으로 관리할 수 있습니다.
전체 메시에서 각 서비스는 하나의 노드입니다. 서비스 메시는 각 팀의 모니터링 레벨, 로깅, 그 밖에 공통의 운영 관심사 등 운영과 커플린된 부분을 글로벌하게 제어하는 콘솔입니다.
아키텍트는 마이크로서비스 아키텍처에 탄력성을 부여하는 수단으로 서비스 디스커버리(service discovery)를 사용합니다. 보통 서비스 메시 안에 서비스 디스커버리를 포함시켜 모든 마이크로서비스의 일부로 만드는 경우가 많습니다. 서비스 디스커버리는 대개 API 레이어가 호스트하므로 유저 인터페이스나 다른 호출부 시스템이 모두 한 곳을 통해 일관되고 탄력 있게 서비스를 발견/생성할 수 있습니다.
17.7 프론트엔드
마이크로서비스 초기 비전에는 유저 인터페이스가 DDD 원칙에 충실한 경계 콘텍스트의 일부로 포함되어 있었지만, 웹 애플리케이션 및 여타 외부 제약조건에서 필요로 하는 분할의 실용성 때문에 이 목표는 달성하기 어렵습니다. 그래서 마이크로서비스 아키텍처의 유저 인터페이스는 보통 두 가지 스타일로 나타납니다.
첫 번째 스타일은 유저 요청을 처리하기 위해 단일 유저 인터페이스 API 레이어를 통해 호출하는 모놀리식 프론트엔드입니다.
이 프론트엔드는 리치 데스크톱(rich desktop), 모바일, 웹 애플리케이션의 형태로 구현합니다.
두 번째 스타일은 마이크로프런트엔드(microfrontend)입니다.
마이크로프런트엔드는 유저 인터페이스 레벨의 컴포넌트를 백엔드 서비스로 활용하여 유저 인터페이스를 동기적인 수준으로 세분화하고 격리합니다. 각 서비스는 자기 서비스에 해당하는 유저 인터페이스를 내보내고(emit), 프론트엔드는 그렇게 내보내진 유저 인터페이스 컴포넌트를 조정합니다. 이런 식으로 유저 인터페이스에서 백엔드 서비스에 이르기까지 서비스 경계를 분리함으로써 전체 도메인을 단일 팀 내부에 통합시키는 것입니다.
마이크로프런트엔드 패턴은 개발자가 컴포넌트 기반 웹 프레임워크를 사용하거나, 이 패턴을 지원하는 여러 오픈 소스 프레임워크 중 하나를 선택하는 등의 다양한 방법으로 구현할 수 있습니다.
17.8 통신
아키텍트는 동기로 할지, 비동기로 할지, 근본적인 통신 방식을 결정해야합니다. 동기 통신은 호출부가 수신부의 응답을 기다립니다. 일반적으로 마이크로서비스 아키텍처는 ‘프로토콜 인지 이종 간 상호 운용성(protocal-aware heterogeneous interoperability)를 활용합니다.
프로토콜 인지(protocol-aware)
서비스는 다른 서비스를 호출할 때 어떤 프로토콜을 사용할지 알아야(또는 발견해야) 합니다.
이종(heterogeneous)
마이크로서비스는 분산 아키텍처라서 각 서비스마다 구현 기술 스택이 상이할 수 있습니다.
상호 운용성(interoperability)
서비스는 네트워크를 통해 다른 서비스를 호출하여 정보를 주고 받으면서 협력해야 합니다.
비동기 통신은 이벤트와 메시지를 주로 사용하며 내부적으로 이벤트 기반 아키텍처를 활용합니다. 브로커 패턴과 중재자 패턴은 마이크로서비스에서 각각 코레오그래피 패턴과 오케스트레이션 패턴으로 나타납니다.
17.8.1 코레오그래피와 오케스트레이션
코레오그래피(choreography)는 브로커 이벤트 기반의 아키텍처와 통신 스타일이 동일합니다. 즉, 이 아키텍처는 중앙의 중재자가 따로 없고 경계 콘텍스트 철학에 충실합니다. 따라서 아키텍트는 서비스 간에 분리된 이벤트를 구현하는 것이 자연스럽다고 생각합니다.
코레오그래피(Choreography)
코레오그래피는 마이크로서비스 아키텍처에서 서비스 간 협업을 관리하는 방식 중 하나로, 각 서비스가 독립적으로 이벤트를 발행하고 구독하여 상호작용하는 패턴입니다. 중앙 조정자 없이 서비스들이 자율적으로 동작하며, 이를 통해 느슨한 결합과 확장성을 확보할 수 있습니다.
마이크로서비스 아키텍트는 디커플링을 추구하므로 마이크로서비스의 형상은 브로커 이벤트 기반 아키텍처를 닮았고 이 두 패턴은 서로 공생(symbiotic) 관계입니다.
코레오그래피에서 각 서비스는 중앙 중재자 없이 자신의 필요에 따라 다른 서비스를 호출합니다.
마이크로서비스 아키텍처는 서비스 지향 아키텍처처럼 전역 중재자를 따로 두지 않으므로 여러 서비스를 조정해야 할 경우에는 스스로 로컬 중재자를 만들 수 있습니다.
소프트웨어 아키텍처 제1법칙에 따라 어느 솔루션이든 완벽한 정답은 없고 각각 일장일단이 있습니다. 코레오그래피 아키텍트는 최대한 디커플링한다는 아키텍처 스타일의 철학을 고집함으로써 가장 많은 이점을 이끌어내려고 하지만, 에러 처리, 조정 같은 공통적인 문제는 코레오그래피 환경에서 훨씬 더 복잡해집니다.
[그림 17-9]에서 처음 호출된 서비스는 자신의 다른 도메인 책임과 더불어 여러 타 서비스를 전체적으로 조정하는 중재자 역할도 겸합니다. 이런 패턴을 프론트 컨트롤러 패턴(front controller pattern)이라고 합니다. 어떤 한 서비스가 명목상 더 복잡한 중재자 노릇을 하는 셈인데, 이 패턴은 서비스 복잡도가 증가하는 단점이 있습니다.
복잡한 비즈니스 프로세스는 [그림 17-10]처럼 오케스트레이션을 사용하는 방법도 있습니다.
[그림 17-10]처럼 워크플로에 필요한 복잡한 처리를 담당하면서 조정 역할도 수행하는 중재자를 두면 서비스 간 커플링은 발생하지만 어느 한 서비스가 조정 작업을 전담하므로 다른 서비스는 거의 영향을 받지 않습니다. 사실, 도메인/워크플로는 내재적으로 커플링되는 경우가 많습니다. 아키텍트가 할 일은, 도메인과 아키텍처 두 마리 토끼를 모두 쫓는 방향으로 커플링을 가장 잘 나타낼 수 있는 방법을 찾아내는 것입니다.
17.8.2 트랜잭션과 사가
마이크로서비스 아키텍트는 극도의 디커플링을 갈망하지만 여러 서비스에 걸친 트랜잭션을 어떻게 조정하는 게 좋을지 고심에 빠질 때가 많습니다.
서비스 경계를 넘나드는 트랜잭션은 그 자체로 마이크로서비스 아키텍처의 핵심 디커플링 원칙에 위배됩니다(그리고 가장 나쁜 형태의 동적 커네이선스, 값 커네이선스를 유발합니다). 여러 서비스에 걸쳐 트랜잭션을 걸고 싶어하는 아키텍트가 있다면 “그렇게 하지 말라!”고 충고하고 싶습니다. 대신 컴포넌트 세분도를 바로잡으세요. 트랜잭션 경계는 서비스 세분도를 가늠할 수 있는 일반적인 지표 중 하나입니다.
물론, 항상 예외는 있습니다. 이런 경우라면 적지 않은 트레이드오프를 감수하고 트랜잭션 오케스트레이션을 처리하는 패턴이 있습니다.
마이크로서비스에서 널리 사용되는 분산 트랜잭션 패턴은 [그림 17-11]의 사가 패턴(saga pattern)입니다.
[그림 17-11]에서 서비스는 여러 서비스 호출에 대해 중재자 노릇을 하면서 트랜잭션을 조정합니다. 중재자는 트랜잭션을 구선하는 파트를 하나씩 호출하여 성공/실패 여부를 기록하고 그 결과에 따라 흐름을 조정합니다.
어느 한 파트라도 실패하면 중재자는 전체 트랜잭션 파트가 모두 성공하지 못하게 해야 합니다.
[그림 17-12]에서 트랜잭션의 첫 번째 파트는 성공했지만 두 번째 파트가 실패한 경우, 중재자는 지금까지 성공한 모든 트랜잭션 파트에게 과거에 처리했던 내용을 언두(undo)하라는 요청을 보냅니다. 이런 종류의 트랜잭션 조정을 보상 트랜잭션 프레임워크(compensating transaction framework)라고 합니다. 이 패턴은 전체가 다 성공했다고 중재자가 알릴 때까지 중재자로부터 받은 각 요청을 보류 상태로 두는 식으로 구현합니다. 하지만 여기에 비동기 요청이 끼어들고, 특히 보류된 트랜잭션 상태에 따라 새로운 요청이 등장하면서 설계가 무척 복잡해집니다. 또 네트워크 레벨에서도 조정 트래픽이 꽤 많이 발생합니다.
트랜잭션 작업마다 두(do)/언두(undo) 로직을 개발하는 식으로 보상 트랜잭션 프레임워크를 구형할 수도 있습니다. 이렇게 하면 트랜잭션 수행 중 조정은 덜 필요하지만, 언두 작업은 두 작업보다 엄청나게 복잡해져 설계, 구현, 디버깅 작업량 역시 2배 이상 증가할 것입니다.
여러 서비스에 트랜잭션을 걸어주는 것이 기술적으로 불가능한 건 아니지만, 이럴 거면 굳이 마이크로서비스 패턴을 선택할 이유가 없습니다. 물론 그래도 예외는 있기 마련이니, 필요한 경우 사가 패턴을 조금씩 곁들여 사용할 것을 권고합니다.
17.9 아키텍처 특성 등급
[그림 17-13]에서 눈여겨 볼 부분은 자동화 배포, 시험성(테스트 용이성), 그 밖에 이 등급표에 없는 것들을 비롯한 현대 엔지니어링 프랙티스를 이 아키텍처가 매우 훌륭하게 지원한다는 사실입니다.
이 아키텍처의 주안점은 확장성, 탄력성, 진화성입니다.
마이크로서비스는 성능 문제가 불거질 때가 많습니다. 분산 아키텍처는 특성상 작업을 완료하기 위해 잦은 네트워크 호출을 해야 하므로 성능 오버헤드가 높고, 엔드 포인트마다 신원 및 접근 권한을 확인하는 보안 체크도 해야 합니다. 마이크로서비스 세상에서는 과도한 네트워크 호출을 줄이고 성능을 개선하기 위해 데이터 캐시, 데이터 복제 등의 기술을 많이 사용합니다. 커플링이 적을수록 통신 속도가 증가하고 병목이 줄기 때문에 오케스트레이션보다는 코레오그래피를 더 많이 사용하는 편입니다.
마이크로서비스는 당연히 도메인 중심적인 아키텍처로서 각 서비스의 경계와 도메인이 일치해야 합니다. 극도의 디커플링을 추구하는 이 아키텍처 철학은 수많은 이들의 두통을 유발하지만, 잘만 만들면 어마어마한 이점을 누릴 수 있습니다.
17.10 더 읽을 거리
- 샘 뉴먼(Sam Newman)의 『마이크로서비스 아키텍처 구축』(한빛미디어, 2017)
- 마크 리처즈(Mark Richards)의 『Microservice vs. Service-Oriented Architecture』(O’reilly, 2016)
- 마크 리처즈(Mark Richards)의 『Microservices AntiPatterns and Pitfalls』(O’reilly, 2016)
요약
17.1 역사
- 마이크로서비스 아키텍처의 등장 배경
- 2014년 마틴 파울러와 제임스 루이스의 블로그 게시글로 널리 알려짐.
- 도메인 주도 설계(DDD)의 경계 콘텍스트 개념에 큰 영향을 받음.
- 내부 요소들은 긴밀하게 연결되지만 외부와는 커플링을 최소화함.
- 재사용과 커플링의 문제
- 재사용은 유익하지만 커플링을 유발할 수 있음.
- 마이크로서비스는 재사용보다 중복을 선택하여 디커플링을 극대화함.
17.2 토폴로지
- 서비스 규모의 차이
- 마이크로서비스는 단일 목적을 가지므로 다른 분산 아키텍처보다 서비스 규모가 작음.
17.3 분산
- 분산 아키텍처 형성
- 서비스는 자체 프로세스로 실행되어 공유 인프라의 제약을 줄임.
- 성능과 세분화의 문제
- 분산으로 인해 성능이 저하될 수 있음.
- 적절한 서비스 세분화가 성공의 핵심 요소임.
17.4 경계 콘텍스트
17.4.1 세분도
- 서비스 경계 설정의 중요성
- 알맞은 세분도를 찾는 것이 중요하며, 과도한 세분화는 문제를 야기함.
- 서비스 경계 설정 방법
- 도메인 중심의 경계 설정이 이상적임.
- 트랜잭션과 통신 오버헤드를 고려하여 경계를 조정해야 함.
- 이터레이션의 필요성
- 반복적인 설계를 통해 최적의 서비스 구조를 찾아야 함.
17.4.2 데이터 격리
- 데이터 격리의 원칙
- 경계 콘텍스트에 따라 데이터를 격리하여 서비스 간 의존성을 최소화함.
17.5 API 레이어
- API 레이어의 역할 제한
- 중재자나 오케스트레이션 도구로 사용하지 않고, 비즈니스 로직은 각 경계 콘텍스트 내부에서 처리해야 함.
17.6 운영 재사용
- 사이드카 패턴의 활용
- 공통 운영 기능을 각 서비스의 사이드카로 분리하여 재사용성 및 관리 효율성 향상.
- 서비스 메쉬의 도입
- 사이드카와 서비스 메쉬를 통해 로깅, 모니터링 등의 운영 관심사를 일원화함.
17.7 프론트엔드
- 두 가지 프론트엔드 스타일
- 모놀리식 프론트엔드: 단일 API 레이어를 통해 서비스에 접근.
- 마이크로프런트엔드: 프론트엔드 컴포넌트를 서비스별로 분리하여 독립적으로 개발 및 배포.
17.8 통신
17.8.1 코레오그래피와 오케스트레이션
- 코레오그래피
- 중앙 중재자 없이 서비스들이 이벤트를 통해 자율적으로 상호작용.
- 디커플링을 극대화하지만 에러 처리 등이 복잡해질 수 있음.
- 오케스트레이션
- 중앙 중재자가 서비스를 조정하여 복잡한 비즈니스 프로세스를 관리.
- 일부 커플링이 발생하지만 전체 시스템의 복잡도를 줄임.
17.8.2 트랜잭션과 사가
- 분산 트랜잭션의 어려움
- 서비스 경계를 넘는 트랜잭션은 디커플링 원칙에 어긋남.
- 가능한 서비스 세분도를 조정하여 트랜잭션 범위를 한정해야 함.
- 사가 패턴의 활용
- 트랜잭션을 분산 환경에서 관리하기 위한 보상 트랜잭션 프레임워크.
- 트랜잭션 실패 시 이전 작업을 취소하는 로직을 구현해야 함.
17.9 아키텍처 특성 등급
- 장점
- 확장성, 탄력성, 진화성, 자동화 배포, 테스트 용이성 등 현대 엔지니어링 프랙티스에 적합.
- 단점
- 분산으로 인한 성능 저하 가능성.
- 복잡한 통신과 데이터 관리 필요.
17.10 더 읽을 거리
- 샘 뉴먼(Sam Newman)의 『마이크로서비스 아키텍처 구축』
- 마크 리처즈(Mark Richards)의 『Microservice vs. Service-Oriented Architecture』
- 마크 리처즈(Mark Richards)의 『Microservices AntiPatterns and Pitfalls』