Ch14. 마이크로서비스
[서비스란 무엇인가?]
OASIS에 따르면, 서비스는 미리 정의된 인터페이스를 사용해 하나 이상의 역량에 접근하기 위한 메커니즘이다.
여기서 미리 정의된 인터페이스란 서비스로부터 데이터를 넣고 빼는 모든 메커니즘을 말한다.

랜디 숍(Randy shoup)은 서비스의 인터페이스를 외부에 노출되는 시스템 영역(front door)에 두는 것을 좋아한다.
서비스의 퍼블릭 인터페이스는 서비스 자체, 즉 서비스가 노출하는 기능을 정의한다.

이를 염두에 두고 마이크로서비스의 정의를 알아보자.
[마이크로서비스란 무엇인가?]
마이크로서비스는 자신의 마이크로 퍼블릭 인터페이스, 즉 마이크로 프론트 도어(micro-front door)에 의해 정의되는 서비스다.
마이크로서비스는 자신의 데이터베이스를 내부에 감싸고 있다.
그리고 데이터는 훨씬 간단하고 연동 지향적인 퍼블릭 인터페이스를 통해서만 접근할 수 있다.
서비스형 메서드: 완벽한 마이크로서비스?
그림 14-3에 백로그 관리 서비스가 있다.
이 서비스의 퍼블릭 인터페이스는 여덟개의 퍼블릭 메서드로 구성되고, ‘서비스 하나에 메서드 하나’ 규칙을 적용할 것이다.

이 서비스는 잘 동작하는 마이크로서비스이므로 각 서비스가 자신의 데이터베이스를 감싸고 있다.
어떤 서비스도 다른 서비스의 데이터베이스에 직접 접근할 수 없고 퍼블릭 인터페이스를 통해서만 가능하다.
그런데 만약 서비스 간에 협업을 해야 한다면 연동 관심사를 처리하기 위해 서비스의 인터페이스를 확장해야 한다.

시스템을 이처럼 작은 단위의 서비스로 분해하면 확실히 서비스의 외부 노출 시스템 영역은 최소화된다.
하지만 전체적인 시스템의 기능을 구현하기 위해서는 각 서비스에 ‘스태프만 출입 가능한’ 수많은 출입구를 추가해야 한다.
설계 목표
각 서비스가 단일 메서드만 갖도록 분해하는 간단한 휴리스틱을 적용하는 것은 여러 이유로 차선책임이 증명됐다.
마이크로 서비스 아키텍처의 목표는 유연한 시스템을 만드는 것이다.
단일 컴포넌트에만 설계 노력을 집중하고, 나머지 연동을 무시하는 다음과 같은 시스템의 정의에는 부합하지 않다.
- 함께 작동하는 연동된 것 또는 디바이스
- 특정 목적을 위해 함께 사용되는 컴퓨터 장비 및 프로그램
시스템의 복잡성
글렌포드 마이어스의 책 ‘Composite/Structured Design’에서는 절차지향적 코드의 복잡성을 줄이기 위한 방법을 논의한다.
복잡성이라는 주제에는 프로그램의 한 부분에 대한 로컬 복잡성을 최소화하려는 단순한 시도보다 더 많은 것이 있다. 더 중요한 복잡성의 유형은 글로벌 복잡성, 즉 프로그램 또는 시스템의 전체적인 구조에 관한 복잡성이다.
이 말을 이 책에 적용해보면
- 로컬 복잡성: 각각의 개별 마이크로 서비스의 복잡성
- 글로벌 복잡성: 전체 시스템의 복잡성
을 말한다.
로컬 복잡성은 서비스의 구현에 따라 달라진다.
글로벌 복잡성은 서비스 간의 상호작용과 의존성으로 정의된다.
마이크로서비스 시스템을 설계할 때는 어느 복잡성의 최적화가 더 중요할까?
글로벌 복잡성을 줄이는 것은 놀랍도롭 쉽다.
단지 시스템 구성요소 간의 상호작용을 없애기만 하면 된다.
즉, 모든 기능을 단일 모놀리식 서비스로 구현하는 것이다.
한편 로컬 복잡성만 최적화하고 시스템의 글로벌 복잡성을 무시하면 어떻게 되는지 알고 있다.
따라서 다음과 같은 관계로 볼 수 있다.

