이 글은 로직이 쌓여감에 따라 도메인 의존성이 늘어나 해결하기 위하여 "이벤트"를 적용한 내용을 기록한 글입니다.
이벤트에 대해서는 정리를 하였으며, 다음의 링크로 대체하겠습니다.
다음의 로직을 살펴보겠습니다.
/**
* 고용정보 생성 메서드
* @param employeeId : 요청자 id(pk)
* @param boardId 배치하려는 게시 글 id(pk)
* @return 고용된 글의 정보 dto
*/
public DtoOfApplyEmploy apply(Long employeeId, Long boardId){
//중략(기타 비즈니스 로직 실행)
employRepository.save(createdEmployEntity);
boardEntity.updateStatus(Status.MATCHED); // 문제가 된다고 판단한 코드
return DtoOfApplyEmploy.builder()
.employeeNickname(employeeEntity.getNickname())
.employerNickname(employerEntity.getNickname())
.boardId(boardEntity.getId())
.build();
}
/**
* 고용자가 고용을 취소하는 메서드
* @param memberId : 요청자 id(pk)
* @param boardId : 요청 대상 게시 글 id(pk)
* @return : 요청 취소에 대한 응답 dto
*/
public DtoOfCancelByEmployer cancelEmployByEmployer(Long memberId, Long boardId){
// 중략 ... 필요 엔티티 find 및 필요 로직 실행
employRepository.delete(employEntity);
boardEntity.updateStatus(Status.WAITING); // 문제가 된다고 판단한 코드
return DtoOfCancelByEmployer.builder()
// 중략
.build();
}
위의 코드를 보면 boardEntity의 상태를 나타내는 "Status"를 update하는 것을 볼 수 있습니다.
해당 클래스는 EmployService.java 클래스이며, boardEntity과 의존성을 우려하였습니다. 그리고 추가적인 기능을 구현함에 따라 해당 "문제가 된다고 판단한 코드"를 이어서 많은 로직이 추가될 것을 우려하였습니다.
그래서 이벤트를 고민하게되었습니다.
변경 전의 로직은 다음과 같습니다.
@Transactional(isolation = Isolation.SERIALIZABLE)
public DtoOfApplyEmploy apply(Long employeeId, Long boardId){
// 관련 엔티티 조회 로직
// 고용 정보 생성 여부 판단 로직
// 고용 정보 생성
try {
employRepository.save(createdEmployEntity);
}catch (Exception e){
throw new TransactionException("이미 배치되었습니다.");
}
boardEntity.updateStatus(Status.MATCHED); // 수정될 코드
return //중략
}
위의 코드에서 로직이 수행되고, boardEntity의 코드를 직접적으로 수정하는 메서드를 호출하게됩니다.
해당 로직에서 의존성과 결합도가 생긴다고 판단이 되어, 이벤트를 적용했습니다.
이벤트기반으로 처리된 코드를 살펴보겠습니다.
MatchedEvent.java 생성
@Getter
public class MatchedEvent{
private final Board board;
private final Status status;
@Builder
public MatchedEvent(Board board, Status status){
this.board = board;
this.status = status;
}
}
Status를 업데이트하는 것이 목표이므로, Status와 Board 객체를 가지고 있도록 만들어줍니다.
@Transactional(isolation = Isolation.SERIALIZABLE)
public DtoOfApplyEmploy apply(Long employeeId, Long boardId){
// 관련 엔티티 조회 로직
// 고용 정보 생성 여부 판단 로직
// 고용 정보 생성
try {
employRepository.save(createdEmployEntity);
}catch (Exception e){
throw new TransactionException("이미 배치되었습니다.");
}
// boardEntity.updateStatus(Status.MATCHED); // 수정될 코드
System.out.println("Publish Event");
eventPublisher.publishEvent(MatchedEvent.builder().board(boardEntity).status(Status.MATCHED).build()); //수정 후의 코드
return //중략
}
publisher로 publish를 하였습니다.
BoardEmployEventHandler.java 생성
@RequiredArgsConstructor
@Component
public class BoardEmployEventHandler{
private final BoardService boardService;
@Async
@TransactionalEventListener
public void updateStatus(MatchedEvent matchedEvent) throws InterruptedException {
Thread.sleep(5000L); // 5초 thread sleep
System.out.println("Update Board Status Event 실행");
Board boardEntity = matchedEvent.getBoard();
boardEntity.updateStatus(matchedEvent.getStatus());
boardService.saveEntity(boardEntity);
}
}
위의 로직대로 Employ 요청을 해보았습니다.
Status가 "WAITING" 상태인 것을 볼 수 있습니다.
Status가 "MATCHED" 상태로 변경된 것을 볼 수 있습니다.
사전에 삽입한 print 문을 통해서 로그를 살펴보겠습니다.
정상적인 흐름으로 수행된 것을 볼 수 있습니다.
이벤트를 이용하여 의존성과 결합도를 줄여보았습니다.
하지만 다음과 같은 의문이 생겼습니다.
이벤트로 엔티티의 수정, 삭제, 생성 등등의 로직을 수행해도되는가?
이에 대한 답은 아직 찾지못하였습니다.
추가로 다음과 같은 의문이 또 생겼습니다.
꼭 핸들러에서 메서드를 구현하여야 하는가? BoardService의 메서드에 "TransactionalEventListener"을 설정하고 사용하면 안되는가?
이에 대한 답으로는 이벤트는 이벤트 핸들러에서 관리되어야 하고, 수많은 이벤트가 생겼을 때 관리하기 힘들기 때문에 이벤트 핸들러를 반드시 작성해야한다고 판단이 됩니다.