도메인 주도 설계에서의 전략적 설계

Updated:

지난 포스트(도메인 주도 설계란)를 통해 도메인 주도 설계는 비즈니스 상 전략적으로 중요한 것들을 찾아서 이를 나누고, 필요에 따라 분할 또는 통합해서 마이크로서비스를 식별하는 전략적 설계와 전략적 설계를 통해 식별된 마이크로서비스를 정제하고 확정해서 비즈니스의 고유한 활동을 모델링하는 전술적 설계로 구성된다고 설명했습니다.

그럼 이번엔 전략적 설계엑 대해 자세히 알아보겠습니다.

전략적 설계

전략적 설계란 팀 내에서 모두가 공통으로 사용할 유비쿼터스 언어를 정의하고, 이 언어를 활용해서 비즈니스를 분석하고 핵심이 되는 개념을 식별합니다. 이렇게 식별된 개념들을 분석해서 바운디드 컨텍스트를 식별하고, 식별된 바운디드 컨텍스트 간의 매핑 관계를 정의해서 컨텍스트 맵(Context map)을 작성합니다. 최종적으로 바운디드 컨텍스트로의 분할에 따른 효과를 분석해서 서비스의 분할 또는 통합을 검토하여 후보 마이크로서비스를 도출하는 것입니다.

온라인 쇼핑몰 시스템을 개발하는 프로젝트를 예를 들어보겠습니다. 설계가 시작되면 프로젝트 팀에서는 프로젝트의 핵심이 되는 부분을 설계하기 시작합니다. 아래 그림이 가장 핵심이 되는 부분으로 이 핵심 영역은 팀의 역량을 모아 반드시 잘 구축해야 하는 영역으로 구축 이후라도 지속적인 개발을 통해 개선되어야 하는 부분입니다. 프로젝트 팀은 계속해서 도메인을 분석하고 이해하면서 필요한 개념들을 하나씩 하나씩 추가하게 됩니다.

이렇게 계속해서 많은내용이 추가되고 도 추가된 내용을 위해 또 다른 개념들이 계속 추가되게 됩니다. 처음 핵심이 되는 부분의 설계로 조그만하게 시작했던 것이 이제는 하나의 큰 덩어리로 만들어졌습니다.

거대한 진흙 공(Big Ball of Mud)

이 거대한 하나의 덩어리 내부의 모델을 보면, 모델안에 많은 개념들이 포함되어 있고, 모델 내의 언어가 모호해져 있는 것을 확인할 수 있습니다. 이렇게 큰 한 덩어리의 모델은 잘 설계된 것 처럼 보이지만, 기능의 추가나 개선, 테스트와 유지보수를 어렵게 만드는 문제를 갖고 있습니다.

서브 도메인

이런 거대하고 복잡한 개념들 속에서 문제가 되는 영역을 쉽게 이해할 수 있도록 전체 비즈니스 도메인을 논리적으로 구분할 수 있는 개념들로 분리해야 합니다. 즉, 많은 개념들이 하나로 엮여진 복잡한 모델은 전체 비즈니스 도메인을 하나의 논리적인 하위영역으로 분리해야 한다라는 것으로 이렇게 전체의 큰 도메인을 분리한 것을 서브 도메인이라고 합니다.

서브도메인은 화면에서 보시는 것과 같이 핵심 서브도메인, 지원 서브도메인, 그리고 일반 서브도메인의 3가지 유형으로 구분됩니다.

서브 도메인 유형

핵심 서브 도메인 (Core Sub-Domain)

서브도메인의 유형 중 첫 번째인 핵심 서브도메인은 다른 경쟁자와 차별화를 만들 수 있는 비즈니스 영역이기 때문에 기업의 프로젝트 목록에서 높은 우선순위를 가지며, 소프트웨어 개발에 있어서 전략적으로 가장 큰 투자가 필요한 영역입니다.

지원 서브 도메인 (Supporting Sub-Domain)

두 번째, 지원 서브도메인은 어느 정도 비즈니스에 필수적이지만, 핵심은 아닌 부분으로 볼 수 있습니다. 그러나 지원 서브도메인 없이는 핵심 도메인을 성공시킬 수 없기 때문에 핵심 서브도메인 다음으로 중요한 영역입니다.

일반 서브 도메인 (Generic Sub-Domain)

그리고 마지막, 일반 서브도메인은 비즈니스적으로 특화된 부분은 아니지만, 전체 비즈니스 솔루션에 필요한 부분으로 기존 제품의 구매 또는 핵심이나 지원 서브도메인이 할당된 팀에서 직접 구현하여 적용할 수 있습니다.

저는 아마존이라는 쇼핑몰을 자주 이용하는데, 아마존 쇼핑몰의 첫 페이지에 장바구니에 담았던 상품, 그리고 검색했거나 아무 생각없이 클릭 해봤던 상품들과 또 비슷한 상품들을 쫘악~ 화면에 보여주는 걸로 봐서 아마도 아마존은 상품을 추천하는 서비스를 핵심 서브도메인으로 정하고 역량과 리소스를 투입해서 다른 경쟁사의 시스템과 차별화를 뒀다고 생각해 봤습니다. 그리고 국내에서는 쿠팡이라는 쇼핑몰이 쿠팡맨을 통한 일일배송, 심지어는 결제 후 수 시간만에 배송하는 서비스를 통해 다른 경쟁 쇼핑몰과 배송 서비스 영역을 차별화한 것으로 보아, 쿠팡은 아마도 배송서비스를 핵심 서브도메인으로 식별하지 않았나 하는 생각을 해봤습니다.

예전에 OO 프로젝트에서 실제로 프로젝트에서 중요한 영역, 중요하지 않은 영역이 따로 있긴 하지만, 막상 여러 팀에 업무를 할당할 때 어느 팀은 핵심 서브도메인, 어느팀은 지원 서브도메인, 또 어느팀은 일반 서브도메인을 할당해보니, 특히 일반 서브도메인 영역을 할당 받은 팀의 표정이 썩 좋지 않았던 기억이 있습니다. 그래서 그 이후에는 핵심과 지원 서브도메인으로만 구분하고 일반은 있어도 없는 것으로 하고 있습니다. ^^

이렇게 거대한 전체 비즈니스 도메인을 핵심 서브도메인, 지원 서브도메인, 일반 서브도메인으로 분리하면 아래 그림과 같은 형태로 나눠지게 됩니다. 이렇게 서브도메인을 식별하는 과정을 통해 프로젝트에서는 비즈니스의 핵심 영역을 찾을 수 있고 또 각 핵심, 지원, 일반 서브도메인 간의 관계를 이해할 수 있게 됩니다

서브 도메인(Sub-Domain)

비즈니스의 실제 문제를 해결하기 위해 하나의 큰 도메인을 여러개의 서브 도메인으로 구분한 것입니다. 환자를 치료하는 병원 시스템을 개발하는 경우 “병을 치료하는 것”과 “치료를 예약하는 것”의 두가지 문제 영역이 있습니다. “병을 치료하는 것”이 핵심 서브 도메인이고, “치료를 위해 예약하는 것”이 지원 또는 일반 서브 도메인이 됩니다. 핵심 영역에서 의사는 무엇이 문제인지 파악하고, 마취를 하고 치료를 합니다. 지원 또는 일반 영역에서느는 환자의 정보, 예약날짜를 관리합니다. 이렇게 각 서브 도메인에는 내부에 한개 또는 여러개의 맥락(문제 파악, 마취, 치료, 환자 정보, 예약)을 포함하고 있습니다. 이렇게 의미적으로 동일한 맥락을 경계로 구분한 것을 바운디드 컨텍스트(Bounded Context)라고 합니다.

바운디드 컨텍스트 (Bounded Context)

바운디드 컨텍스트란 이 단어의 뜻과 같이 의미적으로 동일한 맥락의 경계, 범위를 표현하는 것, 의미적으로 동일한 맥락의 경계라는 것은 그 범주 내에서 소프트웨어 모델의 각 컴포넌트는 특정한 의미를 갖고 특정한 일을 한다는 것을 의미합니다. 바운디드 컨텍스트는 모델이 구현되는 곳이고, 바운디드 컨텍스트마다 각각 분리된 소프트웨어 산출물이 나오게 됩니다. 이 바운디드 컨텍스트는 유비쿼터스 언어로 표현하는 것이 중요합니다.

바운디드 컨텍스트(Bounded Context)

유비쿼터스 언어(Ubiquitous Language)

