10.1 시스템 간 강결합 문제
- 두 바운디드 컨텍스트 간의 강결합 때문에 서로 영향을 받음
-> 이벤트
를 사용해서 해결
- 특히 비동기 이벤트를 사용하면 두 시스템 간의 결합 크게 낮출 수 있음
10.2 이벤트 개요
- 이벤트(event) : 과거에 벌어진 어떤 것
- 이벤트 발생 = 상태가 변경됨
- 도메인의 상태 변경과 관련된 요구사항을 이벤트를 이용해서 구현
10.2.1 이벤트 관련 구성요소
- 이벤트 도입을 위해 필요한 구성요소
- 이벤트
- 이벤트 생성 주체
- 이벤트 디스패치(퍼블리셔)
- 이벤트 핸들러(구독자)
이벤트 생성 주체
- 도메인 모델 관점에서 이벤트 생성 주체 -> 엔티티, 밸류 도메인 서비스와 같은 도메인 객체
- 도메인 로직을 실행해서 상태가 바뀌면 관련 이벤트 발생
이벤트 핸들러
는 이벤트 생성 주체가 발생한 이벤트에 반응
- 이벤트 새성 주체가 발생한 이벤트를 전달받아 이벤트에 담긴 데이터 이용해서 원하는 기능 실행
이벤트 디스패치
는 이벤트 생성 주체와 이벤트 핸들러 연결해 줌
- 이벤트 디스채러의 구현 방식에 따라 이벤트 생성과 처리를 동기나 비동기로 실행하게 됨
10.2.2 이벤트의 구성
10.2.3 이벤트 용도
- 트리거(Trigger)
- 도메인 상태가 바뀔 때 다른 후처리가 필요하면 후처리를 실행하기 위한 트리거로 이벤트 사용 가능
- 서로 다른 시스템 간의 데이터 동기화
10.2.4 이벤트 장점
- 서로 다른 도메인 로직이 섞이는 것을 방지
- 기능 확장 용이
10.3 이벤트, 핸들러, 디스패처 구현
이벤트와 관련된 코드
- 이벤트 클래스: 이벤트를 표현함
- 디스패처: 스프링이 제공하는 ApplicationEventPublisher를 이용
- Events: 이벤트를 발생. 이벤트 발생을 위해 ApplicationEventPublisher 사용
- 이벤트 핸들러: 이벤트 수신해서 처리. 스프링이 제공하는 기능 사용
10.3.1 이벤트 클래스
-
이벤트 자체를 위한 상위 타입은 존재하지 않음
-
원하는 클래스를 이벤트로 사용하되, 클래스명은 과거 시제로 설정해야 함
-
이벤트를 처리하는 데 필요한 최소한의 데이터 포함
-
모든 이벤트가 공통으로 갖는 프로퍼티가 존재한다면 관련 상위 클래스 생성 가능 -> 각 이벤트 클래스가 상속 받음
10.3.2 Events 클래스와 ApplicationEventPublisher
-
이벤트 발생과 출판을 위해 스프링이 제공하는 ApplicationEventPublisher
를 사용
-
ex) publishEvent()
메서드 이용해서 이벤트 발생시킴, setPublicher()
메서드를 통해서 Events 클래스가 사용할 ApplicationEventPublisher 객체를 전달받음
10.3.3 이벤트 발생과 이벤트 핸들러
Events.raise()
메서드를 사용해서 이벤트 발생시킴
- 이벤트 핸들러는 스프링이 제공하는
@EventListener
애너테이션 사용해서 구현
10.3.4 흐름 정리
- 도메인 기능 실행
- 도메인 기능은
Events.raise()
를 이용해서 이벤트 발생시킴
Events.raise()
는 스프링이 제공하는 ApplicationEventPublisher
를 이용해서 이벤트 출판
ApplicationEventPublisher
는 @EventListener
(이벤트타입.class) 애너테이션이 붙은 메서드를 찾아 실행함
- 도메인 상태 변경과 이벤트 핸들러는 같은 트랜잭션 범위에서 실행
10.4 동기 이벤트 처리 문제
10.5 비동기 이벤트 처리
10.5.1 로컬 핸들러 비동기 실행
- 이벤트 핸들러를 별도 스레드로 실행
- 스프링이 제공하는
@Async
애너테이션 사용하면 손쉽게 비동기 이벤트 핸들러 실행 가능
@EnableAsync
애너테이션을 사용해서 비동기 기능을 활성화함
- 이벤트 핸들러 메서드에
@Async
애너테이션을 붙임
10.5.2 메시징 시스템을 이용한 비동기 구현
10.5.3 이벤트 저장소를 이용한 비동기 처리
- 이벤트를 일단 DB에 저장한 뒤에 별도 프로그램을 이용해서 이벤트 핸들러에 전달
- 이벤트가 발생하면 핸들러는 스토리지에 이벤트 저장
- 포워더는 주기적으로 이벤트 저장소에서 이벤트 가져와 이벤트 행들러 실행
- 도메인의 상태와 이벤트 저장소로 동일한 DB 사용
- 핸들러가 이벤트 처리에 실패할 경우 포워더는 다시 이벤트 저장소에서 이벤트 읽어와 핸들러 실행
- 이벤트를 외부에 제공하는 API 사용
- 외부 핸들러가 API 서버를 통해 이벤트 목록 가져감
- 이벤트 목록을 요구하는 외부 핸들러가 자신이 어디까지 이벤트를 처리했는지 기억해야 함
+) 이벤트 저장소 구현 방법
+) 이벤트 저장을 위한 이벤트 핸들러 구현 방법
+) REST API 구현 방법
+) 포워더 구현 방법
10.6 이벤트 적용 시 추가 고려 사항
- 이벤트 소스를 EventEntry애 추가할지 여부
- 포워더에서 전송 실패를 얼마나 허용할 것이냐
- 이벤트 손실에 대한 것
- 이벤트 순서에 대한 것
- 이벤트 재처리에 대한 것
10.6.1 이벤트 처리와 DB 트랜잭션 고려
- 스프링이 지원하는
@TransactionalEventListener
애너테이션을 사용하여 스프링 트랜잭션 상태에 따라 이벤트 핸들러를 실행