적절한 마이크로서비스 기반 시스템을 설계하려면 글로벌 복잡성과 로컬 복잡성 모두를 최적화해야 한다.
깊은 서비스로서의 마이크로서비스
소프트웨어 시스템에서 또는 모든 시스템에서 모듈은 자신의 함수와 로직에 정의된다.
함수(function)는 모듈이 해야 하는 일, 즉 비즈니스 기능을 말한다.
로직(logic)은 모듈의 비즈니스 로직, 즉 모듈이 자신의 비즈니스 기능을 구현하는 방법이다.
존 우스터하우트(John Ousterhout)는 자신의 책 ‘The Philosophy of Software Design’에서 모듈화의 개념에 대해 간단하지만 강력한 시각적 휴리스틱인 ‘깊이(depth)’를 제시했다.

그림 14-6처럼 우스터하우트는 모듈을 사각형으로 시각화하는 것을 제안했다.
사각형의 상단 끝은 모듈의 기능을 표현하거나 퍼블릭 인터페이스의 복잡성을 나타낸다.
넓은 사각형은 폭 넓은 기능을 제공한다.
좁은 사각형은 더욱 제한된 기능을 가져서 간단한 퍼블릭 인터페이스를 갖는다.
이 모델에 따르면 복잡성은 도형의 깊이로 나타내는데, 깊이가 깊을수록 복잡성이 높다.
예를 들어
int AddTwoNumbers(int a, int b)
{
return a + b;
}
이것은 극단적인 얕은 모듈의 예다.
이 같은 모듈은 수많은 ‘유동적인 부분’을 만들어 내고, 결국 시스템 전체에 우발적 복잡성을 발생시킨다.
깊은 모듈로서의 마이크로서비스
깊은 모듈의 개념은 마이크로서비스 패턴과 다르다.
그림 14-3에 나타낸 단일 비즈니스 메서드를 구현하는 서비스는 얕은 모듈이다.
시스템의 복잡성 관점에서 보면 깊은 모듈은 시스템의 글로벌 복잡성을 줄여주는 반면, 얕은 모듈은 글로벌 복잡성을 증가시킨다.
시스템을 마이크로서비스로 분해할 때 임계치는 마이크로서비스를 시스템의 일부로 사용하려고하는 유스케이스에 의해 정의된다.
모놀리식 시스템을 서비스로 분리하면 변경에 드는 비용은 감소한다.
그러나 임계치를 지나서 계속 분해하면 깊은 서비스가 점점 얇은 서비스로 변한다.

[도메인 주도 설계와 마이크로서비스의 경계]
마이크로서비스와 마찬가지로, 앞 장에서 논의한 도메인 주도 설계 패턴의 대부분은 경계에 관한 것이다.
- 바운디드 컨텍스트: 모델의 경계
- 하위 도메인: 비즈니스 역량의 경계
- 에그리게이트와 오브젝트 벨류: 트랜잭션의 경계
바운디드 컨텍스트
마이크로서비스와 바운디드 컨텍스트는 공통점이 많다.
마이크로서비스와 바운디드 컨텍스트 모두 물리적 경계다.
사실상 마이크로서비스가 바운디드 컨텍스트다. (마이크로서비스 → 바운디드 컨텍스트)
그렇다면 반대 관계도 성립할까?

3장에서 배웠듯이, 바운디드 컨텍스트는 유비쿼터스 언어와 모델의 일관성을 보호한다.
만약 시스템에 리드 외에는 충돌하는 모델이 없다고 가정하자.
하위 도메인에 모델 충돌이 없다면 그림 14-9에 나타낸 모든 분해는 모두 완전하게 유효한 바운디드 컨텍스트다.

다양한 요구사항은 바운디드 컨텍스트를 여러 형태로 분해하게 만든다.
예를 들어 팀의 크기, 구조, 수명주기 의존성 등이 그런 요구사항이다.
바운디드 컨텍스트는 유효한 거대한 모놀리식의 경계를 표현한다.
이는 유비쿼터스 언어 또는 비즈니스 도메인 모델의 일관성을 보호하는 유효한 설계 옵션이다.
그림 14-10에 바운디드 컨텍스트와 마이크로서비스의 상관관계를 시각화했다.

