Skip to content

[REFACTOR] RecommendationService 책임 분리 + 레거시 정리 #152

@ryuwldnjs

Description

@ryuwldnjs

상위 이슈


현재 상태 (리팩토링 전)

God Class: 12개 의존성, 415줄, 6가지 책임

public class RecommendationService {
    private final Clock clock;
    private final TeamRepository teamRepository;
    private final TeamMemberRepository teamMemberRepository;
    private final TeamIncludeTagRepository teamIncludeTagRepository;
    private final ProblemTagRepository problemTagRepository;
    private final ProblemService problemService;
    private final ProblemSyncService problemSyncService;
    private final MailSender mailSender;
    private final RecommendationMailBuilder recommendationMailBuilder;
    private final RecommendationRepository recommendationRepository;
    private final RecommendationProblemRepository recommendationProblemRepository;
    private final MemberRecommendationRepository memberRecommendationRepository;
}

파일 하나에 수동 추천 생성, 배치 추천 생성, 배치 이메일 발송, 조회 3개, 미션 사이클 계산이 전부 들어있음.


해결: 책임별 서비스 분리 + 레거시 정리

파일 구조

recommendation/
├── RecommendationController.java
│
├── service/
│   ├── RecommendationService.java         (9 의존성)
│   ├── ScheduledRecommendationService.java
│   ├── RecommendationEmailService.java
│   ├── RecommendationCreator.java         (package-private)
│   └── MissionCyclePolicy.java            (package-private)
│
├── scheduler/
│   ├── ProblemRecommendationScheduler.java
│   └── EmailSendScheduler.java
│
├── mailbuilder/
│   └── RecommendationMailBuilder.java
│
├── domain/
│   ├── Recommendation.java
│   ├── RecommendationProblem.java
│   ├── RecommendationType.java
│   └── member/
│
├── dto/
│   └── response/
│       └── RecommendationDetailResponse.java
│
└── repository/

메서드 배치

service/
├── RecommendationService.java
│   ├── createManualRecommendation(teamId)       ← Controller
│   ├── findTodayRecommendation()                ← Controller, TeamService
│   ├── getMyTodayProblems()                     ← Controller
│   └── validateNoRecommendationToday()          (private)
│
├── ScheduledRecommendationService.java
│   ├── prepareDailyRecommendations()            ← ProblemRecommendationScheduler
│   └── getActiveTeams()                         (private)
│
├── RecommendationEmailService.java
│   ├── sendAll()                                ← EmailSendScheduler
│   └── send(List<MemberRecommendation>)         ← RecommendationService
│
├── RecommendationCreator.java                   (package-private)
│   └── create(Team, RecommendationType)         ← RecommendationService, ScheduledService
│
└── MissionCyclePolicy.java                      (package-private)
    └── getMissionCycleStart(Clock)               ← 전체 서비스

의존 관계

Controller → RecommendationService → RecommendationCreator
                                   → RecommendationEmailService
                                   → MemberRecommendationRepository

TeamService → RecommendationService (조회만)

ProblemRecommendationScheduler → ScheduledRecommendationService → RecommendationCreator

EmailSendScheduler → RecommendationEmailService

파일 변경

신규

파일 역할
service/ScheduledRecommendationService.java 배치 오케스트레이션 (팀 루프 + 중복 체크 + 집계)
service/RecommendationEmailService.java 이메일 발송 (수동/배치 공용)
service/RecommendationCreator.java 팀 1개 추천 생성 공통 로직 (package-private)
service/MissionCyclePolicy.java 미션 사이클 도메인 정책 (package-private)

수정

파일 변경
service/RecommendationService.java 배치/이메일 메서드 제거, Creator와 EmailService에 위임, count 파라미터 제거, deprecated getTodayRecommendation 제거
service/RecommendationCreator.java 예외를 IllegalStateExceptionCustomException(NO_VERIFIED_HANDLE)로 통일
RecommendationController.java count 파라미터 제거, deprecated GET /team/{teamId}/today-problem 엔드포인트 제거
dto/response/RecommendationDetailResponse.java 새 도메인 모델 기반 from() (레거시 TeamRecommendation 의존 제거)
common/enums/CustomResponseStatus.java NO_VERIFIED_HANDLE 에러 코드 추가
scheduler/EmailSendScheduler.java 테스트용 dead method 제거
scheduler/ProblemRecommendationScheduler.java 테스트용 dead method 제거
Team.java teamRecommendations 관계 필드 제거
TeamService.java import 경로 변경

이동/이름 변경

변경 전 변경 후
infrastructure/scheduler/ProblemRecommendationScheduler.java recommendation/scheduler/
infrastructure/scheduler/EmailSendScheduler.java recommendation/scheduler/
recommendation/mail/ recommendation/mailbuilder/
TeamRecommendationDetailResponse RecommendationDetailResponse

삭제

파일 이유
domain/team/TeamRecommendation.java 미사용 레거시 엔티티 (인메모리 DTO 변환용)
domain/team/TeamRecommendationProblem.java 미사용 레거시 엔티티
domain/team/RecommendationStatus.java 미사용 enum (DTO에서 String으로 대체)
repository/TeamRecommendationRepository.java 미사용 레거시 Repository
repository/TeamRecommendationProblemRepository.java 미사용 레거시 Repository

테스트

파일 변경
RecommendationServiceTest.java 배치 테스트 제거, count 파라미터 제거 반영, MemberRecommendationRepository mock 추가
ScheduledRecommendationServiceTest.java 신규: 배치 미션 사이클 테스트 이동
RecommendationCreatorTest.java 신규: 추천 생성 성공/실패, MemberRecommendation 생성 검증
RecommendationEmailServiceTest.java 신규: 배치/수동 이메일 발송, 부분 실패 처리, 이메일 없는 회원 처리
TransactionIssueTest.java import 변경, RecommendationEmailService 사용

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions