서론
최근 다양한 소프트웨어 개발 방법론에 대해서 탐구하고 있었다. 테스트 주도 개발(TDD, Test-Driven Development), 행동 주도 개발(BDD, Behavior-Driven Development)등의 개발 방법론을 살펴보던 중, 도메인 주도 설계(DDD, Domain-Driven Design)에 대한 내용을 찾아 보게되었는데, 소프트웨어의 복잡성을 줄이고, 비즈니스 로직의 이해를 향상시킬 수 있다는 것으로 알게 되었다.
소프트웨어의 복잡성을 어떤 방법으로 줄일 수 있는지에 대해 많은 관심이 있었는데, 다른 개발 방법론 보다 도메인 주도 설계에 대해 필요성을 더욱 느끼게 되어 가장 우선적으로 찾아보기 시작했다.
이번에는 그 도메인 주도 설계에 대해 찾아본 내용을 간략하게 정리하여, 간단하게 도메인 주도 설계를 맛보려고 한다. 도메인 주도 설계란 무엇이고, 어떻게 시작할 수 있는지, 그리고 그 과정에서 이벤트 스토밍이 어떤 역할을 하는지 제로부터 시작하도록 하겠다.
📝 도메인 주도 설계(Domain-Driven Design)
우리가 서비스를 개발한다면, 그 서비스가 그 상태 그대로 변경되지 않는 경우는 엄청 드물게 발생한다. 대부분의 서비스는 고객의 요구사항이나 비즈니스 환경의 변화에 따라 지속적으로 업데이트되고 변화한다. 그러나 이런 변화를 수용하면서 시스템의 복잡성을 관리하는 것은 쉽지 않은 방법일 것이다. 이런 문제를 해결하기 위해 제안된 개발 방법론이 바로 “도메인 주도 설계(DDD, Domain-Driven Design)이다.
도메인 주도 설계는 서비스의 “기능”을 기준으로 코드를 구분하지 않고, “도메인”이라는 비즈니스 영역을 기준으로 코드를 구분하는 것이 가장 핵심적이다. 이렇게 한다면 각 코드의 역할과 책임이 명확해지고, 비즈니스 로직을 이해하고 관리하는 것이 한결 더 쉬워지게 될 것이다.
일반적인 개발 방법론과 달리 도메인 주도 설계는 개발자들이 주축이되어 직접 코드를 작성하는 것 뿐만 아니라, 비개발자들(고객, DBA, 아키텍트 등)과 함께 소프트웨어 설계 과정에 참여하도록 하여, 전체 팀이 동일한 이해를 공유하고 비즈니스 요구사항을 효과적으로 수용할 수 있게 되는것이 가장 특징적인 부분이다.
🤔 이벤트 스토밍(Event Storming)
이벤트 스토밍(Event Storming)은 서비스와 관계 있는 모든 이해관계자들이 서로가 가지고 있는 생각을 공유하며 서비스에서 발생하는 이벤트를 중심(Event-First)으로 분석하는 기법이다.
도메인 주도 설계를 실제로 어떻게 수행할 수 있을까? 여기서 등장하는 것이 바로 이벤트 스토밍(Event Storming)이다.
이벤트 스토밍(Event Storming)은 서비스에서 발생하는 주요 이벤트를 중심으로 도메인 모델을 만드는 방법론이다. 이는 개발자 뿐만 아니라 모든 이해관계자들이 참여하여 서비스에서 발생하는 이벤트를 정의하고, 그 이벤트가 어떻게 발생하는지 함께 이해하려는 접근 방식이다. 이 과정을 통해 각 이해관계자가 서비스에 대한 지식과 경험을 공유하며, 서비스에서 발생하는 이벤트와 그 관련 프로세스를 함께 이해하고 모델링하는 경험을 제공한다.
이벤트 스토밍의 구성요소
그렇다면 이벤트 스토밍은 어떻게 진행되는 걸까?
이벤트 스토밍은 보통 벽이나 대형 보드에 스티커를 붙이며 시작하게된다. 이 스티커들은 서비스에서 발생하는 이벤트를 나타내며, 각각의 색상은 서로 다른 의미를 가지게된다. 이를 통해 시스템에서 어떤 행위가 발생하는지, 그 결과로 어떤 이벤트가 일어나는지를 명확하게 표현할 수 있게 된다.
이벤트 스토밍을 통해 우리는 다음과 같은 질문에 답을 할 수 있게 될 것이다.
- 서비스에서 어떤 “주체”가 어떤 “행동”을 취하는가?
- 그 “행동”의 결과로 어떤 “이벤트”가 발생하는가?
- “이벤트”가 발생하면 시스템에서는 어떤 “변화”가 일어나는가?
- 이 “이벤트”가 다른 “이벤트”에 어떤 영향을 미치는가?
이런 질문들을 통해, 서비스의 핵심 프로세스와 비즈니스 로직을 명확히 이해하고 모델링할 수 있게 될 것이다.
이벤트 스토밍의 스티커는 각각의 색상마다 역할이 구분되어 있다. 각 스티커가 어떤 이름으로 불리고, 어떤 역할을 수행하는지는 이벤트 스토밍에서 중요한 부분이다. 그렇다면, 이벤트 스토밍에서는 어떤 스티커들이 있는지 확인해보도록하자.
도메인 이벤트(Domain Event)
도메인에서 실제로 발생하는 “결과”이다.
고객이 “자동차를 대여하겠다"는 요청을 서버에 전달하면, 서버에서는 “고객에게 자동차를 대여하겠다.”와 같은 “결과”가 발생할 것이다. 여기서, 실제 요청사항을 통해 발생한 결과를 “도메인 이벤트(Domain Event)”라고 부르게 되는데, 이는 서비스에서 발생한 사실, 결과, 특정행위의 Output을 표현하기위해 사용된다. 도메인 이벤트는 오렌지 색상의 스티커를 사용하여 표현하고, 과거 분사형을 이용하여 이름을 정의하게 된다.
커맨드(Command)
도메인에서 특정 주체가 요청하는 “행위”이다.
고객이 “자동차를 대여하겠다”는 요청을 한다면, 이 요청은 “커맨드(Command)”로 분류된다. 대표적으로 커맨드는 입력(Input), API, UI 버튼 등을 표현하는데 사용되며, 파란색 스티커를 사용하여 표현하고, 현재형을 이용해 이름을 정의하게 된다.
액터(Actor)
커맨드를 발생시키는 “주체”를 표현할 때 사용한다.
온라인 강의 사이트에서는 “지식 제공자”는 강의를 촬영하고, “수강생”은 강의를 수강한다. 이처럼 각각의 행위를 수행하는 주체를 “액터(Actor)” 라고 부르며, 노란 색상의 스티커를 사용하여 표현하게 된다.
정책(Policy)
이벤트 조건에 따라 발생하는 “새로운 커맨드”를 나타낸다.
모든 비즈니스로직은 1개의 행위로 1개의 결과가 발생하지 않는다. 고객이 주문을 하였을 때, 알림이 발생하는 경우를 간략하게 요약하면, “고객이 주문 요청(Command) → 주문 완료(Event) → 알림 생성 요청(Policy) → 알림 발송(Event)”으로 정리할 수 있다. 여기서 이벤트로 인해 발생하는 요청 사항을 “정책(Policy)”이라고 부르며, 라일락 색의 스티커를 사용하여 표현하게 된다.
외부 시스템(External System)
도메인 이벤트가 호출하거나 관계가 있는 “외부 시스템”을 표현할 때 사용한다.
여러 서비스에서는 해당 서비스 코드 내부에서만 처리될 수 없이, 외부 시스템이 필요한 경우가 있다. 결제 요청을 처리하기 위해서는 “결제 모듈”이라는 외부 시스템이 필요하며, 고객에게 알림을 전달하기 위해서는 “알림 모듈”이라는 외부 시스템이 필요할 것이다. 이처럼 서비스에 필요한 기능이지만 직접 개발하기 어렵거나, 외부 의존성을 가지는 시스템을 “외부 시스템(External System)”이라고 부르며, 핑크색스티커를 사용하여 표현하게 된다.
어그리게이트(Aggregate)
도메인 이벤트와 커맨드가 처리하게되는 “데이터”이다.
고객이 자동차를 대여하는 요청을 하면, 자동차 대여점에서는 해당 요청을 처리하기 위해 필요한 자동차의 상세한 정보(차종, 연식, 주행거리 등)가 필요하게 된다. 이러한 데이터를 “어그리게이트(Aggregate)”라고 부르며, 연 노란 색상의 스티커를 사용하여 표현하게 된다.
바운디드 컨텍스트(Bounded Context)
“도메인” 그 자체를 나타낸다.
배달 서비스를 구현하기 위해서는 “주문(Order)”, “매장(Store)”, “배달(Delivery)”과 같은 여러가지의 도메인이 있을 것이다. 각 도메인은 여러 개의 액터, 커맨드, 이벤트, 어그리게이트 등으로 구성되는데, 이 모든 구성요소를 포함하여 하나의 도메인을 구성한 것을 “바운디드 컨텍스트(Bounded Context)”라고 부르게 된다.
이벤트 스토밍 구성요소 정리하기
요소 | 스티커 색상 | 설명 |
도메인 이벤트(Domain Event) | 오렌지색 | 도메인에서 실제로 발생하는 “결과” |
커맨드(Command) | 파란색 | 도메인에서 특정 주체가 요청하는 “행위” |
액터(Actor) | 노란색 | 커맨드를 발생시키는 “주체” |
정책(Policy) | 라일락 색 | 이벤트 조건에 따라 발생하는 “새로운 커맨드” |
외부 시스템(External System) | 핑크색 | 도메인 이벤트가 호출하거나 관계가 있는 “외부 시스템” |
어그리게이트(Aggregate) | 연 노란색 | 도메인 이벤트와 커맨드가 처리하게되는 “데이터” |
바운디드 컨텍스트(Bounded Context) | “도메인” 그 자체, 마이크로 서비스 |
⚒️ 제로부터 이벤트 스토밍 시작하기
위에서 이벤트 스토밍의 구성요소에 대해 확인해보았다. 그럼 이제 이러한 구성요소들을 이용해 실제 프로젝트 요구사항을 바탕으로 이벤트 스토밍을 제로부터 시작해보자.
이벤트 스토밍의 Step
이벤트 스토밍의 과정은 개발팀의 구성과 프로젝트의 성격에 따라 다르겠지만, 여기서는 아래의 6가지 단계로 진행해보고자 한다.
- 도메인 이벤트(Domain Event) 도출
- 이벤트에 따른 정책(Policy) 도출
- 커맨드(Command)와 액터(Actor) 식별
- 어그리게이트(Aggregate) 매핑
- 바운디드 컨텍스트(Bound Context) 식별
- 컨텍스트 매핑(Context Mapping)
프로젝트 요구사항
이번에 다룰 예제는 자동차 렌트 서비스의 일부분이다. 이 서비스에 대한 요구사항은 아래와 같다.
- 고객은 자동차를 렌트할 수 있어야 한다.
- 대여점은 자동차 렌트 요청이 도달했을 때, 수락 또는 거절할 수 있어야 한다.
- 고객은 렌트한 자동차를 반환할 수 있어야 한다.
- 자동차 렌트는 최소 8시간에서 최대 30일까지만 가능해야 한다.
- 자동차 렌트 상태가 변경될 때마다 고객과 대여점에 알림이 전송되어야 한다.
1️⃣ 도메인 이벤트(Domain Event) 도출
“도메인 이벤트(Domain Event) 도출” 단계는 서비스에서 발생할 수 있는 비즈니스 이벤트를 분석하는 단계이다. 이번 요구사항을 기반으로는 “렌트 요청”, “렌트 반환”, “렌트 수락”, “렌트 거절”, “알림 발송” 등 총 5개의 이벤트를 도출하게 되었다.
2️⃣ 이벤트에 따른 정책(Policy) 도출
“이벤트에 따른 정책(Policy) 도출” 단계는 도메인 이벤트 발생 후 그에 따라 추가적으로 반응하는 행위인 정책(Policy)을 도출하는 단계이다.
고객이 “자동차 렌트 요청”을 하면, 대여점에는 “렌트 요청 생성”이 발생하며, 고객 또는 대여점이 렌트를 “수락”, “거절”, “반환” 하게된다면, “렌트 상태 변경”이 발생하게 될 것이다. 또한 상태 변경시마다 알림이 전송되어 “알림 발송”도 함께 발생하게 될 것이다.
3️⃣ 커맨드(Command)와 액터(Actor) 식별
“커맨드(Command)와 액터(Actor) 식별” 단계는 도메인 이벤트를 발생시키는 “행위”인 커맨드(Command)와 행위를 수행하는 “주체”인 액터(Actor) 도출하는 단계이다.
"고객"이 "자동차 렌트 요청"을 하면, 고객이라는 "주체"가 자동차 렌트 요청 이라는 "행위"를 하게되므로 액터와 커맨드를 도출해낼 수 있게된다.
4️⃣ 어그리게이트(Aggregate) 매핑
“어그리게이트(Aggregate) 매핑” 단계는 커맨드와 이벤트가 사용하는 데이터를 정의하는 단계이다. 이번 사례에서는 간략하게 “고객(Customer)”, “매장(Store)”, “알림(Notification)”등의 대표적인 데이터를 사용하였지만, 세부 단계에서는 어그리게이트 내부에서 사용하는 상세 데이터도 함께 지정하여 더욱 명확한 이벤트 스토밍을 진행하게 될 것이다.
5️⃣ 바운디드 컨텍스트(Bound Context)
“바운디드 컨텍스트(Bound Context)” 단계는 지금까지의 결과물들을 바운디드 컨텍스트를 이용해 묶어주는 단계이다. 이벤트 스토밍이 완료되었을 때, 이 단계에서 생성된 바운디드 컨텍스트들은 각각의 마이크로 서비스가 될 가능성이 있다.
6️⃣ 컨텍스트 매핑(Context Mapping)
마지막으로 “컨텍스트 매핑(Context Mapping)” 단계는 최종적으로 구성요소간의 관계를 설정하는 단계이다. 이 단계에서는 고려하지 못했던 외부 시스템(External System)이나, 추가적으로 도출된 도메인 이벤트나 커맨드 등을 포함하여 전체 시스템을 검토해야한다.
예를들어 매장에서 발생하는 “렌트 수락됨”, “렌트 거절됨”과 같은 이벤트를 “렌트 상태 변경됨”으로 통합하거나, 알림(Notification) 바운디드 컨텍스트에서 사용하는 외부 시스템(External System)을 추가할 수 있다.
최종적으로는 EDA(Event Driven Architecture)를 이용하여 각 도메인 간의 통신을 유연하게 구성하게 될 것이다.
마지막으로
이번 예제에서는 “주문(Order)” 도메인이나, “재고(Stock)”도메인 등 추가 도메인이 필요하였지만, 이번 게시글의 목적은 이벤트 스토밍을 최대한 간결하게 이해하는 데에 있었다.
처음 이벤트 스토밍을 학습했을 때, 어떻게 각 요구사항들을 구성해야 하는지 명확하게 이해하는 데 어려움이 있었다. 그러나 웹 사이트 또는 자주 사용하는 어플리케이션에서 발생할 수 있는 이벤트들을 기반으로 이벤트 스토밍을 여러 번 시도하면서 점차 이해도가 높아지게 되었고, 현재는 어느정도 친숙해지게 된 것을 느끼게 되었다.
다음 사이트 프로젝트나 개인 학습에서는 이벤트 스토밍을 통해 요구사항과 도메인을 명확하게 이해하고 분석하는 것을 목표로 설정하였다. 이를 통해 역할과 책임이 분리된 안정적인 개발을 추구하며, 지속적인 성장을 위해 노력할 것이다.
'분석과 탐구' 카테고리의 다른 글
제로부터 시작하는 Prisma와 Nest.js (0) | 2023.07.16 |
---|---|
아키텍처 패턴 그리고 헥사고날 아키텍처 (0) | 2023.07.02 |
Amazon API Gateway의 WebSocket API란 무엇인가? (0) | 2023.05.21 |
1일 1커밋에서 벗어나기 (0) | 2023.05.07 |
"RxJS" 넌 도대체 뭐니? (0) | 2023.02.25 |