애그리게이트
바운디드 컨텍스트는 가장 넓은 유효한 경계를 설정하지만, 애그리게이트는 그 반대다.
애그리게이트의 경계는 가능한 좁게 설정한다.
바운디드 컨텍스트와 마찬가지로, 애그리게이트의 경계 또한 마이크로서비스의 경계를 결정한다고 여겨질 때가 많다.
애그리게이트는 내부 비즈니스 규칙과 불변성, 로직의 복잡성을 감싸는 개별적인 비즈니스 단위다.
따라서 (이번 장의 초반부에 봤듯이) 마이크로서비스와는 다르다.
애그리게이트와 자신의 하위 도메인에 있는 다른 비즈니스 엔티티와 관계가 강할수록 얕은 개별 서비스가 된다.
하위 도메인
마이크로서비스를 설계하는데 좀 더 균형 잡힌 휴리스틱은 비즈니스 하위 도메인의 경계와 서비스를 일치시키는 것이다.
비즈니스 도메인은 하위 도메인의 역량이 어떻게 구현되는지(기술)보다 비즈니스 역량을 설명한다.
기술적 관점에서 보면 하위 도메인은 응집된 유스케이스의 집합이다.
그림 14-11처럼 유스케이스 중 하나에서 비즈니스 요구사항을 변경하면 다른 유스케이스도 영향을 받을 가능성이 높다.

하위 도메인의 크기와 ‘어떻게’보다 ‘무엇을’에 중점을 둔 기능이 하위 도메인을 자연스럽게 깊은 모듈로 만든다.
하위 도메인을 설명하는 ‘기능’은 더 복잡한 구현 상세인 ‘로직’을 캡슐화한다.
유스케이스를 작은 케이스로 쪼개면 모듈은 더욱 얕아진다.
이러한 것들이 마이크로서비스 설계를 위한 안전한 경계로 만든다.
하위 도메인을 마이크로서비스로 만드는 것은 대부분의 마이크로서비스를 위한 최적의 솔루션을 만드는 안전한 휴리스틱이다.
[마이크로서비스의 퍼블릭 인터페이스 압축하기]
도메인 주도 설계는 서비스의 경계를 찾는데 쓰일 뿐만 아니라, 서비스를 깊게 만드는데도 도움을 준다.
오픈 호스트 서비스
그림 14-12에서처럼 오픈 호스트 서비스는 비즈니스 도메인의 바운디드 컨텍스트 모델을 시스템의 다른 구성요소와 연동하는데 사용되는 모델과 분리해준다.
연동 지향 모델인 공표된 언어를 도입하면 시스템의 글로벌 복잡성이 줄어든다.
또한 공표된 언어는 좀 더 제한된 모델을 노출한다.
이는 연동에 필요한 요구사항에 맞게 설계된다.
동일한 구현(로직)에 대한 더 간단한 퍼블릭 인터페이스(기능)을 갖게 되면 서비스가 더 ‘깊이’ 있어지고, 더 효과적인 마이크로서비스 설계에 기여하게 된다.

충돌 방지 계층
충돌 방지 계층(ACL) 패턴은 다른 방식으로도 작동한다.
이것은 서비스를 다른 바운디드 컨텍스트와 연동할 때 복잡성을 줄여준다.
그림 14-13의 ACL 서비스는 바운디드 컨텍스트를 사용하는 로컬 복잡성과 시스템의 글로벌 복잡성을 모두 줄여준다.

ACL 서비스 덕분에 바운디드 컨텍스트를 사용할 때의 비즈니스 복잡성과 연동할 때의 복잡성이 분리된다.
[결론]
모든 마이크로 서비스는 바운디드 컨텍스트다.
그러나 모든 바운디드 컨텍스트는 마이크로서비스인 것은 아니다.
Ch15. 이벤트 주도 아키텍처
마이크로서비스와 마찬가지로 이벤트 주도 아키텍처(EDA)는 현대 분산 시스템 어디에서나 찾아볼 수 있다.
많은 사람이 느슨하게 결합하고, 확장 가능하며, 내결함성을 가진 분산 시스템을 설계할 떄 이벤트 주도 커뮤니케이션을 기본 통합 메커니즘으로 사용할 것을 권고한다.
이벤트 주도 아키텍처는 종종 도메인 주도 설계와 연결된다.
결국 EDA는 이벤트를 기반으로 하고, 이벤트는 DDD에서 두드러진다.
[이벤트 주도 아키텍처]
이벤트 주도 아키텍처는 시스템 컴포넌트가 이벤트 메시지를 교환하면서 비동기적으로 서로 커뮤니케이션하는 아키텍처 스타일이다.

