IMS 워크플로우 만들기
1. 요구사항 및 기능 분석
1-1. 워크플로우 기능 및 구조
- 워크플로우는 상태(Status)와 전환(Transition)을 관리하는 서비스
- 워크플로우를 통해 이슈 처리 프로세스를 사용자가 직접 지정할 수 있음
- 워크플로우 구조 및 기능
- 시작 상태 (default)
- 상태 생성
- 시작 상태, 진행상태, 종료 상태 나뉨
- 시작 상태는 시작과 연결되면 시작 상태이며, 단 1개만 존재 가능
- 종료 상태에서는 전환이 생길 수 없음
- 상태명 지정 가능
- 전환 생성
- 시작 상태와 종료 상태를 연결함
- 전환명 지정 가능
- 전환마다 규칙을 생성할 수 있음
- 전환이 동일할경우, 동일한 규칙과 전환명을 가짐
→ ex. REJECTED에서 CLOSED로 가는 전환과 RESOLVED에서 CLOSED로 가는 전환은 동일한 close라는 이름을 가진 전환이며, 해당 전환에 종료일 자동 생성이라는 기능을 추가하면 동일하게 적용 받음
1-2. 유사 시스템 (Jira) 예시




2. 구현 아이디어 및 적용
2-1. ERD 설계

2-2. 워크플로우 상태 및 전환 관리 서비스 로직
- WorkflowService 주요 기능
- 상태와 전환의 생성, 수정, 삭제 작업을 처리하는 서비스 로직
- 워크플로우 상태 관리: 상태 추가, 수정, 삭제
- 워크플로우 전환 관리: 전환 추가, 수정, 삭제
- 생성, 수정, 삭제 API를 따로 분리하여 호출하지 않고, 최종적인 상태만 FE로 부터 받아서 처리
- 검증 로직
- 단일 시작 전환 체크 : 시작 전환이 단 하나만 존재하도록 검증
- 상태/전환의 고유성 검증 : 각 상태와 전환 ID가 고유한지 체크
2-3. 상태/전환의 수정, 생성, 삭제 한번에 처리 방안 (API 분리 X)
UpdateWorkflowService 클래스
- 워크플로우 전체 상태와 전환을 관리하는 서비스의 핵심
- 각각의 상태와 전환을 처리하는 별도의 서브 서비스를 호출해 관리
public class WorkflowService extends Service<WorkflowRequest, Void> {
CreateWorkflowStatusService createWorkflowStatusService = new CreateWorkflowStatusService();
UpdateWorkflowStatusService updateWorkflowStatusService = new UpdateWorkflowStatusService();
DeleteWorkflowStatusService deleteWorkflowStatusService = new DeleteWorkflowStatusService();
CreateWorkflowTransitionService createWorkflowTransitionService = new CreateWorkflowTransitionService();
UpdateWorkflowTransitionService updateWorkflowTransitionService = new UpdateWorkflowTransitionService();
DeleteWorkflowTransitionService deleteWorkflowTransitionService = new DeleteWorkflowTransitionService();
@Override
public Void doService(UpdateWorkflowRequest request, SqlSession sqlSession) {
StatusMapper statusMapper = sqlSession.getMapper(StatusMapper.class);
TransitionMapper transitionMapper = sqlSession.getMapper(TransitionMapper.class);
// 상태와 전환을 처리하고, 삭제할 상태 및 전환을 반환
Set<Long> toBeDeletedStatusIds = processStatuses(sqlSession, statusMapper, request.getWorkflowStatusRequestList());
Set<Long> toBeDeletedTransitionIds = processTransitions(sqlSession, transitionMapper, request.getWorkflowTransitionRequestList());
// 삭제 작업 수행
deleteStatuses(toBeDeletedStatusIds, sqlSession);
deleteTransitions(toBeDeletedTransitionIds, sqlSession);
return null;
}
}
processStatuses 메서드
- 새로운 상태를 생성하고, 기존 상태를 업데이트함.
existingStatusIds : DB에 존재하는 StatusId Set을 가져옴
createStatuses
- List에서 isNewStatus()를 통해 id가 null인지 체크함.
- 즉, id가 존재하지 않는다면 새로 생성된 Status로 간주해서 DB에 추가함
updateStatuses
- List에서 isExistingStatus()통해 DB에 존재하는 StatusId인지 체크
- 즉, Request로 들어온 값 중에 기존에 존재했다면 수정해야할 Status로 간주
- 수정한 Status는
existingStatusIds 에서 삭제함 → 최종적으로 삭제할 Status만 남음
- 모든 상태가 처리된 후에는 더 이상 사용되지 않는 상태를 찾아서 삭제할 수 있도록
existingStatusIds에 남아 있는 상태 ID를 반환함
deleteStatuses : 최종적으로 남은 StatusId에 해당하는 상태는 삭제함

private Set<Long> processStatuses(SqlSession sqlSession, StatusMapper statusMapper, List<StatusRequest> statusRequestList) {
Set<Long> existingStatusIds = findAllStatusIds(statusMapper);
// 새 상태 생성
createStatuses(statusRequestList, sqlSession);
// 기존 상태 업데이트
updateStatuses(existingStatusIds, statusRequestList, sqlSession);
return existingStatusIds; // 더 이상 사용되지 않는 상태 반환
}
private void createStatuses(List<StatusRequest> statusRequestList, SqlSession sqlSession) {
statusRequestList.stream()
.filter(this::isNewStatus)
.forEach(statusRequest -> createStatus(statusRequest, sqlSession));
}
private void updateStatuses(Set<Long> statusIds, List<StatusRequest> statusRequestList, SqlSession sqlSession) {
statusRequestList.stream()
.filter(statusRequest -> isExistingStatus(statusIds, statusRequest))
.forEach(statusRequest -> {
updateStatus(statusRequest, sqlSession);
statusIds.remove(statusRequest.getStatusId());
});
}