책 흐름:
도메인에 집중한 초기 모델 -> 복잡해지는 코드로 변모 --> 도메인 이벤트 패턴과 메시지 버스 패턴 적용
-> 카카오 메시지 발송은 HTTP 계층이 처리해야할 영역이 아님
-> 괜히 유닛테스트만 까다로워 짐(귀찮아짐)
-> 단일 책임 원칙(SRP) 위배
모델 메소드 명 def to_complete
이 def to_complete_and_send_kakao_message
가 됨
위의 2️⃣번과 상황이 달라지지 않는다.
즉, 도메인에 집중하지 못하고 코드 베이스가 더러워진다.
SRP, Single Responsibility Principle
class MatchApply(models.Model):
(중략)
def to_complete(self, *args, **kwargs):
(생략)
vs
class MatchApply(models.Model):
(중략)
def to_complete_and_send_kakao_message(self, *args, **kwargs):
(생략)
then
이나 and
라는 단어를 사용하지 않고 함수가 하는 일을 설명할 수 없다면 SRP를 위반하고 있을 가능성이 높다.
이제 도메인 이벤트 패턴
과 메시지 버스 패턴
이 도입된다.
몇 가지 구현 원칙은 이렇다.
⓵ 도메인 모델은 이벤트를 기록한다.
⓶ 이벤트는 값 객체다. 순수 데이터 구조로써, 엔티티가 아니다.
⓷ 모델이 이벤트를 발행(publish)한다. 이벤트는 메시지 버스에 발행되는 것이다.
⓸ 핸들러(Subscriber)는 메시지 버스로부터 이벤트를 구독한다.
class MatchApply(models.Model):
(중략)
def to_complete(self, *args, **kwargs):
# 매치 신청 완료 처리
(생략)
# 매치 진행 가능
messagebus = MessageBus(events.MatchConfirmTalk)
messagebus.handle()
"X일 때는 Y를 합시다"
라는 마법의 말은 종종 시스템에 구체적으로 만들 수 있는 이벤트를 뜻한다.
이번 챕터를 통해 배운 점은
도메인 모델을 이루는 수많은 주요 함수들이 SRP를 위반하고 있었다는 것이다.
핵심(X)와 부차적인 요소(Y)를 분리하는 것을 천천히 도입해보려 한다.
SOLID는 항상 지켜야하는 절대적인 법칙은 아니지만, 코드의 확장성 및 유지보수에 도움이 된다고 한다.
이 원칙을 염두에 두고 리팩터링을 진행해보려 한다.
Composite over inheritance
캡슐화
p.56 객체는 상태를 캡슐 안에 감춰둔 채 외부로 노출하지 않는다. 객체가 외부에 노출하는 것은 행동뿐이며, 외부에서 객체에 접근할 수 있는 유일한 방법 역시 행동 뿐이다.
p.94 데이터의 내부 표현 방식과 무관하게 행동만이 고려 대상이라는 사실은 외부에 데이터를 감춰야 한다는 것을 의미한다. 따라서 훌륭한 객체지향 설계는 외부에 행동만을 제공하고 데이터는 행동 뒤로 감춰야 한다. 이 원칙을 흔히 캡슐화라고 한다. 공용 인터페이스 뒤로 데이터를 캡슐화하라는 오래된 격언은 객체를 행동에 따라 분류하기 위해 지켜야 하는 기본적인 원칙이다.
p. 170 객체의 자율성을 보존하기 위해 구현을 외부로부터 감추는 것을 캡슐화라고 한다. 객체는 상태와 행위를 함께 캡슐화함으로써 충분히 협력적이고 만족으러울 정도로 자율적인 존재가 될 수 있다. 캡슐화를 정보 은닉(information hiding)이라고 부르기도 한다.
상위 타입의 객체를 하위 타입의 객체로 치환해도
프로그램은 정상적으로 동작해야 한다.
리스코프 치환 원칙이 지켜지지 않으면 개방 폐쇄 원칙을 위반하게 되므로 기능 확장을 위해 더 많은 부분을 수정해야 합니다. 이것은 확장을 어렵게 하는 것이죠. 따라서, 우리는 상속을 잘 정의하여 치환 가능성을 위배되지 않도록 설계해야 합니다.