해당 컴포넌트는 시스템에서 발생한 이벤트를 구독하고 그에 따라 반응할 수 있다.
사가 패턴이 그 대표적인 예다.
이벤트 주도 아키텍처와 이벤트 소싱 모두 이벤트를 기반으로 하지만, 개념이 다르다.
- EDA: 서비스간 통신
- 이벤트 소싱: 서비스 내부간 통신, 즉 서비스에서 구현된 상태 전환을 위한 것
[이벤트]
EDA 시스템에서 이벤트 교환은 구성요소를 연동해서 시스템으로 만들기 위한 핵심 통신 메커니즘이다.
이벤트, 커멘드, 메시지
메시지에는 두가지 유형이 있다.
- 이벤트(Event): 이미 발생한 변화를 설명하는 메시지
- 커맨드(Command): 수행돼야 할 작업을 설명하는 메시지
즉, 이벤트는 이미 일어난 일이고, 커맨드는 어떤 일을 하라는 지시다.
이벤트는 이미 발생한 일이므로 이벤트 이름은 과거 시제로 표현해야 한다.(예: DeliveryScheduled, ShipmentCompleted, DeliveryConfirmed)
구조
이벤트는 선택한 메시징 플랫폼을 사용하여 직렬화하고 전송할 수 있는 데이터 레코드다.
{
"type": "delivery-confirmed",
"event-id": "123123-sdsad-1241asdf-14asdfd",
"correlation-id": "1zxfg03-dfzdf231-jyrtet23",
"delivery-id": "0124124-fdef-124as-sea1-56saqww4",
"timestamp": 12347332,
"payload": {
"confirmed-by": "124dfs-bbbb1-064d-f124ef0ea",
"delivery-time": 1246431
}
}
이벤트의 페이로드(payload)는 이벤트가 전달하는 정보를 설명할 뿐만 아니라 이벤트의 유형도 정의한다.
이벤트 유형
이벤트는 이벤트 알림, 이벤트를 통한 상태 전송, 도메인 이벤트의 세가지 유형 중 하나로 분류 된다.
이벤트 알림
이벤트 알림은 다른 컴포넌트가 반응할 비즈니스 도메인의 변경에 관한 메시지다.
이해 당사자에게 이벤트를 알리는 것이 목적이므로 이벤트에 모든 정보를 포함해서는 안된다.
{
"type": "delivery-generated",
"event-id": "123123-sdsad-1241asdf-14asdfd",
"delivery-id": "0124124-fdef-124as-sea1-56saqww4",
"timestamp": 12347332,
"payload": {
"employee-id": "451242",
"link": "/paychecks/451242/2021/01"
}
}
위의 예제를 보면 급여와 관련된 모든 정보를 담고 있지 않다.

어떤 의미에서 이벤트 알림 메시지를 통한 통합은 미국의 긴급 재난 문자(WEA) 시스템 및 유럽의 EU-Alert와 유사하다.
이 짧은 메시지는 긴급 상황을 알리기에는 충분하지만, 자세한 내용을 보려면 다른 정보 소스를 적극적으로 사용해야 한다.

