Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions chap10/송영민.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# DDD 10장

## 10-1. 시스템 간 강결합 문제
- 여러 바운디드 컨텍스트의 도메인 로직이 강결합되어있으면 다음과 같은 문제가 발생할 수 있다.
- 새로운 기능을 추가할 때, 여러 도메인간의 도메인 로직이 뒤섞이게 된다.
- 내가 처리하고자 하는 도메인 로직의 성능이 타 외부 서비스(시스템)에 의해 저하된다.
- 여러 서비스들을 같이 처리할 경우, 서로간의 트랜잭션 롤백 전략 등을 어떻게 가져갈지 고민하게 된다.
- 이를 해결하기 위해 **이벤트**를 사용하여 서비스간의 강결합을 줄일 수 있다.
- 특히 **비동기 이벤트**를 사용하면 이 결합을 크게 낮출 수 있다.

## 10-2. 이벤트 개요
- 여기서 이벤트란 "과거에 벌어진 어떤 것"을 의미한다.
- 예를 들어 사용자가 암호를 변경했을 때, "암호를 변경함"이 이벤트이다.
- 이벤트에는 다음과 같은 구성요소가 있다.
- 이벤트 생성 주체 : 이벤트를 발행하는 주체로 엔티티, 벨류, 도메인 서비스 등 도메인 객체이다.
- 이벤트 디스패처 : 이벤트 생성 주체가 발행한 이벤트를 이벤트 핸들러로 전달한다.
- 이벤트 핸들러 : 이벤트 생성 주체가 발행한 이벤트에 구독(반응) 한다.
- 예를 들어, 암호를 변경했을때, User 엔티티의 changePassword함수는 UserPasswordChangedEvent 를 발행한다.
- 이때 UserChangedPasswordEvent를 구독한 이벤트 핸들러에서 메일을 보낸다거나 하는 행위를 할 수 잇다.
- 이벤트는 크게 두가지 용도가 있다.
- 트리거 : 도메인의 상태가 바뀔 때 후처리 용도로 사용할 수 있다. (ex. 주문 취소 시 결제 환불을 실행할때 그 트리거)
- 데이터 동기화 : 서로 다른 시스템간의 데이터를 동기화한다. 주문 도메인이 배송지 변경을 하면, 배송 서비스에게 배송지 변경을 알려주는 이벤트를 발행할 수 있다.

## 10-3. 이벤트, 핸들러, 디스패쳐 구현
- 이벤트 클래스: 이벤트를 표현하는 클래스이다.
- 상속을 받아도 된다. 딱히 최상위 클래스가 상속받아야하는 클래스는 없다. (스프링에선)
- 디스패쳐 : 스프링이 ApplicationEventPublisher를 제공한다.
- 빈 주입 없이 사용하기 위해 static 필드로 EventPublisher를 두고, Configuration 클래스에서 주입해줄 수 있다.
- 이 클래스(Event)의 스태틱 함수 raise()를 통해 빈이 아닌 클래스에서 이벤트를 발행할 수 있다.
- 이벤트 핸들러 : 스프링이 제공하는 EventHandler를 사용한다.

## 10-4. 동기 이벤트 처리 문제
- 기본적으로 ApplicationEventPublisher는 동기로 동작한다. 즉 EventHandler에서 발생한 오류가 raise를 호출한 시점에서도 전파될 수 있다.
- 또한 이벤트 핸들러가 동작해야 다음으로 넘어가기에, 기존의 성능 문제 역시 그대로 발생한다.
- 따라서 비동기 처리를 하거나, 이벤트와 트랜잭션을 연계할 수 있다.

## 10-5. 비동기 이벤트 처리
- 주문을 취소하자마자 결제를 취소할 필요는 없다. 나중에 취소되어도 문제 없다.
- 회원가입 후 메일 발송도 마찬가지이다. 이걸 비동기로 처리해도 되는지 확인하는 방법은,
- 'A하면 일정 시간 안에 B하라'로 바꿀 수 있는 요구사항은 비동기로 구현할 수 있다.
- 방법은 크게 4가지가 있다.
- 로컬 핸들러를 비동기로 실행하기. (@Async 어노테이션)
- 메세지 큐 이용하기
- 이 경우 트랜잭션으로 묶여야 한다면 글로벌 트랜잭션(혹은 분산 트랜잭션)이 필요하다.
- 이벤트 저장소와 이벤트 포워더 사용하기
- 일단 이벤트를 디비에 저장하고, 이벤트 포워더라는 프로세스가 주기적으로 쌓인 이벤트를 추적해서 실행한다.
- 이벤트 저장소와 이벤트 제공 API 사용하기.
- 외부 핸들러가 API서버로 이벤트 목록을 가져간다.
- 포워더는 추적을 포워더가 한다면, API 방식에서는 이벤트 핸들러가 어디까지 이벤트 읽었는지 추적해야한다.

## 10-6. 이벤트 적용시 추가 고려 사항
- 이벤트를 구현할때 이벤트 소스 (누가 발행햇는지?)를 조회할 수 없다. 필요하다면 추가해야한다.
- 포워더의 전송 실패를 얼마나 허용하느냐? 도 고려해야한다. 계속 하나 실패해서 뒤에게 밀리면 안되기 때문
- 이벤트가 손실될 때 (처리하지 못했을때)도 고려해야 한다.
- 이벤트 발생 순서대로 실행이 필요하다면, 이벤트 저장소를 사용하거나 메세징큐중 순서 보장되는걸 써야한다.
- 이벤트 재처리가 요청왔을때 어떻게 핸들링하는가 (동일한 순번의 이벤트가 오는경우 등)
- TransactionalEventListener로 트랜잭션이 성공할때만 이벤트가 실행되게 하면 좋다.