앞에서 바운디드 컨텍스트를 유비쿼터스 언어로 표현하는 것이 중요하다고 했는데, 유비쿼터스 언어란 바운디드 컨텍스트를 소유한 팀, 할당받은 팀이 사용하는 공통의 언어로 도메인의 의도와 개념의 명확하고 구체화된 표현으로 정의하는 것이 중요하다는 걸 의미합니다. 이런 언어를 정의해서 사용함으로써 프로젝트 팀은 불명확한 의사소통을 피할 수 있게 됩니다. 이 언어는 팀에 속해있는 모든 이해관계자가 공통으로 사용하는 언어로 의사소통 뿐 아니라 산출물을 작성할때, 그리고 상세한 설계 모델링 할 때나 소스코드를 구현할 때 소스코드의 클래스 이름, 매소드 이름에도 사용되게 됩니다.

유비 쿼터스 언어(Ubiquitous Language)

컨텍스트 매핑 (Context Mapping)

하나의 큰 도메인을 여러개의 바운디드 컨텍스트로 분리를 하게 되면, 하나의 비즈니스를 실행하기 위해 여러개의 바운디드 컨텍스트가 연계되어야 하는 경우가 발생하게 됩니다. 이 컨텍스트 간의 연간관계를 표현한 것을 컨텍스트 매핑이라고 합니다. 즉 바운디드 컨텍스트를 다른 바운디드 컨텍스트와 데이터를 주고 받는 것을 의미합니다. 두 컨텍스트가 서로 연관관계가 있다는 것은 두 컨텍스트 간에 선을 그려서 표현합니다.

컨텍스트 매핑(Context Mapping)

컨텍스트 매핑하는 방법은 아래와 같이 총 7가지가 있습니다.

  • 각자의 길(Separate Ways) : 다소 중복이 발생되더라도 각각의 컨텍스트 내에서 독립적으로 해결하는 방식
  • 공유 커널(Shared kernel) : 서로 다른 바운디드 컨텍스트 사이에 모듈을 공유하는 방식
  • 고객-공급자(Customer-Supplier) : 공급자가 고객이 요구사항에 맞는 정보를 제공해 주는 방식
  • 준수자(Conformist) : 고객이 공급자 제공하는 방식에 맞추는 방식
  • 부패 방지 계층(Anticorruption Layer) : 정보를 요청하는 컨텍스트에서 정보를 제공하는 컨텍스트의 정보가 개념적으로 맞지 않는 경우 중간에서 변환해서 요청한 컨텍스트로 전달하는 방식
  • 오픈 호스트 서비스(Open Host Service) : 다수의 외부 시스템과 통합이 필요한 경우 외부의 접근과 관련된 프로토콜을 공개해서, 연계하려는 시스템에게 프로토콜을 사용할 수 있게 하는 방식
  • 발행된 언어(Published Language) : 컨텍스트에서 제공하는 정보를 사용하는 컨텍스트에게 제공하는 방식

마이크로서비스 식별

이렇게 바운디드 컨텍스트를 식별하고, 컨텍스트 간 연관관계를 찾은 후 컨텍스트 내의 핵심 개념을 기반으로 마이크로서비스를 식별하게 됩니다. 이렇게 비즈니스 관점으로 중요도에 따라 식별된 마이크로서비스는 업무 간 연관관계 분석을 통해 서비스의 분리 또는 통합을 검토하게 됩니다. 또한 각 서비스가 독립적으로 배포되어야 하는지, 변경에서 배포까지의 Lead Time이 최소화 되어야 하는지의 마이크로서비스 적용에 따른 효과를 분석해서 최종 마이크로서비스의 단위를 결정하게 됩니다.

마이크로서비스 식별

마이크로서비스 별 전술적 설계 방향 결정

이렇게 마이크로서비스를 식별한 후에는 식별된 마이크로서비스의 유형을 식별해서 유형별 전술적 설계와 구현의 방식을 선정하게 됩니다. 이 전술적 설계와 구현의 방식은 비즈니스의 복잡성이나 규칙, 마이크로서비스가 속해있는 서브 도메인의 유형을 고려해서 결정하게 됩니다.

아래 그림은 마이크로소프트 홈페이지에서 가져온 “The multi-architectural patterns & Polyglot microservice world” 입니다.

The multi-architectural patterns & Polyglot microservice world

애플리케이션 구현 언어와 설계 패턴, 데이터 베이스 종류까지 Polyglot하게 조합할 수 있는 12가지의 마이크로서비스 유형을 적용할 수 있다는 내용입니다. Microsoft Azure에서는 개발 언어는 ASP.NET, Node.js, Python, Java, GoLang, 설계와 구현 패턴은 Simple CRUD, DDD, DDD & CQRS Pattern, 데이터베이스는 SQL Server, MongoDB, PostgreSQL DB, Oracle DB, MySQL DB 등 애플리케이션의 적합한 언어, 설계/구현 패턴, 데이터베이스를 선택하고 조합해서 사용할 수 있습니다.