또한 다음과 같은 시나리오에서 이벤트 알림은 선호된다.
- 보안: 민감한 정보가 메시징 인프랑를 통해 공유되는 것을 막고, 데이터 접근을 위한 추가 권한이 필요하다.
- 동시성: 정보의 특성이 경쟁조건에 민감한 경우 명시적으로 질의하면 최신 상태를 얻는다.
이벤트를 통한 상태 전송
이벤트를 통한 상태 전송(ECST) 메시지는 구독자에게 제공자의 내부 상태에 대한 변경사항을 알려준다.
ECST 메시지는 두가지 형태로 나타날 수 있다.
{
"type": "customer-updated",
"event-id": "123123-sdsad-1241asdf-14asdfd",
"customer-id": "1zxfg03-dfzdf231-jyrtet23",
"timestamp": 12347332,
"payload": {
"first-name": "Carolyn",
"last-name": "Hayes",
"phone": 555-1232,
"status": "follow-up-set",
"birthday": "1992/10/14",
"version": 6
}
}
규모가 큰 자료구조를 사용하는 경우 실제로 수정된 필드만 ECST 메시지에 포함시켜야 합리적이다.
{
"type": "customer-updated",
"event-id": "123123-sdsad-1241asdf-14asdfd",
"customer-id": "1zxfg03-dfzdf231-jyrtet23",
"timestamp": 12347332,
"payload": {
"status": "follow-up-set",
"follow-up-date": "2020/03/11",
"version": 9
}
}
개념적으로 이벤트를 통한 상태 전송 메시지는 비동기 데이터 복제 메커니즘이다.
이 접근 방식을 사용하면 시스템의 내결함성이 향상되므로 제공자 서비스가 가용하지 않은 경우라도 사용자는 계속해서 작업할 수 있다.
또한 여러 소스의 데이터를 처리해야 하는 컴포넌트의 성능도 향상시킬 수 있다.

도메인 이벤트
도메인 이벤트는 이벤트 알림과 ECST 메시지 사이 어딘가에 있다.
둘 다 비즈니스 도메인에서 중요한 이벤트를 설명하고, 관련된 모든 데이터를 포함한다.
도메인 이벤트와 이벤트 알림의 관계
도메인 이벤트와 이벤트 알림은 모두 제공자의 비즈니스 도메인에서 발생한 변경사항을 설명한다.
다음과 같은 차이점이 있는데
- 도메인 이벤트에는 이벤트를 설명하는 모든 정보를 포함한다.
- 모델링 의도가 다르다.
- 이벤트 알림은 다른 컴포넌트와의 연동을 돕기 위해 설계
- 도메인 이벤트는 비즈니스 도메인을 모델링하고 설명하기 위한 것
도메인 이벤트와 이벤트를 통한 상태 전송의 관계
도메인 이벤트에 포함된 데이터는 일반적인 ECST 메시지의 스키마와 개념적으로 다르다.
ECST 메시지는 제공자 데이터를 로컬 캐시로 보유하기에 충분한 정보를 제공한다.
모든 도메인 이벤트는 이러한 풍부한 모델을 노출해서는 안 된다.
이벤트 유형: 예제
다음 예제를 살펴보자.
eventNotification = {
"type": "marriage-recorded",
"person-id": "01b9a751",
"payload": {
"person-id": "123as32",
"details": "/01b9a751/marriage-data"
}
};
ecst = {
"type": "personal-details-changed",
"person-id": "01b9a751",
"payload": {
"new-last-name": "Williams"
}
};
domainEvent = {
"type": "married",
"person-id": "01b9a751",
"payload": {
"person-id": "01b9a751",
"assumed-partner-last-name": true
}
};
marriage-recorded는 이벤트 알림 메시지다.
지정된 아이디를 가진 사람이 결혼했다는 사실 외에는 어떠한 정보도 포함되어 있지 않다.
personal-details-changed는 이벤트를 통한 상태 전송 메시지다.
그 사람의 개인정보 변경사항, 즉 성이 변경되었음을 보여준다.
married는 도메인 이벤트다.
그 사람의 ID와 그 사람이 배우자의 이름을 따르는지를 나타내는 플래그를 포함한다.
[이벤트 주도 연동 설계]
3장에서 논의한 바와 같이 소프트웨어 설계는 주로 경계에 관한 것이다.
경계는
- 내부에 속한 것
- 외부에 남아있는 것
- 경계를 넘어서는 것
즉, 컴포넌트가 서로 연동되는 방식을 정의한다.
EDA 기반 시스템에서 이벤트는 컴포넌트가 연동하는 방식과 컴포넌트의 경계 자체에 모두 영향을 주는 가장 중요한 설계 요소다.
다양한 이벤트 유형을 적용하기 위한 휴리스틱을 학습하기 전에 어떻게 이벤트를 사용하면 강한 결합을 가진 커다란 진흙 덩어리를 설계할 수 있는지 알아보자.
분산된 커다란 진흙 덩어리
그림 15-5에 있는 시스템을 살펴보자.
CRM 바운디드 컨텍스트는 소싱 도메인 모델을 적용하여 구현된다.
광고최작화(AdsOptimization) 바운디드 컨텍스트를 도입했을 때, CRM 바운디드 컨텍스트에서 생성된 정보도 처리해야 한다.
따라서 다시 한 번 광고최적화가 CRM에서 생성된 모든 도메인 이벤트를 구독하고 광고최적화의 요구사항에 맞는 모델을 프로젝션하기로 결정했다.

