페어와 함께 생각해본 설계도를 도식화해보면 위와 같습니다. 여기에서의 각 Entity
는 다음과 같은 역할을 합니다
LottoGameManager : 게임을 시작하고, Domain
과 View
를 셋팅하는 역할을 수행합니다. 도메인의 메소드를 직접 호출하여 데이터를 변형하고, 뷰의 렌더링 메소드를 직접 호출하여 화면을 그립니다
LottoGameView : 새로운 도메인과 함께 화면을 그려냅니다. Domain
과 분리되어 있으나, render method
들이 Domain
형식에 종속적입니다.
LottoGameModel : 게임에 사용되는 도메인을 관리합니다. 데이터를 변형하는 메소드를 가지고 있습니다.
LottoGameManager가 도메인과 뷰의 메소드를 직접 호출하고 있습니다.
// ... LottoGameManager Class
onSubmitChargeInputForm = (e) => {
e.preventDefault();
try {
const { value: chargeInputStr } = this.$chargeInput;
const chargeInput = Number(chargeInputStr);
this.triggerChargeInputAction(chargeInput);
} catch ({ message }) {
alert(message);
}
};
triggerChargeInputAction(chargeInput) {
// 직접 도메인의 메소드를 호출하여 변경합니다.
this.lottoGameModel.createLottoList(chargeInput);
const lottoList = this.lottoGameModel.getLottoList();
// 직접 뷰의 메소드를 호출하여 변경합니다.
this.lottoGameView.renderLottoSection(lottoList);
}
이 경우 다음과 같은 문제점들이 발생할 수 있습니다.
1. 뷰나 도메인의 메소드 명 또는 인자의 수가 변경되는 경우 LottoGameManager에서도 수정해주어야합니다.
개인적으로 변경하는 곳이 많아질수록 파급효과가 커진다고 생각합니다. (휴먼에러 등)
2. 도메인과 뷰는 자신이 할 일을 스스로 하지 못하는 애송이가 됩니다.
도메인과 뷰에 어떤 일을 해달라는 요청이오면, 그에 맞게 스스로 할일을 수행해야하는데 지금의 코드에선 할일을 다른 클래스
가 지시하고 수행시킵니다. ( 객체자체가 능동적이지 못하고, 독립적이라 할 수 없습니다.)
사진에서 보이듯 이번 프로젝트의 목표는 UI와 도메인 영역을 분리하는 것입니다. 도메인과 UI의 분리는 설계 및 구현에서 제대로 수행되었다고 생각되지만, 그들이 각개의 모듈인가를 생각해보면 아닌 것 같습니다. 그렇기 때문에 각 Entity
들이 좀 더 확실한 모듈이 되도록 다시 설계해보았습니다.
도메인과 뷰를 사용하는 입장에서 (LottoGameManager
) 좀 더 선언적으로 도메인과 뷰를 동작하게 하고싶습니다. (지금 처럼 모든 일을 하나하나 찝어주며 시키는게 아니라, 어떤 일을 하세요! 라고 요청하면 그들이 알아서 일을 하도록)
1.Domain
과 UI
가 좀 더 능동적으로 동작했으면
2.Game Manager
에서 선언적인 코드 작성이 가능해졌으면
위 두 아이디어에 집중하여 설계를 다시 해보았습니다.
매니저들은 업무를 지시하거나, 수행하는 역할을 합니다.
LottoGameManager
: DomainManager
- ViewManager
에게 새로운 데이터로 할 일을 전달합니다. 기존의 기능 (DOM select
, Domain, View
가 할일을 모두 수행시키는 기능은 하지않습니다)
DomainManager
: Lotto Game
에서 요구되는 도메인을 관리합니다. LottoGameManager
로 부터 할일과 데이터(사용자가 입력하는)를 전달받으면, 그에 맞게 도메인 데이터를 변형하는 업무를 수행합니다.
ViewManager
: Lotto Game
의 모든 뷰들을 관리합니다. 마찬가지로 LottoGameManager
로 부터 할일과 데이터를 전달받으면 그에 맞게 화면에 그리는 업무를 세부 뷰들에게 지시합니다.
도메인 데이터를 가지고 있으며, 독립적으로 변형할 수 있는 능력을 갖추었습니다.
Lotto
: 로또 도메인 입니다. lottoNumbers(숫자 6개의 배열)
라는 멤버를 가지고 있습니다.
LottoContainerView
: 다음 사진의 UI와 관련된 업무를 수행합니다.
LottoResultView
: 다음 사진의 UI와 관련된 업무를 수행합니다.
// LottoGameManager Class
triggerChargeInputAction(chargeInput) {
// 도메인에게 새로운 데이터와 할 일에 대한 정보를 가진 액션 키를 넘깁니다.
const lottoList = this.#lottoDomainManager.mutateDomainState({
newData: chargeInput,
actionKey: MUTATE_DOMAIN_KEY.NEW_CHARGE_INPUT,
});
// 뷰에게 새로운 데이터와 할 일에 대한 정보를 가진 액션 키를 넘깁니다.
this.#lottoViewManager.renderView({
newData: lottoList,
actionKey: RENDER_VIEW_KEY.UPDATE_LOTTO_LIST,
});
}
위 와 같이 매니저에서 할 일에 대해 알려주면, 도메인은 다음과 같이 일합니다
// LottoDomainManager Class
mutateDomainState({ newData, actionKey }) {
if (actionKey === MUTATE_DOMAIN_KEY.NEW_CHARGE_INPUT) {
// 새로운 데이터와 함께 할 일을 수행합니다.
this.#createLottoList(newData);
return this.lottoList;
}
}
도메인에서 업데이트 된 새로운 데이터와 함께 할 일 정보가 넘어오면, 뷰는 다음과 같이 일합니다.
// LottoViewManager Class
renderView({ newData, actionKey }) {
if (actionKey === RENDER_VIEW_KEY.UPDATE_LOTTO_LIST) {
// 새로운 데이터와 함께 각 뷰들이 할 일을 명령합니다. (할 일을 수행하는 단계)
this.#containerView.renderLottoSection(newData);
this.#resultView.showWinNumberInputSection();
}
if (actionKey === RENDER_VIEW_KEY.UPDATE_VISIBLE_STATE) {
this.#containerView.renderAlignState(newData);
}
}