CH02 크게 소리 내어 모델링 하기
Last updated
Last updated
전략적 설계의 정의와 목적을 이해한다(feat. UBIQUITOUS LANGUAGE, 효과적인 모델링)
Bounded Context의 정의와 목적을 이해한다.
이벤트 스토밍(Event Storming)에 대해서 이해한다.(이벤트 스토밍에 사용되는 개념과 포스트잇을 활용한 Map 시각화 방법)
프로그램이 소화하고자 하는 도메인이 얼마나, 어떻게 쪼개진다고 해도 하나의 뜻으로 통용되는 단어가 있을때 이를 유비쿼터스 언어라고 한다. 그리고 이는 곧 나올 Bounded Context의 명칭 역시 도메인 내에서 global 하게 사용되므로 유비쿼터스 언어라고 할 수 있다.
유비쿼터스 언어 자체가 중요하다기 보다는 유비쿼터스 언어라는 단어 자체가 있다는 배경에 더 집중할 필요가 있다. 무슨 의미냐면 DDD 의 핵심인 전술적 설계를 위해서는 도메인이 여러 도메인으로 나뉘게 되는데(Bounded Context) 이때 어떤 Context 에서든 통용될 단어에 대해서 이름을 붙였다는 사실이 곧 특정 Context에서만 통하는 단어가 있다는 의미이기 때문이다.
따라서 유비쿼터스 언어 자체의 뜻도 알고 있어야겠지만 DDD에서는 Context 별로 용어가 존재하고 설령 동음의 단어가 있다고 하더라도 뜻이 다른(동음이의어)가 존재한다는 것을 유비쿼터스 언어의 존재로 유추할 수 있다.
그래서 이번 수업의 실습이 이와 밀접하게 연관되어 있는 ‘용어사전 정의하기’ 였다. 용어사전은 아래와 같은 모습을 띄고 있다.
경험상 실무에서도 처음 보는 코드랑 도메인을 접할 때 단어를 해석하는데 시간을 많이 썼었는데, README 에 이렇게 코드가 한글과 영어로 정의되어 있고 옆에 설명까지 써져있으면 정말 시간을 많이 아낄 수 있고 도메인 이해에도 큰 도움이 될 것 같다는 생각이 들었다.
팀 단위로 하면 좋지만 개인적으로도 따로 이런 것들을 정리하면 좋겠다는 생각이 든다. 정리하는 과정에서 도메인에 대한 이해가 더 구체적이게 되기 때문이다.
도메인을 ‘모델링’ 한다는 것이 정의하기엔 너무 광범위한 단어라는 생각이 든다. 도메인을 이해하기 쉽게 나누고 시각화 하는 것을 ‘모델링’ 한다는 의미라고 한다면 DDD 수업 전반에 걸쳐서 여러가지 방법들로 ‘모델링’ 을 하는데 이번 수업에서의 모델링은 아래와 같은 형식이었다.
요구 사항과 거의 흡사했는데 굳이 차이점을 찾자면 용어사전을 정리하고서 모델링을 한 만큼 모델링에 사용되는 용어들은 용어사전에 정의해둔 단어들에 국한된다는 것이다.
강의 자료에서는 효과적인 모델링의 좋은 예시와 나쁜 예시로 아래와 같은 문구를 들고 있다.
물론 RouteSpecification 에 대한 정의가 따로 되어있다는 전제가 있다.
DDD 의 목적 자체가 ‘전략적인 설계’이다. 설계가 전략적이기 위해서는 복잡도와 의존도가 최대한 낮으면서 응집력이 좋아야 하는데 이 나뉨의 단위가 결국 커다란 도메인 내에서 세부적으로 쪼개지는 세부 도메인이라 할 수 있다.
Bounded Context란 이렇게 쪼개어지는 세부 도메인이라고 할 수 있다. 최상위에 모든 것을 포함할 수 있는 도메인이 있고 도메인 밑에 하위 도메인이 존재할 수 있다는 의미이다. 여기서 하위 도메인이 곧 Bounded Context 이다. 경계가 있는 독자적인 Context로서 존재하는 단위인 것이다.
이렇게 맥락이 경계가 지어진다는 의미는 해당 맥락 내에서만 통용되는 특수성이 존재한다는 것이다. 그래서 용어사전은 Bounded Context 마다 각각 존재해야한다.
개발관점에서 보면 각각의 Bounded Context는 각기 다른 개발 환경을 지닐 수도 있고, 각기 다른 팀들이 관리할 수도 있다.
솔직히 회사에서 다같이 이걸 할 수 있을까 싶다. 회사에서 여건을 보장해줘야 가능한 부분이다.
하지만 다같이 할 수 없다고 하더라도 이벤트 스토밍의 진행방식 자체가 도메인에 대한 깊은 이해와 함께 시간의 개념을 넣어서 통시적으로 도메인을 이해하는데 매우 유용한 방식이라 생각이 든다.(개인적으로 기획서에 늘 보이는 flow chart 보다 더 직관적인 것 같다)
자세한건 자료의 링크에서 내용들을 다 볼 수 있기 때문에 세세히 정리는 하지 않을 것이고 간략히 핵심만 뽑아보자면 아래와 같다.
주황색은 이벤트를 의미한다.
시간은 좌에서 우로 흐르며 동시간대의 이벤트는 세로로 나타낸다. 동일한 세로 선상에서는 같은 시간의 흐름을 지닌다.
자주색은 일종의 backlog와 같은 의미로 추가 논의가 필요한 이슈이다.
노란색은 액터(Actor)로 구체적으로 명시하는게 좋다(사용자 말고 고객 등 최대한 구체적으로)
분홍색은 책임을 전가할 수 있는 모든 것(주로 외부 요소와 같은)
파란색은 시스템에서 일어나는 일이다. 즉, 이벤트의 원인이 되는 것으로 파란색으로 인해 주황색이 생긴다고 보면 된다.
초록색은 액터가 결정을 내리기 위해서(파란색을 발생시키는데) 사전에 알고 있어야할 정보를 의미한다.
보라색은 정책을 의미한다.
자꾸 똑같은 말을 반복하지만 이것 역시 팀으로 하지 않아도 개인적으로 실무에 적용해서 내가 이해도를 높이기 위한 용도로 사용해도 괜찮을 것 같다는 생각이 들었다.
요구사항, 용어사전, 모델링 작성이 실습이었는데 옮기기 편해서 링크와 함께 내용까지 아래에 옮겼다. 실제로 다른 프로젝트 수행시 아래 과정을 그대로 답습했는데 도메인 이해도를 높히는데 개인적으로 많은 도움이 되었다.
상품을 등록할 수 있다.
상품의 가격이 올바르지 않으면 등록할 수 없다.
상품의 가격은 0원 이상이어야 한다.
상품의 이름이 올바르지 않으면 등록할 수 없다.
상품의 이름에는 비속어가 포함될 수 없다.
상품의 가격을 변경할 수 있다.
상품의 가격이 올바르지 않으면 변경할 수 없다.
상품의 가격은 0원 이상이어야 한다.
상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.
상품의 목록을 조회할 수 있다.
메뉴 그룹을 등록할 수 있다.
메뉴 그룹의 이름이 올바르지 않으면 등록할 수 없다.
메뉴 그룹의 이름은 비워 둘 수 없다.
메뉴 그룹의 목록을 조회할 수 있다.
1 개 이상의 등록된 상품으로 메뉴를 등록할 수 있다.
상품이 없으면 등록할 수 없다.
메뉴에 속한 상품의 수량은 0 이상이어야 한다.
메뉴의 가격이 올바르지 않으면 등록할 수 없다.
메뉴의 가격은 0원 이상이어야 한다.
메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.
메뉴는 특정 메뉴 그룹에 속해야 한다.
메뉴의 이름이 올바르지 않으면 등록할 수 없다.
메뉴의 이름에는 비속어가 포함될 수 없다.
메뉴의 가격을 변경할 수 있다.
메뉴의 가격이 올바르지 않으면 변경할 수 없다.
메뉴의 가격은 0원 이상이어야 한다.
메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.
메뉴를 노출할 수 있다.
메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 높을 경우 메뉴를 노출할 수 없다.
메뉴를 숨길 수 있다.
메뉴의 목록을 조회할 수 있다.
주문 테이블을 등록할 수 있다.
주문 테이블의 이름이 올바르지 않으면 등록할 수 없다.
주문 테이블의 이름은 비워 둘 수 없다.
빈 테이블을 해지할 수 있다.
빈 테이블로 설정할 수 있다.
완료되지 않은 주문이 있는 주문 테이블은 빈 테이블로 설정할 수 없다.
방문한 손님 수를 변경할 수 있다.
방문한 손님 수가 올바르지 않으면 변경할 수 없다.
방문한 손님 수는 0 이상이어야 한다.
빈 테이블은 방문한 손님 수를 변경할 수 없다.
주문 테이블의 목록을 조회할 수 있다.
1개 이상의 등록된 메뉴로 배달 주문을 등록할 수 있다.
1개 이상의 등록된 메뉴로 포장 주문을 등록할 수 있다.
1개 이상의 등록된 메뉴로 매장 주문을 등록할 수 있다.
주문 유형이 올바르지 않으면 등록할 수 없다.
메뉴가 없으면 등록할 수 없다.
매장 주문은 주문 항목의 수량이 0 미만일 수 있다.
매장 주문을 제외한 주문의 경우 주문 항목의 수량은 0 이상이어야 한다.
배달 주소가 올바르지 않으면 배달 주문을 등록할 수 없다.
배달 주소는 비워 둘 수 없다.
빈 테이블에는 매장 주문을 등록할 수 없다.
숨겨진 메뉴는 주문할 수 없다.
주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다.
주문을 접수한다.
접수 대기 중인 주문만 접수할 수 있다.
배달 주문을 접수되면 배달 대행사를 호출한다.
주문을 서빙한다.
접수된 주문만 서빙할 수 있다.
주문을 배달한다.
배달 주문만 배달할 수 있다.
서빙된 주문만 배달할 수 있다.
주문을 배달 완료한다.
배달 중인 주문만 배달 완료할 수 있다.
주문을 완료한다.
배달 주문의 경우 배달 완료된 주문만 완료할 수 있다.
포장 및 매장 주문의 경우 서빙된 주문만 완료할 수 있다.
주문 테이블의 모든 매장 주문이 완료되면 빈 테이블로 설정한다.
완료되지 않은 매장 주문이 있는 주문 테이블은 빈 테이블로 설정하지 않는다.
주문 목록을 조회할 수 있다.
키친포스
kitchenpos
음식점에서 매장 관리에 사용되는 관리 시스템(현재 프로젝트로 구현된 앱 전체)
상품
Product
메뉴를 구성하는 최소 단위로 음식점에서 만들어지는 음식
상품 이름
Product name
비속어가 포함되지 않은 상품의 이름
상품 가격
Product price
음수가 아닌 상품의 가격
비속어
Profanity
욕설과 같은 비속어
상품 등록
Product create
새로운 상품을 등록
상품 가격 변경
Product changePrice
상품의 가격을 변경
상품 목록 조회
Product findAll
등록된 상품 전체 목록을 조회
메뉴 그룹
MenuGroup
메뉴가 분류될 수 있는 종류
메뉴 그룹 이름
MenuGroup name
메뉴 그룹의 이름
메뉴 그룹 등록
MenuGroup create
새로운 메뉴 그룹을 등록
메뉴 그룹 목록 조회
MenuGroup findAll
등록된 메뉴 그룹 전체 목록을 조회
메뉴
Menu
특정 메뉴 그룹에 속하며 하나 이상의 상품들로 구성되는 단위
메뉴 이름
Menu name
메뉴의 이름
메뉴 가격
Menu price
메뉴의 가격
메뉴 노출(숨김) 상태
displayed
메뉴의 노출 또는 숨김에 대한 상태
메뉴에 속한 상품
Menu MenuProduct
메뉴를 구성하는 상품으로 1개 이상의 수량을 갖는다
메뉴 등록
Menu create
새로운 메뉴를 등록
메뉴 가격 변경
Menu changePrice
메뉴의 가격을 변경
메뉴 노출
Menu display
메뉴 노출(숨김) 상태를 노출로 변경
메뉴 숨김
Menu hide
메뉴 노출(숨김) 상태를 숨김으로 변경
메뉴 목록 조회
Menu findAll
등록된 메뉴 전체 목록을 조회
주문 테이블
OrderTable
음식점 내 테이블
주문 테이블 이름
OrderTable name
주문 테이블의 이름
주문 테이블 방문한 손님 수
OrderTable name
주문 테이블을 이용하고 있는 손님 수
주문 테이블 빈 테이블
OrderTable empty
주문 테이블이 비었는지 여부에 대한 상태
주문 테이블 등록
OrderTable create
새로운 주문 테이블 등록
주문 테이블 빈 테이블로 해지
sit
빈 테이블 상태를 비어있지 않은 상태로 변경
주문 테이블 빈 테이블로 설정
clear
빈 테이블 상태를 비어있는 상태로 변경
주문 테이블 방문한 손님 수 변경
OrderTable changeNumberOfGuests
주문 테이블의 방문한 손님 수를 변경
주문 테이블 목록 조회
OrderTable findAll
주문 테이블 전체 목록을 조회
주문
Order
음식점에서 손님이 1개 이상의 등록된 메뉴를 요청하는 행위
주문 유형
OrderType
주문의 유형으로 배달 주문, 포장 주문, 매장주문이 있다
배달 주문
OrderType.DELIVERY
배달을 통해 전달되는 주문
포장 주문
OrderType.TAKEOUT
손님이 포장해서 가져가는 주문
매중 주문
OrderType.EAT_IN
손님이 음식점의 주문 테이블에서 먹고가는 주문
주문 상태
OrderStatus
주문의 상태로 접수 대기, 접수된, 서빙된, 배달 중인, 배달 완료된, 완료된 상태가 있다.
접수 대기
OrderStatus.WAITING
최초로 주문이 등록되고 접수가 되기 전 상태
접수된
OrderStatus.ACCEPTED
주문이 접수된 상태
서빙된
OrderStatus.SERVED
주문이 손님에게 서빙된 상태
배달 중인
OrderStatus.DELIVERING
주문이 손님에게 배달중인 상태
배달 완료된
OrderStatus.DELIVERED
배달중이던 주문이 손님에게 전달완료된 상태
완료된
OrderStatus.COMPLETED
주문이 완료된 상태
주문한 메뉴
OrderLineItem
주문을 구성하는 메뉴이며 주문 유형이 배달 또는 포장인 경우 수량은 1개 이상이어야 하고, 메뉴가 숨김 상태이면 안된다.
배달 주소
deliveryAddress
주문이 배달되어 도착할 손님의 주소
주문 등록
Order create
새로운 주문을 등록
주문 접수
Order accept
주문의 주문 상태를 '접수된' 상태로 변경
주문 서빙
Order serve
주문의 주문 상태를 '서빙된' 상태로 변경
주문 배달시작
Order startDelivery
주문의 주문 상태를 '배달 중인' 상태로 변경
주문 배달완료
Order completeDelivery
주문의 주문 상태를 '배달 완료된' 상태로 변경
주문 완료
Order complete
주문의 주문 상태를 '완료된' 상태로 변경
주문 목록 조회
Order findAll
주문 전체 목록을 조회
배달 대행사
kitchenriders
주문 배달시작시 음식점에서 배달을 요청하는 곳
속성
비속어(Profanity)가 포함되지 않는 이름(name) 을 가진다
0원 이상인 가격(price) 을 가진다
기능
상품을 등록(create) 할 수 있다.
상품의 이름(name)에 비속어(Profanity)가 포함되면 안된다.
상품의 가격(price)는 0원 이상이어야 한다.
상품의 가격(price)을 변경 할 수 있다.
변경할 상품의 가격(price)는 0원 이상이어야 한다.
가격(price)이 변경된 상품을 메뉴에 속한 상품(MenuProduct)으로 갖는 메뉴의 경우, 메뉴의 가격이 메뉴에 속한 상품(MenuProduct) 가격의 합보다 비싸면 해당 메뉴는 숨김 상태(displayed = false)가 된다.
상품의 목록을 조회(findAll) 할 수 있다.
속성
이름(name)을 가진다.
기능
메뉴 그룹을 등록(create) 할 수 있다.
메뉴의 이름(name) 없이 등록할 수 없다.
메뉴 그룹의 목록을 조회(findAll) 할 수 있다.
속성
메뉴에 속한 상품(MenuProduct)을 가진다.
비속어(Profanity)를 포함하고 있지 않는 이름(name)을 가진다.
0원 이상의 가격(price)를 가지며 이 가격은 메뉴에 속한 상품(MenuProduct) 가격의 합보다 비싸면 안된다.
해당 메뉴가 포함될 메뉴 그룹(MenuGroup)이 있어야 한다.
노출 또는 숨김상태(display)를 가진다.
기능
메뉴를 등록(create) 할 수 있다.
메뉴에 속한 상품(MenuProduct)없이 등록할 수 없으며 메뉴에 속한 상품(MenuProduct)의 수량은 0 이상이어야 한다.
메뉴의 이름(name)은 비속어(Profanity)를 포함하고 있으면 안된다.
메뉴의 가격(price)은 0원 이상이어야 하며 해당 메뉴가 가지는 메뉴에 속한 상품(MenuProduct)의 가격의 합보다 비싸면 안된다.
메뉴가 포함될 메뉴 그룹(MenuGroup)없이 등록할 수 없다.
메뉴의 가격을 변경(changePrice) 할 수 있다.
0원 이상의 가격으로만 변경 할 수 있다.
변경될 메뉴의 가격은 메뉴에 속한 상품(MenuProduct)의 가격의 합보다 비싸면 안된다.
메뉴를 노출(display = true) 처리 할 수 있다.
메뉴의 가격이 메뉴에 속한 상품(MenuProduct)의 가격의 합보다 비싸면 메뉴를 노출 할 수 없다.
메뉴를 숨김(display = false) 처리 할 수 있다.
메뉴의 목록을 조회(findAll) 할 수 있다.
속성
비어있지 않은 이름(name)을 가진다.
기능
주문 테이블을 등록(create) 할 수 있다.
빈 이름("")으로 등록 할 수 없다.
빈 테이블로 해지(sit) 할 수 있다.
빈 테이블로 설정(clear) 할 수 있다.
해당 주문 테이블의 주문(order)가 완료(OrderStatus.COMPLETED) 상태가 아니면 빈 테이블로 설정(clear) 할 수 없다.
방문한 손님 수를 변경(changeNumberOfGuests) 할 수 있다.
변경할 손님 수는 0 이상이어야 한다.
주문 테이블의 목록을 조회(findAll) 할 수 있다.
속성
주문 유형(OrderType) 을 가지며 주문 유형으로는 배달 주문(DELIVERY), 포장 주문(TAKEOUT), 매장 주문(EAT_IN)이 있다.
1개 이상의 등록된 메뉴(Menu)를 주문한 메뉴(OrderLineItem)로 가지며 주문한 메뉴(OrderLineItem)는 가격(price)를 가진다.
배달 주문(DELIVERY)의 경우 비어있지 않은 배달 주소(deliveryAddress)를 가진다.
주문 상태(OrderStatus)를 가지며 주문 상태로는 접수 대기(WAITING), 접수된(ACCEPTED), 서빙된(SERVED), 배달 중인(DELIVERING), 배달 완료된(DELIVERED), 완료된(COMPLETED) 상태가 있다.
주문 테이블(OrderTable)을 가진다.
기능
주문을 등록(create) 할 수 있다.
주문 유형(OrderType)이 배달 주문(DELIVERY), 포장 주문(TAKEOUT), 매장 주문(EAT_IN) 중 하나여야 한다.
등록된 메뉴(Menu) 가 아니면 주문 할 수 없다.
주문한 메뉴(OrderLineItem)의 수량은 0개 이상이어야 한다. 단, 매장 주문(EAT_IN) 의 경우 수량이 0 미만일 수 있다.
매장 주문(EAT_IN)의 경우 빈 테이블에 등록할 수 없다.
메뉴의 상태가 숨김(display=false)인 경우 주문을 등록할 수 없다.
주문한 메뉴(OrderLineItem)의 가격은 실제 메뉴의 가격(price)와 일치해야 한다.
주문을 접수(accept) 할 수 있다.
주문의 상태(OrderStatus)가 접수 대기(WAITING)인 주문만 접수(accept) 할 수 있다.
주문 유형(OrderType)이 배달 주문(DELIVERY)인 경우 이를 접수하게 되면 배달 대행사(kitchenriders)를 호출해야 한다.
주문을 서빙(serve) 할 수 있다.
주문 상태(OrderStatus)가 접수된(ACCEPTED) 상태인 주문만 서빙 할 수 있다.
주문을 배달(startDelivery) 할 수 있다.
주문 상태(OrderStatus)가 서빙된(SERVED) 상태인 주문만 서빙 할 수 있다.
주문 배달을 완료(completeDelivery) 할 수 있다.
주문 상태(OrderStatus)가 배달중인(DELIVERING) 상태인 주문만 서빙 할 수 있다.
주문을 완료(complete) 할 수 있다.
주문 유형(OrderType)이 배달 주문(DELIVERY)인 경우 주문 상태(OrderStatus)가 배달 완료된(DELIVERED) 상태인 주문만 완료 할 수 있다.
주문 유형(OrderType)이 포장 주문(TAKEOUT)인 경우 주문 상태(OrderStatus)가 서빙된(SERVED) 상태인 주문만 완료 할 수 있다.
주문 완료시 해당 주문의 주문 테이블(OrderTable)을 빈 테이블로 설정(clear) 한다.
주문 목록을 조회(findAll) 할 수 있다.
박재성님이 발표를 하셨던 같은데 이벤트 스토밍에 대해서 매우 잘 정리가 되어 있다.
강의에서 안내해주는 라는 사이트에서 이를 온라인 상에서 해볼 수 있도록 툴을 제공한다. 아래는 이 사이트에서 수강생들이 강의 시간에 실시간으로 다 같이 붙인 포스트잇이다. 혼자 해볼 때에도 저 사이트를 이용하면 좋을 것 같다.