▼ What ?
객체지향 시스템을 설계할 때 '기능'과 '구조' 중 무엇에 중점을 두고 설계를 해야 하는지가 이번 여섯 번째 챕터의 발단이었다. 그래서 '구조'에 중점을 두고 설계를 할 때 필요한 '도메인(Domain)'이라는 개념이 무엇이며, '유스케이스(Use Case)'와 '도메인 모델(Domain Model)'이 '책임-주도 설계'와 어떠한 관련이 있는지에 대한 내용이 담겨있다. 쉽게 말해서 '책임-주도 설계'를 하기 전에 먼저 어떤 책임이 필요한지부터 생각해야 하는데, 그 생각해내는 과정에 대한 내용을 다루는 챕터라고 할 수 있다.
▼ Summory & Comment
이 챕터를 읽고 나서 기억에 남는 말 !
안정적인 도메인 모델을 기반으로 시스템의 기능을 구현하라. 도메인 모델과 코드를 밀접하게 연관시키기 위해 노력하라.
- 도메인과 관련된 파트는 이번 챕터에서 처음 다루는 내용이었는데, 객체지향 시스템을 설계할 때 이 도메인 모델이라는 개념을 어떤 식으로 활용해야 하는지를 명료하게 설명한 문장인 것 같아 기억해두면 좋을 것 같다.
객체지향 개발은 "지도"처럼 !
- 어떤 지역을 찾아간다고 하자.
- "길을 직접 알려주기" ➙ 현재의 요구사항('기능')에 초점을 맞춘 접근 방식
- "지도" ➙ 길을 찾기 위한 '구조'를 제공하는 접근 방식
- "길을 직접 알려주기" ➙ 현재의 요구사항('기능')에 초점을 맞춘 접근 방식
- "지도"는 잘 변하지 않는 지형 정보를 기반으로 하고 있기 때문에 과거의 지도도 현재에 유용하게 사용될 수 있고 범용적이다.
➙ 그때그때 길을 묻지 말고 지도처럼 '안정적인 구조'에 따라 소프트웨어를 설계하자 !
➙ "객체 지도"
- 요구사항이 예측 불가능하게 계속해서 변경되는 것은 소프트웨어의 필연적인 특성이다.
➙ 그러한 변경사항에 미리 대비하기 위해선 변경을 '예측'하는 것이 아니라 변경을 '수용'할 수 있도록 설계해야 한다 !
객체지향 세계를 구축하기 위한 두 가지 재료
- 훌륭한 소프트웨어를 만들기 위한
충분조건 = '기능' + 필요조건 = '구조'
➙ '기능(function)'과 '구조(structure)' 측면을 함께 녹여 조화를 이루도록 소프트웨어를 설계해야 한다 !
- 기능은 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템의 행위로,
구조는 사용자나 이해관계자들이 도메인(domain)에 관해 생각하는 개념과 개념들 간의 관계로 표현한다.
- '기능'을 수집하고 표현하기 위한 기법 ➙ "유스케이스 모델링"
- '구조'를 수집하고 표현하기 위한 기법 ➙ "도메인 모델링"
- '기능'을 수집하고 표현하기 위한 기법 ➙ "유스케이스 모델링"
안정적인 재료: 구조 (cf. 도메인 모델)
- 도메인과 도메인 모델 ?
'도메인'은 사용자가 프로그램을 사용하는 대상의 분야를 의미한다.
(ex) 은행 업무에 종사하는 사람들은 "은행"이라는 도메인을 고객과 계좌 사이의 돈의 흐름으로 이해할 것이다.
➙ 이처럼 도메인과 관련된 사람들이 '도메인'을 바라보는 모델을 '도메인 모델'이라고 한다 !
- "도널드 노먼(Donald Norman)"은 멘탈 모델*을 사용자 모델, 디자인 모델, 시스템 이미지의 세 가지로 구분했다.
*멘탈 모델(Mental Model): 세상에 존재하는 현상을 이해하고 현상에 반응하기 위해 자신의 마음 속에 구축하는 것.
- 사용자 모델: 사용자가 제품에 대해 갖고 있는 개념들의 모습.
- 디자인 모델: 설계자가 마음 속에 갖고 있는 시스템을 개념화한 모습.
- 시스템 이미지: 최종 제품.
➙ 소프트웨어를 설계할 때 '디자인 모델'을 기반으로 만든 '시스템 이미지'가 '사용자 모델'을 정확하게 반영하도록 노력해야 한다. - 즉, 사용자가 도메인을 바라보는 관점이며, 설계자가 시스템의 구조를 바라보는 관점인 동시에 소프트웨어 안에 구현된 코드의 모습 그자체인 '도메인 모델'을 기반으로 애플리케이션을 설계하자 !
- '소프트웨어 객체'와 '현실 객체' 사이의 의미적 거리를 "표현적 차이" 혹은 "의미적 차이"라고 하는데,
보통 소프트웨어 '도메인'은 현실에는 존재하지 않는 것들을 대상으로 한다.
소프트웨어 객체는 현실 객체를 모방한 것이 아니라 은유를 기반으로 재창조한 것이다.
➙ 따라서, "표현적 차이"를 줄이기 위해선 '도메인'을 은유할 것이 아니라 사용자가 그러한 도메인에 대해 생각하는 개념들의 집합인 '도메인 모델'을 기반으로 설계하고 구현해야 한다 !
➙ 소프트웨어를 쉽게 이해하고 수정할 수 있게 된다.
- 도메인 모델을 기반으로 코드를 작성해야 하는 두 가지 이유
- 사용자가 도메인에 대해 생각하는 개념들(사용자의 멘탈 모델)을 그대로 코드에 반영할 수 있다.
(+ 코드의 구조가 도메인의 구조를 따르기 때문에 코드를 이해하기가 훨씬 수월해진다.) - 사용자 모델에 포함된 개념과 규칙은 비교적 변경될 확률이 적다.
➙ 그런 도메인의 '본질적인' 측면을 누구보다 잘 이해하고 있는 '사용자'의 관점을 반영한다면, 변경에 쉽게 대처할 수 있는 '안정적인 구조'에 따라 소프트웨어를 설계할 수 있게 된다 !
- 사용자가 도메인에 대해 생각하는 개념들(사용자의 멘탈 모델)을 그대로 코드에 반영할 수 있다.
불안정적인 재료: 기능 (cf. 유스케이스)
- 사용자들이 '목표'를 달성할 수 있도록 시스템이 '기능'을 제공하는 것인데,
그런 사용자와 시스템 간에 이뤄지는 상호작용의 흐름을 텍스트로 정리한 것이 "유스케이스(Use Case)"이다 !
- 유스케이스는 사용자의 목표와 관련된 모든 시나리오(scenario)의 집합이다.
(ex) "이자 계산"이라는 유스케이스가 있다
- 시나리오1 : 예금주가 계좌를 선택 후, 당일까지의 이자액 계산.
- 시나리오2 : 예금주가 계좌를 선택 후, 특정 일자까지의 이자액 계산.
➙ 이때 "예금주"처럼 서비스 중에 하나를 요청하는 이해관계자를 일차 액터라고 한다 !
- 사용자 인터페이스와 관련된 세부 정보는 포함해선 안된다.
➙ 자주 변경되지 않는 시스템의 '행위'에 초점을 맞추자 !
➙ 이처럼 사용자 인터페이스를 배제한 유스케이스 형식을 "본질적인 유스케이스(essential use case)"라고 한다.
- 단, 유스케이스는 시스템이 사용자에게 제공해야 하는 기능들을 서술하기 위해 시나리오 형식으로 모아놓은 것일 뿐,
절대 내부적인 설계에 대한 내용을 담고 있는 것이 아니다.
도메인 모델, 유스케이스, 그리고 책임-주도 설계
- 일단 '책임-주도 설계'는 언제부터 시작될까 ?
➙ 먼저, '시스템'을 사용자로부터 전송된 메시지, 즉 사용자에게 필요한 기능을 제공하기 위해 책임을 수행하는 거대한 자율적인 객체로 봐야 한다.
➙ 그 커다란 규모의 책임이 시스템 속 더 작은 크기의 객체들의 협력을 통해 구현되어야 할 때 비로소 '책임-주도 설계'가 시작되는 것이다.
기능(도메인 모델)과 구조(유스케이스)는 어떤 식으로 조화를 ?
도메인 모델은 안정적인 구조를 개념화하기 위해, 유스케이스는 불안정한 기능을 서술하기 위해 사용되는 도구이다.
- '유스케이스'는 어떤 역할 ?
➙ 앞서 말한 '협력'의 출발점은 시스템의 '기능'을 시스템의 '책임'으로 바꿨을 때인데,
이렇게 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공해준다 !
- '도메인 모델'은 어떤 역할 ?
➙ 앞서 말했듯이 기능을 수용하기 위해 은유할 수 있는 안정적인 구조를 제공한다.
- '책임-주도 설계'는
유스케이스로부터 첫 번째 메시지와 사용자가 달성하려는 목표(필요한 기능)를,
도메인 모델로부터는 기능을 수용(요구사항 변경에 대응)할 수 있는 안정적인 구조를 제공받아
실제로 동작하는 객체들의 협력 공동체를 창조한다.
➙ 유스케이스에서 출발해 객체들의 협력으로 이어지는 일련의 흐름은 객체 안에 다른 객체를 포함하는 재귀적 합성이라는 객체지향의 기본 개념을 잘 보여준다 !
- 여기서 중요한 것은 사용자의 관점에서 시스템의 기능을 명시하고(유스케이스),
사용자와 설계자가 공유하는 안정적인 구조(도메인 모델)를 기반으로 (시스템의) 기능을 책임으로 변환하는 절차를 따라야 한다는 것이다 !
➙ 꼭, 유스케이스와 도메인 모델이 필요하다는 것이 아니다 !
- 책임 할당의 기본 원칙은 책임을 수행하는 데 필요한 정보를 가진 객체에게 그 책임을 할당하는 것이다.
(ex) "이자"를 계산하는 책임을 가진 객체는 "이자율"이 되고, 따라서 "이자"는 "이자율"에 의해 생성된다.
➙ 상태와 행동을 함께 '캡슐화'하는 자율적인 객체를 생성 가능하다.
➙ 도메인 모델에 명시된 "이자" 외에도 "정기예금(TimeDeposit)"이나 "계좌(Account)"와 같은 실세계에선 '수동적인' 개념이 소프트웨어 객체로 구현될 땐 스스로 상태나 행위를 간주하는 자율적인 객체로 간주한다는 점이 중요하다.
- 도메인 모델이 안정적인 이유 ?
➙ 도메인 모델을 구성하는 개념 자체가 잘 변하지 않는 개념이며, 개념 간의 관계 또한 비즈니스 규칙을 기반으로 하기 때문이다.
(ex) "정기예금" 도메인에서 "정기예금", "계좌", "이자율", "이자" 같은 개념은 해당 정책이 사라지거나 크게 개편되지 않는 이상 개념이 변동되기 힘들다.
➙ 따라서, 비즈니스의 핵심 정책이나 규칙이 변경되지 않는 한 '안정적인 도메인 모델'을 기반으로 시스템을 구현할 경우엔 전체적인 구조가 한 번에 흔들리지는 않는다 !
(ex) 이자를 계산하는 방식이 변경("복리 이자 방식 추가")되는 경우
- 시스템에서 변경을 수용하는 과정
1. 기존의 "단리 이자"만 계산하던 InterestRate 클래스를 추상 클래스로 변경
2. SimpleInterest(단리 이자 계산)와 CompoundInterest(복리 이자 계산)가 InterestRate를 상속받도록 한다.
3. 기존에 InterestRate에 정의된 createInterest() 메시지를 수신할 경우 이제 객체의 타입에 따라 실행될 메서드가 선택될 수 있게 한다.(객체의 타입이 SimpleInterest인 경우와 CompoundInterest인 경우)
🔻 이처럼 인터페이스를 정의하는 추상 클래스와 인터페이스를 구현하는 구체적인 클래스 간의 상속 관계는 클래스 기반의 객체지향 언어에서 다형성을 구현하는 가장 기본적인 방법이다.
- "연결 완전성"
: 도메인 모델링에서 사용한 객체와 개념을 프로그래밍 설계에서의 객체와 클래스로 매끄럽게 변환되는 특성.
➙ 역으로 코드에서 모델로의 매끄러운 흐름을 의미하는 것이 "가역성(reversibility)"이다.
코드를 통해 도메인 모델을 유추할 수 있게 하는 것이 도메인 모델의 진정한 목표 !
🔻 예를 들어, 시스템 내부에서 변경된 코드로부터 이자를 계산하는 방식이 어떻게 변경되었는지 등 개념적인 도메인 모델의 구조를 유추하는 것이 가능하다.
▼ Study
'유스케이스' 개념은 어디까지 적용되는 것일까 ?
- 책에서 언급한 '유스케이스'의 역할과 특성적인 측면을 생각해보면 될 것 같다.
- 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공한다.
- 유스케이스는 시스템이 사용자에게 제공해야 하는 기능들을 서술하기 위해 시나리오 형식으로 모아놓은 것일 뿐,
절대 내부적인 설계에 대한 내용을 담고 있는 것이 아니다.
- 사용자에게 제공할 기능을 시스템의 책임으로 보게 함으로써 객체 간의 안정적인 구조에 책임을 분배할 수 있는 출발점을 제공한다.
➙ 이처럼 유스케이스는 단순하게 사용자가 필요로 하는 기능의 명세, 즉 그림을 그리기 전 "스케치"를 해두는 느낌이다.
➙ 세부적인 구조에서까지 유스케이스 개념을 대입해서 생각하는 것은 적절하지 않은 것 같다 !
Issue_1
유스케이스에서 출발해 객체들의 협력으로 이어지는 일련의 흐름은 객체 안에 다른 객체를 포함하는 재귀적 합성이라는 객체지향의 기본 개념을 잘 보여준다.
"재귀적 합성"이라는 개념과 "유스케이스" 사이에 어떤 관련성이 있는지 잘 모르겠습니다.
- "재귀적 합성"이라는 개념은 객체끼리의 협력, 즉 '메시지'의 연쇄적인 흐름을 의미하는 것 같습니다.
➙ '유스케이스'가 사용자의 기능을 시스템의 기능으로 바라보게 하여 '책임-주도 설계'의 출발점을 찍는다는 점에서 유스케이스는 "재귀적 합성"의 출발점이라고 연관 지을 수 있을 것 같습니다.
(아래는 여려 유스케이스 사이의 관계(포함, 확장, 일반화)를 나타내는 그림입니다.)
Issue_2
이상한 나라의 앨리스 상황에서는
유스케이스 명 : 재판하라
일차 액터 : 왕에게 말한 누군가
주요 성공 시나리오 :
- 왕이 증인을 부른다.
- 증인이 증언한다.
- 왕이 판단한다.
이렇게 할 수 있을까요?
- 네. 이상한 나라의 앨리스를 하나의 '시스템'이라고 생각하면 적절하게 은유한 것 같습니다.
하지만, '유스케이스'는 사용자와 기능 간의 상호작용의 흐름을 보기 편하게 만들어주는 도구이기 때문에, "이상한 나라의 앨리스"라는 이야기에서 유스케이스를 생각해낸다는 점은 모순이 있는 것 같습니다.
또한, 책에서도 굳이 언급하지 않은 이유도 이와 관련이 있을 것 같습니다. - 아래에 첨부한 이미지를 살펴보면 이해에 도움될 것 같습니다.
➙ "수강 신청"이라는 거대한 시스템 내에서 "학생", "교수", "교직원"이라는 '액터(actor)'가 있습니다.
그리고 각 액터들이 요구하는 기능이 "수강신청", "수강취소", "로그인" 등이 있는 것이고, 각각의 기능들과 액터 간 이뤄지는 상호작용의 흐름을 시나리오 형식으로 작성해낸 것이 '유스케이스(use case)'라고 하는 것입니다.
➙ "학생"에게 제공하는 기능 중 하나인 "수강신청"을 예로 들면,
<"강좌를 선택한다" → "선택한 강좌를 수강신청한다">라는 간단한 시나리오가 도출될 수 있고, 이러한 시나리오들이 모여 "수강신청"이라는 하나의 '유스케이스'가 작성되는 것입니다.
➙ 따라서, 액터들과 유스케이스를 다이어그램으로 나타낸 "유스케이스 다이어그램(use case diagram)"입니다.
Issue_3
Chapter6에 제시된 은행 예시를 전체적으로 함께 살펴보면 좋겠습니다!
- 스터디에서 이야기한 내용을 정리하면 아래와 같을 것 같습니다. (cf. 첨부한 그림)
- 사용자가 "정기예금"이라는 도메인을 바라보는 관점을 개념화한 '도메인 모델'로부터 안정적인 '구조'를 설계합니다.
- 사용자가 원하는 기능인 "중도 해지 이자액을 계산한다."라는 '유즈케이스'로부터 시스템의 '기능'을 추출합니다.
- 안정적인 '구조' 속에서 해당 '기능'을 시스템의 거대한 '책임'으로 보고 '책임-주도 설계'에 따라 시스템 속에서 거대한 '책임'을 세분화하며 '협력', '책임', '역할'을 구성해나갑니다.
➙ 이 과정에서 어떤 객체를 선택할 것인지를 결정할 때 '도메인 모델'에 포함된 개념인 "정기예금", "이자", "계좌", "이자율"을 은유하는 소프트웨어 객체를 선택하는 것이 중요합니다. - 이러한 과정에 따라 '도메인 모델'을 '코드'로 구현해낸다.
➙ 이번 챕터의 키포인트라고 생각합니다.
- 사용자가 "정기예금"이라는 도메인을 바라보는 관점을 개념화한 '도메인 모델'로부터 안정적인 '구조'를 설계합니다.