TIL

Chapter10 이벤트

10.1 시스템 간 강결합 문제

주문을 취소하면 환불 처리를 해야하는 예시

public class CancelOrderService {
	private final RefundService refundService;

	@Transactional
	public void cancel(OrderNo orderNo) {
		// ...
		refundService.refund(order.getPaymentId());
		// ...
	}
}

10.2 이벤트 개요

10.2.1 이벤트 관련 구성요소

10.2.2 이벤트의 구성

10.2.3 이벤트 용도

10.2.4 이벤트 장점

10.3 이벤트, 핸들러, 디스패처 구현

10.3.1 이벤트 클래스

public class OrderCancedEvent {
	private String orderNumber;

	// 생성자, getter ...
}

10.3.2 Events 클래스와 ApplicationEventPublisher

10.3.3 이벤트 발생과 이벤트 핸들러

10.3.4 흐름 정리

  1. 도메인 기능을 실행
  2. 도메인 기능은 Events.raise()를 이용해 이벤트 발생시킴
  3. Events.raise()ApplicationEventPublisher를 이용해 이벤트 출판
  4. ApplicationEventPublisher@EventListener 메서드를 찾아 실행
    • 이렇게만 처리하면 이벤트 발생과 이벤트 처리가 같은 트랜잭션 범위 내에서 실행된다.

10.4 동기 이벤트 처리 문제

10.5 비동기 이벤트 처리

10.5.1 로컬 핸들러 비동기 실행

10.5.2 메시징 시스템을 이용한 비동기 구현

10.5.3 이벤트 저장소를 이용한 비동기 처리

10.6 이벤트 적용 시 추가 고려 사항

  1. 이벤트에 이벤트 소스를 추가할 것인지 고려해야 한다.
    • ‘Order가 발생시킨 이벤트만 조회하기’처럼 특정 주체에 대한 이벤트만 읽고 싶으면 이벤트에 발생 주체 정보를 추가해야 한다.
  2. 포워더에서 전송 실패를 얼마나 허용할 것인지 고려해야 한다.
    • 포워더는 이벤트 전송에 실패하면 실패한 이벤트부터 다시 읽어 전송을 시도한다.
    • 특정 이벤트에서 계속 전송이 실패하면 나머지 이벤트는 계속 전송할 수 없게 된다.
    • 실패한 이벤트의 재전송 횟수 제한을 두면 해결할 수 있다.
    • 처리에 실패한 이벤트를 별도 저장소에 저장하면 실패 이유 분석이나 후처리에 도움이 된다.
  3. 이벤트 손실에 대해서 고려해야 한다.
    • 이벤트 저장소를 사용하는 방식은 이벤트 발생과 저장을 한 트랜잭션으로 처리하기에 손실을 막을 수 있다.
    • 반면 로컬 핸들러를 이용한 비동기 처리에선 이벤트 처리에 실패하면 이벤트를 유실하게 된다.
  4. 이벤트 순서에 대해서 고려해야 한다.
    • 이벤트 발생 순서대로 외부 시스템에 전달해야 한다면 이벤트 저장소를 사용하는 것이 좋다.
    • 반면 메시징 시스템은 기술에 따라 발생 순서와 전달 순서가 다를 수도 있다.
  5. 이벤트 재처리에 대해 고려해야 한다.
    • 동일한 이벤트를 다시 처리해야 할 때 어떻게 할지 결정해야 한다.
    • 마지막 처리한 이벤트 순번을 기억해 두었다가 이미 처리한 순번 이벤트가 도착하면 무시하는 방법이 가장 쉽다.
    • 이벤트 핸들러가 멱등성을 가진다면 이벤트 중복 발생이나 중복 처리에 대한 부담이 없다.

10.6.1 이벤트 처리와 DB 트랜잭션 고려

이벤트를 사용할 때 동기로 하든 비동기로 하든 이벤트 처리 실패와 트랜잭션 실패를 함께 고려해야 한다.