본문으로 건너뛰기
Leo Jung
뒤로 가기

[DDD Intro #25] Microservice와 DDD: Bounded Context는 서비스인가

페이지 수정

DDD를 이야기하면 마이크로서비스가 함께 등장하는 경우가 많다. Bounded Context를 나누고, 각 컨텍스트를 서비스로 만들고, 독립 배포와 독립 데이터베이스를 갖추는 그림은 매력적이다.

하지만 Bounded Context와 Microservice는 같은 개념이 아니다. 둘은 관련이 있지만 출발점이 다르다.

Bounded Context는 모델의 경계다. 어떤 언어와 모델이 어디까지 일관되는지 정하는 경계다. Microservice는 배포의 경계다. 독립적으로 배포되고 운영될 수 있는 실행 단위다.

둘이 잘 맞을 때도 있지만, 항상 일대일로 대응해야 하는 것은 아니다.

모델의 경계와 배포의 경계

구독 서비스에서 다음과 같은 Bounded Context가 있다고 해보자.

회원 컨텍스트
구독 컨텍스트
결제 컨텍스트
권한 컨텍스트
알림 컨텍스트
정산 컨텍스트

이 경계는 모델의 언어를 기준으로 나눈 것이다. 회원 컨텍스트의 Member와 구독 컨텍스트의 Subscriber는 다를 수 있다. 결제 컨텍스트의 Payment와 정산 컨텍스트의 SettlementItem도 다른 모델이다.

이 컨텍스트들을 각각 마이크로서비스로 만들 수 있다.

member-service
subscription-service
payment-service
authorization-service
notification-service
settlement-service

하지만 반드시 그래야 하는 것은 아니다. 같은 애플리케이션 안에서 모듈로 나눌 수도 있다. 하나의 배포 단위 안에 여러 Bounded Context가 존재할 수 있다.

중요한 것은 서비스로 나누기 전에 모델의 경계를 이해하는 것이다.

서비스를 나눈다고 DDD가 되는 것은 아니다

DDD를 도입한다며 먼저 서비스를 나누는 팀이 있다. 주문 서비스, 결제 서비스, 배송 서비스, 회원 서비스로 분리한다. 하지만 내부를 보면 각 서비스가 여전히 같은 데이터 모델을 공유하거나, 서로의 DB를 직접 조회하거나, 공통 라이브러리의 거대한 Entity를 함께 사용한다.

이 경우 배포 단위는 나뉘었지만 모델 경계는 나뉘지 않았다.

더 나쁜 경우도 있다. 서비스는 나뉘었지만 하나의 유스케이스를 처리하기 위해 여러 서비스를 순차적으로 동기 호출해야 한다. 한 서비스의 작은 변경이 여러 서비스에 영향을 준다. 장애도 연쇄적으로 전파된다.

이것은 마이크로서비스가 아니라 분산 모놀리스에 가깝다.

배포는 분산되어 있는데,
변경은 함께 해야 하고,
데이터는 강하게 묶여 있으며,
장애는 서로 전파된다.

서비스를 나누는 것만으로 DDD를 했다고 볼 수 없다. DDD의 핵심은 모델의 언어와 경계를 명확히 하는 것이다.

Bounded Context는 좋은 서비스 후보가 될 수 있다

그렇다고 Bounded Context와 Microservice가 무관한 것은 아니다. 오히려 Bounded Context는 마이크로서비스 경계를 찾는 좋은 출발점이 될 수 있다.

마이크로서비스는 독립적으로 변경되고 배포될 수 있어야 한다. 그러려면 내부 모델이 독립적이어야 한다. Bounded Context는 바로 그 모델의 독립성을 설명한다.

구독 컨텍스트와 알림 컨텍스트를 생각해보자. 구독 컨텍스트는 구독 상태와 갱신 정책이 핵심이다. 알림 컨텍스트는 어떤 이벤트에 어떤 채널로 어떤 메시지를 보낼지 관심이 있다.

두 컨텍스트는 연결되어야 하지만 같은 모델을 공유할 필요는 없다. 구독이 갱신되면 SubscriptionRenewed 이벤트를 발행하고, 알림 컨텍스트는 이를 받아 알림을 보낼 수 있다.

이 정도로 독립성이 있다면 두 컨텍스트를 별도 서비스로 분리할 후보가 될 수 있다.

일대일 대응이 항상 좋은 것은 아니다

하나의 Bounded Context가 하나의 Microservice가 되는 경우도 있다. 하지만 모든 상황에서 그런 대응이 최선은 아니다.

작은 팀에서는 여러 컨텍스트를 하나의 모놀리식 애플리케이션 안에 모듈로 두는 편이 나을 수 있다. 배포와 운영 복잡도를 줄이면서도 모델 경계는 지킬 수 있기 때문이다.

반대로 하나의 큰 Bounded Context가 내부적으로 여러 배포 단위로 나뉠 수도 있다. 성능이나 확장성, 운영상의 이유로 같은 모델 경계 안에서도 여러 서비스를 둘 수 있다.

따라서 질문은 “Bounded Context마다 서비스를 만들까?”가 아니다.

이 컨텍스트는 독립적으로 변경될 수 있는가?
다른 컨텍스트와 데이터 일관성 요구가 얼마나 강한가?
팀이 독립적으로 소유하고 운영할 수 있는가?
네트워크 호출과 배포 복잡도를 감당할 수 있는가?
모듈 경계만으로 충분한가?

이 질문에 답한 뒤 서비스 분리를 결정해야 한다.

마이크로서비스 전에 모듈 경계부터

많은 팀에게 더 현실적인 출발점은 모듈러 모놀리스다. 하나의 애플리케이션 안에서 Bounded Context를 모듈로 나누고, 모듈 간 의존성을 통제하는 방식이다.

subscription module
payment module
authorization module
notification module

각 모듈은 자신의 모델과 언어를 갖고, 다른 모듈의 내부 모델을 직접 사용하지 않는다. 필요한 상호작용은 명시적인 인터페이스나 이벤트를 통해 한다.

이 구조는 마이크로서비스보다 운영이 단순하다. 동시에 모델 경계를 연습할 수 있다. 나중에 특정 모듈을 독립 서비스로 분리할 필요가 생기면, 이미 경계가 정리되어 있어 더 쉽게 분리할 수 있다.

서비스부터 나누는 것보다 모델과 모듈부터 나누는 것이 안전한 경우가 많다.

데이터베이스 분리는 신중해야 한다

마이크로서비스에서는 서비스마다 데이터베이스를 독립적으로 갖는 것이 이상적이라고 말한다. 이 원칙은 서비스의 자율성을 위해 중요하다. 하지만 현실에서는 큰 비용을 만든다.

데이터베이스를 나누면 조인이 어려워지고, 트랜잭션이 분산되며, 데이터 동기화가 필요해진다. 조회 모델을 따로 만들거나 이벤트 기반 동기화를 설계해야 한다.

따라서 DB 분리는 모델 경계와 일관성 요구를 충분히 이해한 뒤에 해야 한다. 단순히 “마이크로서비스니까 DB도 나누자”는 접근은 위험하다.

Bounded Context를 나눈다는 것은 서로 다른 모델을 인정한다는 뜻이다. 하지만 배포와 저장소 분리는 그 다음 단계의 운영 결정이다.

마치며

Bounded Context는 모델의 경계다. Microservice는 배포의 경계다. 둘은 자주 함께 이야기되지만 같은 개념은 아니다.

좋은 마이크로서비스 경계는 좋은 모델 경계에서 출발할 수 있다. 하지만 서비스를 나눈다고 자동으로 좋은 모델이 생기지는 않는다. 오히려 모델 경계를 이해하지 못한 채 서비스를 나누면 분산 모놀리스가 된다.

DDD를 적용한다면 먼저 언어와 모델의 경계를 찾아야 한다. 그 경계가 독립적인 변경과 운영의 단위가 될 수 있는지 판단한 뒤, 필요할 때 서비스로 분리하면 된다.

마이크로서비스는 DDD의 목표가 아니다. DDD의 목표는 복잡한 도메인을 이해하고, 그 이해가 코드와 시스템 구조 안에 살아남게 하는 것이다. 서비스 분리는 그 목표를 도울 수도 있고, 방해할 수도 있다.


페이지 수정
공유하기:

이전 글
[DDD Intro #26] 테스트로 도메인 모델 설명하기
다음 글
[DDD Intro #24] Event Sourcing은 DDD의 필수가 아니다