책 리뷰/도메인 주도 개발하기

10장 이벤트

곰탁 2022. 7. 24. 00:19

10.1 시스템 간 강결합 문제

 

바운디드 컨텍스트간의 강결합을 이벤트를 사용하면 해결 할 수 있다. 특히 비동기 이벤트를 사용하면 두 시스템 간의 결합을 크게 낮출 수 있다. 한번 익숙해지면 모든 연동을 이벤트와 비동기로 처리하고 싶을 정도로 강력하고 매력적인 것이 이벤트다.

 

10.2 이벤트 개요

이벤트라는 용어는 '과거에 벌어진 어떤 것'을 의미한다.

이벤트는 발생하는 것에서 끝나지 않는다. 이벤트가 발생하면 그 이벤트에 반응하여 원하는 동작을 수행하는 기능을 구현한다.

 

도메인 모델에 이벤트를 도입하려면 네 개의 구성요소인 이벤트, 이벤트 생성 주체, 이벤트 디스패처(퍼블리셔), 이벤트 핸들러(구독자) 를 구현해야 한다.

 

이벤트는 발생한 이벤트의 정보를 담는다.

  • 이벤트 종류 : 클래스 이름으로 종류를 표현
  • 이벤트 발생 시간
  • 추가 데이터 : 주문번호, 신규 배송지 정보 등 이벤트와 관련된 정보

이벤트 용도

이벤트는 크게 두 가지 용도로 쓰인다.

트리거

 - 도메인 상태가 바뀔 때 다른 후처리가 필요하면 후처리를 실행하기 위한 트리거로 이벤트를 사용.

서로 다른 시스템간의 데이터 동기화

 - 외부 서비스에 바뀐 정보를 전송해야 할때, 변경 이벤트를 발생시키고 외부 서비스와 정보를 동기화 할 수 있다.

 

이벤트 장점

서로 다른 도메인 로직이 섞이는 것을 방지할 수 있다.

 

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

  • 이벤트 클래스 : 이벤트를 표현한다.
  • 디스패처 : 스프링이 제공하는 ApplicationEventPublisher를 이용한다.
  • Events : 이벤트를 발생한다. 이벤트 발행을 위해 ApplicationEventPublisher를 사용한다.
  • 이벤트 핸들로 : 이벤트를 수신하여 처리한다. 스프링이 제공하는 기능을 사용한다.

이벤트 자체를 위한 상위 타입은 존재하지 않는다. 원하는 클래스를 이벤트로 사용하면 된다.

이벤트는 과거에 벌어진 상태 변화나 사건을 의미하므로 이벤트 클래스의 이름을 결정할 때에는 과거 시제를 사용해야 한다는 점만 유의하면 된다.

 

10.4 동기 이벤트 처리 문제

외부 시스템과의 연동을 동기로 처리할 때 발생하는 성능과 트랜잭션 범위 문제를 해소하는 방법은 이벤트를 비동기로 처리하거나 이벤트와 트랜잭션 연계하는 것이다.

 

10.5 비동기 이벤트 처리

  • 로컬 핸들러를 비동기로 실행하기
  • 메시지 큐를 사용하기
  • 이벤트 저장소와 이벤트 포워더 사용하기
  • 이벤트 저장소와 이벤트 제공 API 사용하기

 

@EnableAsync 애너테이션은 스프링 비동기 실행 기능을 활성화 한다.

 

또다른 방법은 카프카나 래빗MQ와 같은 메시징 시스템을 사용하는 것이다.

메시지 큐를 사용하면 보통 이벤트를 발생시키는 주체와 이벤트 핸들러가 별도 프로세스에서 동작한다.

 

이벤트를 비동기로 처리하는 또 다른 방법은 이벤트를 일단 DB에 저장한 뒤에 별도 프로그램을 이용해서 이벤트 핸들러에 전달하는 방법이다.

 

포워더는 주기적으로 이벤트 저장소에서 이벤트를 가져와 이벤트 핸들러를 실행한다. 포워더는 별도 스레드를 이용하기 때문에 이벤트 발행과 처리가 비동기로 처리된다.

 

이벤트 저장소를 이용한 두 번째 방법은 외부에 API를 제공하여 사용하는 것이다.

API 방식과 포워더 방식의 차이점은 이벤트를 전달하는 방식에 있다.  포워더 방식은 이벤트를 어디까지 처리했는지 추적하는 역할이 포워더에 있다면 API 방식에서는 이벤트 목록을 요구하는 외부 핸들러가 자신이 어디까지 이벤트를 처리했는지 기억해야 한다.

 

이벤트 저장소 구현

  • EventEntity : 이벤트 저장소에 보관할 데이터
  • EventStore : 이벤트를 저장하고 조회하는 인터페이스 제공.
  • JdbcEventStore : JDBC를 이용한 EventStroe 구현체
  • EventApi : REST API 를 이용해서 이벤트 목록을 제공하는 컨트롤러

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

1. 이벤트 소스를 EventEntry에 추가할지 여부.

- 이벤트 발생 주체에 대한 정보를 갖지 않는다.

2. 포워더에서 전송 실패를 얼마나 허용할 것이냐.

- 이벤트 전송에 실패하면 실패한 이벤트부터 다시 읽어와 전송을 시도한다. 하지만 특정 이벤트 에서 계속 전송에 실패하면 문제가 생기기 때문에 실패한 이벤트의 재전송 횟수 제한을 두어야 한다. 생략하고 넘어가는 등.

3. 이벤트 손실

4. 이벤트 순서

5. 이벤트 재처리에 대한 것

 - 멱등성

 

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

@TransactionalEventListener 애너태이션을 이용하여 db트랜잭션이 다 일어난 후 이벤트를 발생 시킬 수 있다.