Bounded Context
정의
Bounded Context는 특정 모델이 정의되고 적용되는 경계에 대한 기술이다. 보통 하나의 하위 시스템이거나 특정 팀의 작업 범위에 대응한다.
Fowler는 이를 “DDD 전략적 설계의 중심 패턴"이라고 부른다. DDD가 큰 모델을 다루는 방식은 그것을 여러 Bounded Context로 나누고, 그 사이의 관계를 명시적으로 밝히는 것이다.
동작 원리
큰 프로젝트에는 항상 여러 모델이 존재한다
Evans가 제시하는 근거는 세 가지다.
- 두 하위 시스템이 서로 다른 사용자 집단을 대상으로 하고, 하는 일이 달라서 서로 다른 모델이 유용한 경우가 흔하다
- 독립적으로 일하는 팀들은 소통 부족으로 같은 문제를 다른 방식으로 해결한다
- 도구 자체가 달라서 코드를 공유할 수 없는 경우도 있다
여러 모델이 존재하는 것은 불가피하다. 문제는 서로 다른 모델에 기반한 코드가 합쳐질 때 생긴다. 소프트웨어에 버그가 생기고, 신뢰할 수 없게 되고, 이해하기 어려워진다. 팀원 간 소통도 혼란스러워진다. 어떤 컨텍스트에서 모델을 적용하면 안 되는지가 불분명해지기 때문이다.
모델의 표현은 다른 모든 어구와 마찬가지로 컨텍스트 안에서만 의미를 가진다.
다의어(polyseme)가 경계를 드러낸다
Fowler가 드는 사례가 이 문제를 잘 보여준다. 전력회사에서 “meter"라는 단어는 부서마다 다른 것을 가리켰다.
- 전력망과 특정 위치의 연결
- 전력망과 고객의 연결
- 고장 나면 교체되는 물리적 계량기 자체
대화에서는 이런 미묘한 차이를 얼버무리고 넘어갈 수 있지만 컴퓨터의 정밀한 세계에서는 불가능하다. “Customer”, “Product” 같은 흔한 단어에서 같은 혼란이 반복해서 발생한다.
같은 단어가 컨텍스트마다 다른 뜻인 것은 문제가 아니라 정상이다. Bounded Context는 그 사실을 인정하고 각 컨텍스트마다 일관된 모델을 갖게 하는 장치다.
Bounded Context는 서로 무관한 개념(고객 지원 컨텍스트에만 존재하는 지원 티켓 같은 것)도 갖지만, 공유되는 개념(제품, 고객)도 갖는다. 서로 다른 컨텍스트가 공통 개념에 대해 완전히 다른 모델을 가질 수 있고, 통합을 위해서는 이 다의적 개념들 사이를 매핑하는 장치가 필요하다.
경계를 긋는 요인
컨텍스트 사이에 경계를 긋는 요인은 여러 가지지만, 보통 가장 지배적인 것은 인간의 문화다. 모델이 보편 언어로 작동하는 이상, 언어가 달라지는 지점에서 모델도 달라져야 한다.
같은 도메인 컨텍스트 안에서도 여러 컨텍스트가 나타날 수 있다. 예를 들어 하나의 애플리케이션 안에서 인메모리 모델과 관계형 DB 모델이 분리되는 것이 그렇다. 이 경계는 모델을 표현하는 방식의 차이에서 온다.
적용
Evans의 처방은 다음과 같다.
모델이 적용되는 컨텍스트를 명시적으로 정의하라. 팀 조직, 애플리케이션의 특정 부분에서의 사용, 그리고 코드베이스와 데이터베이스 스키마 같은 물리적 표현의 관점에서 경계를 명시적으로 설정하라.
이 경계 안에서 모델 개념과 용어가 엄격히 일관되도록 Continuous Integration을 적용하되, 경계 밖의 문제에 정신이 팔리거나 혼란스러워하지 마라. 컨텍스트 안에서 단일한 개발 프로세스를 표준화하라. 그 프로세스가 다른 곳에서까지 쓰일 필요는 없다.
여기서 놓치기 쉬운 대목이 “코드베이스와 데이터베이스 스키마 같은 물리적 표현” 이다. Bounded Context는 개념적 선언에 그치지 않고 물리적 산출물의 경계로 나타나야 한다. 마이크로서비스 아키텍처에서 각 Bounded Context가 서비스 후보가 되는 이유가 여기에 있다.
경계를 유지하기 - Continuous Integration
컨텍스트를 정의한 뒤에는 그것을 건강하게 유지해야 한다. 여러 사람이 같은 Bounded Context 안에서 작업하면 모델이 파편화되는 강한 경향이 나타난다. 팀이 클수록 문제도 커지지만, 서너 명만 되어도 심각한 문제가 발생할 수 있다.
그렇다고 시스템을 점점 더 작은 컨텍스트로 쪼개면 통합과 응집이라는 가치 있는 수준을 잃게 된다. Evans의 처방은 모든 코드와 구현 산출물을 자주 병합하고, 파편화를 빠르게 잡아내는 자동화된 테스트를 두는 것이다.
마이크로서비스에서의 크기 기준
Microsoft의 아키텍처 가이드는 이 경계를 서비스 크기와 연결해 다음 원칙을 제시한다.
마이크로서비스는 하나의 Aggregate보다 작지 않게, 하나의 Bounded Context보다 크지 않게 설계한다.
즉 Bounded Context가 서비스 크기의 상한이고 Aggregate가 하한이다.
예제
공연 티켓 예매 시스템에서 “좌석"이라는 단어는 두 컨텍스트에서 다른 것을 가리킨다.
| 컨텍스트 | “좌석"의 의미 | 성격 |
|---|---|---|
| 공연/공연장 관리 | 공연장의 물리적 위치 (A구역 3열 12번) | 거의 변하지 않는 카탈로그 데이터, 읽기 전용 |
| 예약 | 이번 회차에 팔리는 상품 1개 | 상태를 가지고 동시성 경합의 대상이 됨 |
두 개념을 “둘 다 좌석이니까” 하나의 테이블과 하나의 클래스로 합치는 것이 모델을 통합해버리는 전형적인 실수다. 수명도, 읽기/쓰기 패턴도, 변경 이유도 다르다.
소유권 판단에는 다음 기준이 유용하다.
한 컨텍스트가 자기 데이터의 불변식을 스스로 지킬 수 없다면 그것은 그 컨텍스트의 데이터가 아니다.
판매 좌석의 상태 규칙(선점 가능 여부, 만료 처리)을 아는 쪽은 예약 컨텍스트이므로, 판매 좌석은 예약 컨텍스트가 소유한다.
경계가 틀렸다는 신호
- 두 모듈을 항상 함께 수정하게 된다
- 하나의 유스케이스가 세 모듈을 왔다 갔다 한다
- 같은 개념을 두 모듈이 서로 다른 방향으로 계속 확장한다
Bounded Context는 도메인을 이해할수록 바뀐다. Verraes와 Wirfs-Brock은 컨텍스트를 나누는 이유가 도메인 개념 못지않게 조직의 역사와 인간관계인 경우가 있다는 점도 지적한다.
관련 개념
- Ubiquitous Language - Bounded Context 안에서 통용되는 언어. 언어가 바뀌는 지점이 곧 경계다
- Context Map - 정의된 컨텍스트들 사이의 관계를 그리는 방법
- Aggregate - 컨텍스트 안에서 트랜잭션 일관성 경계를 정하는 패턴
- DDD 개요 - 전략적 설계와 전술적 설계의 전체 지도
참고 자료
- Eric Evans, Domain-Driven Design Reference (2015), “Bounded Context”, “Continuous Integration”
- Martin Fowler, Bounded Context
- Mathias Verraes, Rebecca Wirfs-Brock, Splitting a Domain Across Bounded Contexts
- Microsoft, Tactical DDD