이번 장에서 하려는 목표
9.2 서비스 함수를 메시지 핸들러로 리팩터링하기
그전 섹션에서 설명이 나와있지만 기존에 서비스에서 하던걸 전부 핸들러로 바꾼다 어차피 내부적으로 이벤트발생으로 처리하는 거나 엔드포인트에서 핸들러로 처리하는 거나 로직이 차이가 없음. 그리고 여기서 보면 엔드포인트에서 원시값으로 서비스 함수를 호출하던 게 핸들러로 바뀌면서 이벤트 객체로 묶여서 넘김. 그럼 저번에 언급했던 부분이 생각이 남. 객체로 보내던걸 원시 타입으로 바꿨는데 그걸 다시 객체로 묶으면 의존성이 생기는 거 아니냐? 그거에 대한 답변을 책에서 해줌
> 도메인 객체에서 기본 타입에 대한 집착을 거쳐 인터페이스로 이벤트를 사용하기까지
> OO사이클에서 사람들은 기본 타입에 대한 집착을 안티패턴으로 간주한다. 공개 API에서 기본 타입을 피하고, 커스텀 값 클래스로 기본 타입 값을 감싸라고 이야기한다. 파이썬 세계에서는 이를 함수 파라미터 타입을 고르는 대략적인 규칙으로 활용하는 것에 대해 많은 사람이 회의적이다. 기본 타입에 대한 집착은 분명 아무 생각 없이 적용하면 불필요한 복잡도를 추가하는 패턴임. 따라서 여기서 한일(함수 파라미터를 도메인 객체에서 기본 타입으로 바꿈) 자체는 그 자체로는 복잡도를 추가하는 일이 아님.
> 파라미터를 도메인 객체에서 기본 타입으로 바꾸면 연결을 멋지게 끊을 수 있음. 클라이언트 코드는 더 이상 도메인과 직접 묶이지 않고, 그에 따라 모델을 변경해도 서비스 계층은 API를 변경하지 않고 예전과 같이 그대로 제공 가능. 반대로 API가 변경되어도 모델은 그대로 남길 수 있음.
> 그렇다면 이벤트 도입으로 반대방향으로 다시 돌아가는 것인가? 하지만 핵심 도메인 모델은 전혀 다른 계층과 상관없이 바뀔 수 있음. 이벤트 도입은 외부 세계와 이벤트 클래스를 연결할 뿐임. 이벤트도 도메인의 일부분이지만. 덜 바뀔 것이라고 예상하면 어느 정도 타당하다고 함.
> 이벤트를 도입하면 어떤 이익이 있을까? 애플리케이션의 어떤 유스 케이스를 호출할 때 기본 타입들의 특정 조합을 기억할 필요가 없음. 단지 애플리케이샨에 대한 입력을 표현하는 단일 이벤트 클래스만 사용하면 됨.
9.2.1 메시지 버스는 이제 이벤트를 UoW로 부터 수집한다
서비스 계층에서 메시지 버스가 직접 핸들러를 호출하니 이벤트 핸들러가 UoW가 필요하게 됨. 또한 메시지 버스가 명시적으로 새 이벤트를 수집하고 처리하는 것을 담당. 현재는 UoW와 메시지 버스 사이에 순환적 의존성이 있다고 함 이걸 단방향으로 바꿔야 함
UoW가 직접 이벤트를 넣지 않음. 커밋이 일어나도 더 이상 publish_events를 자동으로 호출하지 않음. 이벤트 제공만 함.
9.2.3 결과를 반환해야 하는 메시지 버스
메시지 버스 핸들 함수 보면 결과를 출력해야 함. 서비스 계층이 할당된 배치에 대한 참조를 알고 싶기 때문임. 그니까 호출한 곳에서 그거 참조를 알아야 된다는 말임. 이렇게 된 건 시스템에서 읽기랑 쓰기가 혼재되어 있어서 그렇다 함. 이걸 12장에서 다룬다는데 그게 CQRS 같음
```python
class AbstractUnitOfWork(abc.ABC):
products: repository.AbstractRepository
def __enter__(self) -> AbstractUnitOfWork:
return self
def __exit__(self, *args):
self.rollback()
def commit(self):
self._commit()
def collect_new_events(self):
for product in self.products.seen:
while product.events:
yield product.events.pop(0)
```
9.3 새로운 요구 사항 구현
BatchQuantityChanged 이벤트의 경우 내부에서 다시 alloctionrequired 이벤트를 발생시킴. 여기서는 다음과 같은 시퀀스 다이어그램을 가짐
근데 여기서 주의해야 할게 위처럼 두 단위의 UoW에 걸쳐 나누면 DB 트랜잭션이 2개가 생김 따라서 데이터 정합성 문제가 발생함. 첫 트랜잭션이 끝났지만 두 번째 트랜잭션이 끝나지 않아서 생기는 문제임. 이 문제 처리는 14장에서 다룸
9.5 가짜 메시지 버스와 독립적으로 이벤트 핸들러 단위 테스트하기
다른 장들은 새 기능에 대해 구체적으로 어떻게 구현했는지 나와있어서 정리 스킵하고 이 9.5절에서는 테스트 관련 이야기를 함. 주제는 기존에 보여준 에지투에지 테스트 말고, 만약 다른 핸들러와 독립적으로 일부 핸들러를 테스트하고 싶을 땐 어떻게 하냐에 대한 이야기다.
가짜를 만들어서 설계하는데 FakeUnitOfWork의 publish_events()를 수정해서 실제 메시지 버스와 분리할 수 있다. 이벤트 버스에 이벤트를 넣는 대신 그냥 발생시킨 이벤트를 리스트에 저장함. 그니까 기존에 handle에서 uow에서 이벤트가 추가되는 부분이 없어지고 핸들러만 실행되게 만든다는 소리임. 책에서는 메서드 이름이 이전 publish_events로 되어있는데 이전 부분 말하는 거 같다 이번장에서는 collect_new_events로 바뀌었음. 아무튼 핸들러 실행하게 하고 이벤트가 발생되는지만 체크하게 분리한다는 거임. 근데 이렇게 하면 복잡해지니까. 일단 에지투에지부터 하고 나중에 필요할 때 구현하라고 함.
그리고 위에 언급한 fakeUoW는 복잡하기 때문에 그냥 추상 messageBus를 만들고 각각 진짜 메시지 버스, 가짜 메시지버스를 만들어서 구현하는 게 낫다고 함. 예전 레포지토리처럼.
9.6 마치며
9.6.2 왜 이렇게 시스템을 변경했는가?
이런 아키텍처 패턴을 채택한 목적은 애플리케이션의 크기가 커지는 속도보다 복잡도가 증가하는 속도를 느리게 만들기 위해서임. 메시지 버스에 모든 것을 실으면 아키텍처의 복잡도 측면에서는 비용을 지불함. 필요한 작업을 수행하기 위해 더 이상 개념적으로나 아키텍처적으로 코드를 변경할 필요 없이 복잡한 요구 사항 대부분을 거의 다 처리할 수 있는 패턴을 얻게 됨. 여기서 추가한 유스케이스가 복잡한 편이라고 함. 근데 아키텍처 측면에서는 변한 게 없음. 단지 이벤트, 핸들러만 추가됨. 아키텍처의 물건에 속하는 것이기 때문에 구조상 복잡도는 변함이 없음.
참고 및 출처: <파이썬으로 살펴보는 아키텍처 패턴: TDD, DDD, EDM 적용하기)> (해리 퍼시벌, 밥 그레고리 지음, 오현석 옮김, 한빛미디어 , 2021)
'Book' 카테고리의 다른 글
명령-질의 책임 분리(CQRS) (0) | 2023.11.03 |
---|---|
커맨드와 커맨드 핸들러 (0) | 2023.10.29 |
오브젝트. Chapter 01 객체,설계 (0) | 2023.10.18 |
애그리게이트와 일관성 경계 (0) | 2023.10.08 |
작업 단위 패턴 (0) | 2023.10.05 |