10.1. 시스템 간 강결합 문제

쇼핑몰에서 구매를 취소하면 환불 처리를 해야하는데, 이 때 환불 기능을 실행하는 주체는 주문 도메인 엔티티가 될 수 있다. 도메인 객체에서 환불 기능을 실행하려면 환불 기능을 제공하는 도메인 서비스를 파라미터로 받아 실행 할 수 있다.

public class Order{
 
	public void cancel(RefundService refundService){
		verifyNotYetShipped();
		this.state = OrderState.CANCELED;

		this.refundStatus = State.REFUND_STARTED;
		try{
			refundService.refund(getPaymentId());
			this.refundStatus = State.REFUND_COMPLETED;
		} catch(Exception ex){
		
		}
	}
}

또는 응용 서비스에서 실행할 수도 있는데, 보통 결제 시스템은 외부에 존재하므로 응용서비스는 외부에 있는 결제 시스템이 제공하는 환불 서비스를 호출한다.

public class CancelOrderService {
	private RefundService refundService;

	@Transactional
	public void cancel(OrderNo orderNo) {
		Order order = findOrder(orderNo);
		order.cancel();

		order.refundStarted();
		try {
			refundService.refund(order.getPaymentId()); //외부 서비스 성능에 직접 영향을 받는다.
			order.refundCompleted()p;
		} catch(Exception ex) {
			???
		}
	}
}

이 때 두가지 문제가 발생할 수 있다. 첫번째로 외부 서비스가 정상이 아닐 경우 트랜잭션 처리에 대한 부분이다. 익셉션이 발생하면 트랜잭션을 롤백 해야 할까? 아니면 일단 커밋해야 할까? 트랜잭션을 롤백 하는 것이 맞다고 생각되지만 주문은 취소 상태로 변경하고 환불만 나중에 다시 시도하는 방식으로 처리할 수 있다.

두번째 문제는 성능에 관한 것이다. 환불을 처리하는 외부 시스템의 응답 시간이 길어지면 그만큼 대기 시간도 길어진다. 즉, 외부 서비스 성능에 직접적인 영향을 받게 된다.

위와 같은 문제가 발생하는 이유는 주문 바운디드 컨텍스트와 결제 바운디드 컨텍스트간의 강결합 때문이다. 이벤트를 사용하면 이런 강결합을 없앨 수 있는데, 특히 비동기 이벤트를 사용하면 두 시스템간의 결합을 크게 낮출 수 있다.

10.2. 이벤트 개요

이벤트라는 용어는 '과거에 벌어진 어떤 것'을 의미한다. 이벤트가 발생한다는 것은 상태가 변경됐다는 것을 의미한다.

10.2.1. 이벤트 관련 구성요소

도메인 모델에 이벤트를 도입하려면 아래와 같은 네개의 구성요소인 이벤트, 이벤트 생성 주체, 이벤트 디스패처, 이벤트 핸들러를 구현해야 한다

Untitled

이벤트 생성 주체

이벤트 핸들러