리포팅 바운디드 컨텍스트는 CRM에서 발행한 도메인 이벤트의 일부만 구독했고, 광고최적화 컨텍스트에서 수행한 계산도 가져오기 위해 이벤트 알림 메시지를 사용했다.
그러나 광고최적화에서 동일한 이벤트를 사용해서 계산을 시작하므로 리포팅 모델에서 구독을 지연시키기로 했다.
이 설계는 끔찍한 설계가 되었다.
시간 결합
광고최적화의 리포팅 바운디드 컨텍스트는 시간적으로 결합하여 엄격한 실행 순서에 따라 달라진다.
따라서 순서가 역전되면 리포팅 시스템에서 일관성 없는 데이터가 생성된다.
기능 결합
마케팅과 광고최적화 바운디드 컨텍스트는 모두 CRM의 도메인 이벤트를 구독하고 결국 동일한 고객 데이터를 프로젝션하도록 구현했다.
따라서 컴포넌트 중 하나에서 프로젝션이 변경된 경우 두 번째 바운디드 컨텍스트에서 변경사항을 복제해야 했다.
구현 결합
마케팅과 광고최적화 바운디드 컨텍스트는 CRM의 이벤트 소싱 모델로 생성된 모든 도메인 이벤트를 구독한다.
결과적으로 새 도메인 이벤트를 추가하거나, 기존 이벤트의 스키마를 변경하면 이것을 구독하는 양쪽 바운디드 컨텍스트에 모두 반영해야 한다!
이벤트 주도 연동의 리팩토링
이제 이벤트를 조정하여 설계를 적극적으로 개선하는 방법을 살펴보자.
구현 결합과 기능 결합은 모두 제공자(CRM 바운디드 컨텍스트)에서 프로젝션 로직을 캡슐화하여 결합도를 낮출 수 있다.
CRM에서는 사용자 주도 컨트랙트 패턴을 따를 수 있다.
즉, 사용자가 필요로 하는 모델을 프로젝션하고 이를 바운디드 컨텍스트의 공표된 언어의 일부로 만든다.
결과적으로 사용자는 필요한 모든 데이터를 얻을 뿐 CRM의 구현 모델을 알지 못한다.
광고최적화와 리포팅 바운디드 컨텍스트 간의 시간 결합을 처리하기 위해서는 광고최적화 컴포넌트가 이벤트 알림 메시지를 발행하여 리포팅 컴포넌트가 필요한 데이터를 가져오도록 트리거할 수 있다.

