-
[알림 서비스] 알림 서비스 구조 개선 - 알림 전송 외부 라이브러리 의존성 분리 리팩토링STOVE DEVCAMP 3기/알림 서비스 2023. 3. 10. 03:08
📌 개선 대상
- 알림 요청 처리 구조
📌 기존 처리 구조의 문제점 및 보완할 점 파악
Q. 알림 전송 수단이 추가되었을 때 확장 가능한 구조인가?
- 알림 요청 처리 비즈니스 로직에 알림 전송 로직이 포함되어 있어 알림 전송 외부 라이브러리와의 의존성이 높은 구조임
- 알림 전송 수단 추가 시 비즈니스 로직 수정 불가피함
<코드 상세>
// 알림생성API 요청을 처리하는 비즈니스 로직 @Service public class CreateNotificationService implements CreateNotificationUseCase { ... @Override public void createNotification(CreateNotificationCommand command) { saveNotification.saveAll(); // 알림 데이터 저장 (알림 목록 조회를 위함) getDevicePort.findAllByUserIdsWithOptTrue(); // 알림 수신 유저의 FCM 디바이스 토큰 목록 조회 sendNotificationPort.sendPushNotification(); // 푸시 알림 전송 (FCM에 전송할 알림 메시지 저장 수행) } }
- 알림 전송 수단이 추가될 경우, 위의 비즈니스 로직의 수정이 필수임
- 이는 클린 아키텍처의 목적을 지키지 못한 형태임
- 알림 요청을 처리하는 비즈니스 로직과 알림 전송 외부 라이브러리와의 결합도가 높음
📌 개선 구조
- 알림 요청 처리 비즈니스 로직에 이벤트 발행/구독 패턴 적용
- 역할과 구현을 분리하고자 Port & Adapter 패턴 적용 (현재 알림 서비스 아키텍처 구조를 따름)
<코드 상세>
알림 요청 처리하는 비즈니스 로직
// 알림생성API 요청을 처리하는 비즈니스 로직 @Service public class CreateNotificationService implements CreateNotificationUseCase { private final SaveNotificationPort saveNotificationPort; private final PublishEventPort publishEventPort; ... @Override public void createNotification(CreateNotificationCommand command) { saveNotificationPort.saveAll(command); // 알림 데이터 저장 (알림 목록 조회를 위함) publishEventPort.createNotification(command); // 알림생성이벤트 발행 } }
이벤트 발행 어댑터
@Componentpublic class ApplicationEventAdapter implements PublishEventPort {private final ApplicationEventPublish eventPublisher; // Spring의 ApplicationEvent 라이브러리 사용private final SaveEventPort saveEventPort;...@Overridepublic void createNotification(CreateNotificationCommand command) {...NotificationCreationEvent savedEvent = (NotificationCreationEvent) saveEventPort.save(event); // 알림생성이벤트 저장eventPublisher.publishEvent(savedEvent); // 알림생성이벤트 발행}}이벤트 핸들러
@Componentpublic class FcmEventHandler {private final GetDevicePort getDevicePort;private final SaveMessagePort saveMessagePort;...@EventListenerpublic void saveMessage(NotificationCreationEvent event) {List<Device> devices = getDevices(event.getReceivers()); // 알림 수신 디바이스 목록 조회List<NotificationMessage> messages = getNotificationMessages(event, devices); // 알림메시지 생성saveMessagePort.saveMessageAll(messages); // 알림메시지 저장}}상세 설명
- 동작 방식
- 알림생성API 호출 시 알림 요청을 처리하는 비즈니스 로직에서는 알림 데이터를 저장하고, '알림생성이벤트' 발행을 호출함
- '알림생성이벤트' 발행을 처리하는 어댑터(구현체)에서는 해당 이벤트를 저장하고, 이벤트를 발행함
- 해당 이벤트 핸들러인 FcmEventHandler가 알림 수신 디바이스별 알림메시지 생성 후 저장함
- 개선 사항
- 이벤트 발행/구독 패턴, Port & Adapter 패턴 적용
- 이벤트 발행 방식이 달라질 경우, 이벤트 발행 어댑터를 추가하여 확장 가능함
- 현재는 SpringApplicationEvent를 사용하지만, 카프카 프로듀서 등으로 교체 가능함
- 알림 전송 수단이 추가될 경우, EventHandler 클래스를 추가하여 확장 가능함
- 전달받은 이벤트를 전송 수단에 맞게 처리하도록 구현하면 되므로, 기존의 비즈니스 로직 수정이 필요하지 않음
- 알림 요청을 처리하는 비즈니스 로직과 외부 라이브러리와의 결합도를 낮춤
- DB, SpringApplicationEvent 라이브러리 등
- 추가적으로,
- 알림과 이벤트가 저장되는 공간이 변경되거나 추가되는 경우를 대비해 save() 메서드를 인터페이스로 추상화함
- 현재는 이벤트 발행 및 이벤트 핸들러의 처리가 하나의 스레드에서 수행되지만, 이후 이벤트 핸들러의 작업을 비동기로 실행하여 별도의 스레드에서 수행되도록 변경 가능함
구성 그림
'STOVE DEVCAMP 3기 > 알림 서비스' 카테고리의 다른 글
[알림 서비스] 알림 서비스 구조 개선 방향 수립 (0) 2023.03.10 [알림 서비스] 알림 서비스 구조 개선 - 대량의 알림 데이터 처리를 위한 장기적 관점의 확장 가능한 구조 설계 (0) 2023.03.10 [알림 서비스] 알림 서비스 구조 개선 - 신뢰성 있는 알림 서비스 제공 (0) 2023.03.10 [알림 서비스] 알림 서비스 데이터베이스 설계 (0) 2023.03.10 [개념] MongoDB 알고 사용하기 - 데이터 모델링 (0) 2023.03.10