역사
- 대부분의 아키텍처는 아키텍처가 동시다발적으로 탄생하고 스타일이 정의됨
- 그러나 마이크로서비스는 먼저 스타일이 정의되되었음
- 마이크로서비스의 주요 목표는 bounded context의 논리적 개념을 물리적으로 모델링하는 고도의 디커플링임
토폴로지
- 다른 분산 아키텍처보다 서비스 규모가 훨씬 작다
- 각 서비스에는 데이터베이스 등 기타 종속적인 컴포넌트가 작동하기에 필요한 모든 것이 준비되어 있다
분산
- 뭔가 공유함으로써 불거지는 문제들은 각 서비스를 자체 프로세스로 분리하면 모두 자연스럽게 해소됨
- 지금처럼 무료 오픈 소스 운영 체제가 자동화 머신 프로비저닝 도구와 결합되는 수준까지 발전되기 전에는 도메인마다 자체 인프라를 둔다는 발상이 비현실적이었지만, 이제는 클라우드 리소스와 컨테이너 기술을 활용해 도메인 레벨, 운영 레벨 모두 디커플링의 이점을 누릴 수 있게 되었음
- 마이크로서비스의 분산 속성 탓에 성능은 다소 부정적. 아무래도 네트워크 호출은 메서드 호출보다 오래 걸리고 엔드포인트마다 보안 검증 절차를 거치면 그만큼 처리 시간이 소요되므로 시스템을 설계하는 아키텍트는 서비스 세분도에 대해 심사숙고해야 함
- 마이크로서비스는 분산 아키텍처의 일종이므로 숙련된 아키텍트라면 개발자가 서비스 경계를 넘나드는 트랜잭션을 사용하지 않도록 권고해야 함. 이 아키텍처는 서비스를 얼마나 세분화할 것인가를 잘 결정하는 것이 성공의 관건임
bounded context
- 서비스마다 도메인이나 워크플로를 모델링하는 개념
- 클래스, 기타 부속 컴포넌트, 데이터베이스 스키마 등 애플리케이션 작동에 필요한 모든 것들이 각 서비스에 들어감
- 커플링보다는 차라리 중복이 낫다
얼마나 서비스를 세분화하여야 하는가
- 아키텍트가 경계를 찾는 데 도움이 되는 몇 가지 가이드라인 제시
- 이 가이드라인을 활용하여, 서비스를 얼마나 분리해야 하는지 판단 가능
목적
- 아키텍처 스타일의 본래 의도인 도메인은 어떻게 분리해야 하는가?
- 전체 애플리케이션을 대표하여 하나의 핵심 기능을 제공하는 것이 이상적임
트랜잭션
- 여러 엔티티가 함께 개입하여 작동되는 트랜잭션은 좋은 서비스 경계 후보임
- 분산 아키텍처에서 문제가 될 소지가 있으므로 그런 문제를 방지할 수 있도록 설계하는 것이 바람직함
- MSA 에서 트렌젝션이 문제가 될 수 있는가
코레오그래피
- 도메인 격리가 잘 되어 있더라도, 통신을 해야 제대로 작동하는 서비스 세트를 구축하였다면 아키텍트는 통신 오버헤드를 줄이기 위해 더 큰 서비스로 다시 뭉치는 것을 고려해야 할 수 있음
데이터 격리
- 경계 콘텍스트 개념에 따라 데이터를 격리하여야 함
- MSA 는 통합 지점으로 사용하는 공유 스키마, 데이터베이스 등 모든 종류의 커플링을 없애려고 함
- 어떻게 아키텍처 전체에 데이터를 분산시킬 수 있는지 결정하여야 함
- 도메인을 어떤 fact 에 대한 소스로 식별하여 값을 잘 가져오게 하던지
- 데이터베이스 복제나 캐시 기술로 정보를 분산시키던지
- 각 서비스마다 적합한 도구를 선택할 수 있음
- 그때그때 상황에 맞게 적합한 데이터베이스 선택 가능
API 레이어
- 여러 시스템 사이의 API 레이어 존재(gateway)
- API 레이어를 중재자나 오케스트레이션 도구로 사용해서는 안 됨
- 모든 비즈니스 로직은
bounded context내부에서 발생하여야 하며, 오케스트레이션 등의 다른 로직을 중재자에 넣는 것은 규칙 위반임
운영 재사용
- 실제로 커플링이 더 유리한 아키텍처 부분은 어떻게 처리해야 하는가(모니터링, 로깅, 서킷브레이커 등)
- 이를 해결하기 위한 사이드카 패턴이 존재함
- 공통 운영 관심사를 각 서비스마다 별도의 컴포넌트에 두고, 해당 팀이나 공유 인프라팀이 소유할 수 있도록 함
- 서비스 메시를 구축하면 로깅,모니터링 등의 관심사를 아키텍처 전체적으로 일원화하여 제어할 수 있음
- 서비스 메시 안에 서비스 디스커버리를 포함시켜 MSA 의 일부로 만드는 경우가 많음
- 유저 인터페이스나 다른 호출부 시스템이 한곳을 통해 일관되고 탄력적으로 서비스를 발견 / 생성 가능
프런트엔드
- 프론트엔드는 보통 두 가지 스타일로 나뉨
- 단일 유저 인터페이스가 API 레이어를 통해 호출하는 모놀리식 프런트엔드. 리치 데스크톱, 모바일, 웹 애플리케이션의 형태로 구현됨
각 MSA 서비스가 자기 서비스에 해당하는 유저 인터페이스를 내보내고, 프론트엔드는 그 인터페이스 컴포넌트를 조정한다. 마이크로프런트앤드 패턴은 리엑트 같은 컴포넌트 웹 기반 프레임워크를 사용하거나, 다른 오픈소스를 사용한다
통신
- 동기로 할지, 비동기로 할지 근본적인 통신 방법을 결정하여야 함
- 일반적으로
포로토콜 인지 이종 간 상호 운영성을 활용함 - 프로토콜 인지
- 각 서비스는 다른 서비스 호출시에 어떤 프로토콜을 사용할지 알아야 함
- 이종
- 서비스마다 사용하는 플렛폼이 저마다 다른 폴리글랏 환경을 완벽하게 지원하여야 함
- 상호 운용성
- 여러 서비스가 서로 호출하면서 협력하여야 한다
코레오그래피와 마이크로서비스의 오케스트레이션
- 코레오그래피는 브로커 이벤트 형태의 아키텍처와 통신 스타일이 동일함
- 중앙의 중재자가 따로 없이 bounded context 철학에 충실함
- 도메인/아키텍처 동형성은 특정한 문제에 어떤 아키텍처 스타일이 적합한지 평가할 때 살펴보아야 할 핵심 특성임. MSA 는 디커플링을 추구하므로 브로커 이벤트기반 아키텍처를 닮았음
- 이처럼 필요한 경우 다른 서비스를 호출하여 데이터를 받아온다
- 완벽한 정답은 없지만, 에러 처리나 조정 같은 문제는 코레오그레피 환경에서는 훨씬 더 복잡해진다
- 처음 호출된 서비스는 자신의 도메인 책임과 더불어 자신이 호출하는 서비스를 중재하는 중재자 역할도 겸해야 한다. 이런 패턴을 프론트 컨트롤러 패턴이라 부른다
오케스트레이션
복잡한 서비스는 오케스트레이션을 하는 경우도 있다.
- 이렇게 하면 서비스 간 커플링은 발생하지만 한 서비스가 조정 작업을 전담하므로 다른 서비스는 거의 영향을 받지 않는다
트렌젝션과 사가
- 트렌젝션을 어떻게 해야 하는지, 그리고 원자성 문제는 어떻게 처리해야 하는지 문제가 대두된다
- 여러 서비스에 걸쳐 트랜잭션을 걸고 싶어하는 아키텍트가 있다면 “그렇게 하지 말라!” 고 충고하고 싶다
- 대신에, 컴포넌트 세분도를 바로잡는 편이 좋다. 트랜잭션 경계는 서비스 세분도를 가늠할 수 있는 지표 중 하나다
- 다만 어쩔 수 없는 경우에는 트랜잭션 오케스트레이션을 처리하는 패턴이 있다
사가
- 서비스는 여러 서비스 호출에 대해 중재자 노릇을 함
- 어느 한 파트라도 실패한다면, 중재자는 지금까지 성공한 모든 트랜잭션 파트에게 과거에 처리한 내용을 undo 하라는 요청을 보낸다. 이를 보상 트랜잭션 프래임워크라 한다
- undo 하는 로직을 추가하여야 하니 작업량이 2배가 됨