이벤트를 통해 도메인간 의존을 제거하고 후처리를 쉽게 할 수 있다.
이벤트라는 용어는 '과거에 벌어진 어떤 것'을 의미한다.
이벤트가 발생했다는 것은 상태가 변경됐다는 것을 의미한다.
이벤트는 발생하는 것에서 끝나지 않고 그 이벤트에 반응하여 원하는 동작을 수행하는 기능을 구현한다.
'~ 할 때', '~가 발생하면', '만약 ~하면'과 같은 요구사항은 도메인의 상태 변경과 관련된 경우가 많고 이는 이벤트로 구현할 수 있다.
예를 들어 '주문을 취소할 때 이메일을 보낸다'라는 요구사항에서 '주문을 취소할 때'는 주문이 취소 상태로 바뀌는 것을 의미하고 '주문 취소됨 이벤트'를 활용해 구현할 수 있다.
도메인 모델에 이벤트를 도입하기 위해서 다음 네 개의 구성요소가 필요하다.
- 이벤트
- 이벤트 생성 주체
- 이벤트 디스패처(퍼블리셔)
- 이벤트 핸들러(구독자)
이벤트 생성 주체는 엔티티, 밸류, 도메인 서비스와 같은 도메인 객체이다.
도메인 로직을 실행해서 상태가 바뀌면 관련 이벤트를 발생시킨다.
발생한 이벤트에 반응한다.
이벤트에 담긴 데이터를 이용해 원하는 기능을 실행한다.
이벤트 생성 주체와 이벤트 핸들러를 연결해준다.
이벤트 디스패처의 구현 방식에 따라 이벤트 생성과 처리를 동기나 비동기로 실행하게 된다.
이벤트는 다음과 같은 정보를 담는다.
- 이벤트 종류 - 클래스 이름으로 이벤트 종류를 표현
- 이벤트 발생 시간
- 추가 데이터 - 주문번호, 신규 배송지 정보 등 이벤트와 관련된 정보
다음과 같이 이벤트는 과거 시제를 사용한 클래스이다.
public class ShippingInfoChangedEvent {
private String orderNumber;
private long timestamp;
private ShippingInfo newShippingInfo;
// 생성자, getter
}
이벤트를 생성하는 주체가 되는 애그리거트는 다음과 같다.
Events.raise()는 디스패처를 통해 이벤트를 전파하는 기능을 제공한다.
public class Order {
public void changeShippingInfo(ShippingInfo newShippingInfo) {
verifyNotYetShipped();
setShippingInfo(newShippingInfo);
Events.raise(new ShippingInfoChangedEvent(number, newShippingInfo));
}
...
}
디스패처로부터 필요한 작업을 수행하는 핸들러는 다음과 같이 구현할 수 있다.
public class ShippingInfoChangedHandler {
@EventListener(ShippingInfoChangedEvent.class)
public void handle(ShppingInfoChangedEvent evt) {
shippingInfoSynchronizer.sync(
evt.getOrderNumber(),
evt,getNewShippingInfo());
}
...
}
이벤트는 이벤트 핸들러가 작업을 수행하는 데 필요한 데이터를 담아야 한다.
이벤트는 데이터를 담아야 하지만 그렇다고 이벤트 자체와 관련 없는 데이터를 포함할 필요는 없다.
이벤트는 크게 두 가지 용도로 쓰인다.
- 트리거(Trigger)
- 데이터 동기화
도메인의 상태가 바뀔 때 다른 후처리가 필요하면 후처리를 실행하기 위한 트리거로 이벤트를 사용할 수 있다.
예매 결과를 SMS로 통지하거나 주문을 취소했을 때 환불을 하는 후처리를 이벤트를 통해 구현할 수 있다.
서로 다른 시스템 간의 데이터 동기화가 필요할 때도 이벤트를 사용할 수 있다.
외부 시스템을 이용하고 있다면 도메인 상태가 바뀌었을 때 외부 시스템에 변경된 내용을 이벤트를 발생시켜 데이터를 동기화시킬 수 있다.
이벤트를 사용하면 서로 다른 도메인끼리의 의존을 제거해 로직이 섞이는 것을 방지할 수 있다.
이벤트 핸들러를 통해 구현하면 기존 로직을 수정할 필요가 없어 기능 확장에 용이하다.
다음과 같이 추가 기능이 생긴다면 해당 기능을 처리하는 핸들러를 만들면 해결된다.
이벤트를 이용해 처리하는 것과 기존 메서드를 통해 코드를 분리하는 것과 큰 차이를 느끼지 못했다.
도메인 간의 직접적인 의존을 막아 확장에 용이한 것은 이해가 가지만 이벤트의 장점이 크게 와닿지 못했다.
동기 요청을 비동기스럽게 처리하는 것과 비동기 요청은 다르기 때문에 비동기 처리에 용이할 것 같긴 하다.