기존의 알림 기능은 이메일 알림, 문자 알림, 웹소켓 알림 3가지가 있다.
이메일, 문자 전송은 각 서버에서 직접 수행하고 있었고 웹소켓 서버는 분리가 되어있고 API를 통해 전송 요청을 하는 상태였다.
푸시 알림 추가 요구사항이 생겨 기존에 남겨두었던 문제를 개선하기로 했다.
주문로직을 예로 들어 글을 작성한다.
알림의 종류가 하나씩 추가 될 때마다 코드가 한 줄씩 늘어났다.
public void order() {
// 주문 핵심 비즈니스 로직
...
// 주문 완료 후 알림 전송
emailService.send();
smsService.send();
pushNotiService.send();
...
}
알림이 변경되거나 추가가 되면 코드에 계속 추가되고 그에따라 실행 시간도 늘어난다.
또한 주문 로직은 정상적으로 수행되었는데 알림 전송 실패로 인해 주문 실패가 되는 일이 발생 할 수 있다.
도메인 별로 서버가 분리되어 있기 때문에 알림을 전송해야 하는 각 서버는 필요한 알림을 전송하는 라이브러리에 종속이 되어있고 전송을 담당하는 로직도 중복이 되어있다.
만약 알림의 내용이 바뀌거나 추가되면 해당 알림을 전송해야하는 모든 서버의 로직에 변경이 일어나고 다 재배포 해야한다.
기존의 문제점들은 모두 관심사가 분리 되어 있지 않기 때문에 일어났다.
주문 도메인의 입장에서 보면 알림의 전송 여부는 관심이 없다. 주문만 잘 수행하면 된다. 하지만 현재는 주문과 알림이 한 몸이 되어있다.
알림의 변경이 일어나면 주문의 비즈니스 로직 코드에 영향이 가는 부분을 개선하고자 구조를 변경했다.
먼저 알림을 처리하는 서버를 분리하였다. 각 서버가 하나의 역할만을 가지는게 좋다고 생각하여 푸시알림 서버, 이메일전송 서버, 문자전송 서버 각각 분리했다.
다음으로 알림을 요청하는 서버와 알림을 처리하는 서버간의 통신을 비동기로 수행한다.
다음으로 이벤트 발행에 어떤 데이터를 전송할 지에 대한 고민이 있었다.
이벤트 발행할 때 포함한 데이터는 이벤트 이름과 식별자 두개 만을 전송하기로 결정했다.
{
"event_type" : "ORDER_SUCCESS",
"target_id" : 1
}
이벤트를 가져와 알림을 보내는 서버에서는 식별자를 가지고 해당 이벤트종류에 맞게 데이터를 조회하여 사용하도록 했다.
이벤트로 데이터를 직접 주고 받으면 데이터가 추가 혹은 변경 될 경우에 클라이언트 쪽에서도 변경이 일어나는 것을 막고자 했다.
구조를 변경하는 목적은 이벤트를 발행하는 서버와 해당 이벤트를 처리하는 서버간 의존을 완전히 분리하는 것이기 때문이다.
public void order() {
// 주문 핵심 비즈니스 로직
...
// 주문 완료 후 이벤트 발행
snsSender.publish();
}