우리 회사에서 수행하는 대부분의 프로젝트는 Java를 사용하고, 또 데이터베이스는 프로젝트의 제안단계나 착수단계에서 선정되므로, 개발언어와 데이터베이스를 제외한 설계/구현 패턴을 Simple CRUD Pattern과 DDD Pattern의 2가지 중에서 하나를 선택하도록 정의하고 있습니다.

디자인 패턴유형 선정시 고려할 사항같이 사용할 수 있는 기술
DDD Pattern • 핵심 비즈니스가 지속적으로 변화하는 비즈니스 규칙이 있는 업무
• 비즈니스가 매우 복잡한 업무
• 핵심 서브 도메인
• CQRS(Command and Query Responsibility Segregation)
• Event Sourcing
• EDA (Event-Driven Architecture)
Simple CRUD Pattern • 비즈니스의 복잡성이나 규칙이 적은 간단한 업무
• 데이터 조회성 업무
• 지원/일반 서브도메인
• EDA (Event-Driven Architecture)

이전 글에서 도메인 주도 설계의 장점에 대해서 설명을 해왔습니다. 그렇다고 마이크로서비스의 구현을 항상 도메인 주도 설계 방식으로만 설계해야 한다는 것은 아닙니다.
모든 상황에 적합한 아키텍처 패턴이나 스타일 또는 특정 기술을 하나만 선택할 수 없으므로, 서비스 별로 다양한 디자인 패턴을 기반으로 하는 서로 다른 내부 아키텍처를 가질 수 있다는 것입니다. 따라서, 마이크로서비스의 설계는 도메인 주도 설계 패턴을 기본으로 사용하되, 간단한 업무, 단순 데이터 관리(생성/조회/업데이트/삭제) 형태의 업무는 데이터 중심 설계(Simple CRUD Pattern) 방식 또는 다양한 디자인 패턴을 적용할 수 있습니다.

효과적인 설계에 대한 요구

지금까지 하나의 큰 비즈니스 도메인을 하위의 서브도메인으로 구분하는 것, 또 유비쿼터스 언어를 정의하고, 이 언어를 기반으로 바운디드 컨텍스트와 각 컨텍스트간의 연관관계를 찾은 후 핵심 개념을 기반으로 마이크로서비스를 식별하는 전술적 설계에 대해 설명드렸습니다. 그런데 실제 프로젝트를 수행하다보면, 어느순간 구현해야할 시스템의 큰 그림(Big Picture)을 잊어버리는 경우가 많습니다. 우리의 관리자는 일정과 비용, 인력 등 리소스 관리에 집중하고, 우리 같은 개발자들은 새로운 기술 등 관심있는 것에 집중해서 어느순간 소프트웨어의 목적에서 점점더 멀어지네 되는 경우가 많았습니다. 최근에는 고객의 요구사항의 변경과 개선에 유연하게 대응할 수 있는 Agile 의 도입으로 전체 프로젝트 기간을 수개월 씩 수행했던 분석, 설계, 구현, 테스트를 2~3주 단위의 짧은 스프린트(Sprint)로 나눠서 진행하는 경우가 많아졌습니다. 따라서 기존의 방식이 아닌 더 효과적인 설계를 빠르게 할 수 있는 기법의 적용이 필요하게 되었습니다. 그래서 SK주식회사는 이벤트 스토밍(Event Storming) 기법을 적용해서 Sprint #0단계에서 마이크로서비스를 도출하고 Sprint n 단계에서 점진적으로 서비스를 개발하고, 리뷰와 회고를 통해 지속적으로 서비스를 개선할 수 있는 방법과 절차를 방법론으로 정립하여 활용하고 있습니다.

이벤트 스토밍 기법

정리

설명으로 시작해서 자랑으로 마무리 된 것 같습니다. ^^ 저희도 도메인 주도 설계에서 얘기하는 전략적 설계를 책이나 웹사이트 검색을 통해 접하게 되면 뭔가 추상적이고 적당히 알 것 같으면서도 실제 프로젝트에서는 어떻게 적용해야 할지 몰랐습니다. 몇개의 프로젝트를 통해 시행착오도 경험하고 또 많은 실수와 개선을 반복해서 실제 프로젝트에 적용이 가능한 방법론으로 정의를 하게 되었습니다. 전략적 설계에 이어서, 전술적 설계에 대한 글도 기대해 주시기 바랍니다