이벤트 주도 설계 휴리스틱
위의 변경에 숨어 있는 설계에 대한 휴리스틱을 정리해보자.
최악의 상황을 가정하라.
다음을 한번 생각해보자.
- 네트워크가 느려질 것이다.
- 가장 어렵거나 중요한 순간에 서버 장애가 발생한다.
- 이벤트가 순서대로 도착하지 않는다.
- 이벤트가 중복된다.
이벤트 주도 아키텍처에서 ‘주도(driven)’라는 단어는 전체 시스템이 메시지의 성공적인 전달에 달려 있음을 의미한다.
따라서 다음과 같은 방법으로 무조건 이벤트가 항상 일관되게 전달되도록 한다.
- 메시지를 안정적으로 발송하기 위해 아웃박스 패턴을 사용하자.
- 메시지를 발송할 때 구독자가 메시지 중복을 제거하고, 순서가 잘못된 메시지를 식별하고 재정렬할 수 있게 하라.
- 보상 조치를 실행해야 하는 교차 바운디드 컨텍스트 프로세스를 조율할 때 사가 패턴과 프로세스 관리자 패턴을 이용하라.
퍼블릭 이벤트와 프라이빗 이벤트를 사용하라.
이벤트 소싱 애그리게이트에서 도메인 이벤트를 발송할 때 세부 정보를 노출하지 않도록 주의하라.
이벤트를 바운디드 컨텍스트의 퍼블릭 인터페이스의 내재된 부분으로 취급하라.
바운디드 컨텍스트의 퍼블릭 인터페이스를 설계할 때 다른 유형의 이벤트를 활용하라.
이벤트를 통한 상태 전송 메시지는 필요한 정보만 전달하는 더욱 간결한 모델을 쓰자.
마지막으로 외부 바운디드 컨텍스트와 통신할 때는 도메인 이벤트를 최소화하여 사용하자.
일관성 요구사항을 평가하라.
이벤트 주도 커뮤니케이션을 설계할 때 이벤트 유형 선택을 위한 또 다른 휴리스틱을 적용해보자.
- 컴포넌트가 궁극적으로 일관된 데이터를 처리할 수 있는 경우, 이벤트를 통한 상태 전송 메시지를 사용한다.
- 사용자가 제공자의 마지막으로 변경된 상태를 읽어야 하는 경우, 제공자의 최신 정보를 가져오는 후속 질의와 함께 이벤트 알림 메시지를 발행한다.
[결론]
이번 장에서는 세 가지 유형의 이벤트에 대해서 배웠다.
이벤트 알림
중요한 일이 발생했지만, 사용자가 제공자에게 추가 정보를 명시적으로 질의해야 하는 알림
이벤트를 통한 상태 전송
메시지 기반 데이터 복제 메커니즘.
각 이벤트에는 제공자 데이터의 로컬 캐시를 유지 관리하는데 사용할 수 있는 스냅샷이 포함될 수도 있다.
도메인 이벤트
제공자의 비즈니스 도메인 내에서 발생하는 이벤트를 설명하는 메시지
✅ Chapter 14~15 요약 섹션
핵심 포인트 (Key Takeaways)
- 마이크로서비스 분리는 단순히 “쪼개기”가 아니라, 변경 비용/복잡도 관점에서 임계치가 존재한다.
- ‘깊은 모듈(Deep module)’ 관점에서, 얕은 서비스(기능 1개짜리 서비스)는 시스템 전체 복잡도를 오히려 증가시킬 수 있다.
- 결국 “얼마나 작게 나눴는가”가 아니라, 서비스 경계가 유스케이스를 기준으로 충분히 깊은가가 핵심이다.
실무 연결 포인트 (How to apply)
- 마이크로서비스 도입 논의에서 “분리 자체”가 목표가 되면 위험하다.
변경 비용 감소 vs 연동 복잡도 증가의 균형을 계산해야 한다. - 서비스는 “작은 기능”이 아니라 “하나의 책임 있는 유스케이스 묶음”으로 만들어야 운영 가능하다.
- 팀이 분리/확장을 고민할 때 “깊은 서비스” 개념은 설득력 있는 판단 기준이 된다.
한 줄 인사이트
마이크로서비스의 본질은 분리가 아니라, 글로벌 복잡도를 줄이는 경계 선택이다.
'Book Notes' 카테고리의 다른 글
| [개발서적] 도메인 주도 설계 첫걸음 4부(ch16) 요약 (2) (1) | 2026.01.14 |
|---|---|
| [개발서적] 도메인 주도 설계 첫걸음 3부(ch12~13) 요약 (2) (0) | 2026.01.11 |
| [개발서적] 도메인 주도 설계 첫걸음 3부(ch10~11) 요약 (1) (0) | 2026.01.11 |
| [개발서적] 도메인 주도 설계 첫걸음 2부(ch7~9) 요약 (2) (0) | 2026.01.11 |
| [개발서적] 도메인 주도 설계 첫걸음 2부(ch5~6) 요약 (1) (0) | 2026.01.11 |