Skip to content

Commit 9b80c2c

Browse files
refactor: Service 클래스에 CQRS 패턴 및 Redis 적용(#75)
* feat: MemberService를 삭제하고, MemberQueryService, MemberCommandService 구현 * feat: TripService를 삭제하고, TripQueryService, TripCommandService 구현 * feat: StampService를 삭제하고, StampQueryService, StampCommandService 구현 * feat: MissionService를 삭제하고, MissionQueryService, MissionCommandService 구현 * feat: StudyLogService를 삭제하고, StudyLogQueryService, StudyLogCommandService 구현 * feat: DailyMissionService를 삭제하고, DailyMissionQueryService, DailyMissionCommandService 구현 * feat: StudyLogDailyMissionService를 삭제하고, StudyLogDailyMissionQueryService, StudyLogDailyMissionCommandService 구현 * feat: DailyGoalService를 삭제하고, DailyGoalQueryService, DailyGoalCommandService 구현 * feat: PomodoroService를 삭제하고, PomodoroQueryService, PomodoroCommandService 구현 * feat: DailyGoal에 updateCompleted 메서드 추가 * feat: MemberFacade, TripFacade, StampFacade, MissionFacade, DailyGoalFacade, HardDeleteFacade 각 메서드에 트랜잭션 적용 * refactor: MemberService, TripService, StampService, MissionService에서 @transactional 어노테이션 제거 * refactor: DailyGoalService, DailyMissionService에서 @transactional 어노테이션 제거 * refactor: PomodoroService, StudyLogService, StudyLogDailyMissionService에서 @transactional 어노테이션 제거 * refactor: Facade 조회 메서드에 @Cacheable 및 등록, 수정, 삭제 메서드에 @CacheEvict 적용 * refactor: Facade에서 @service 어노테이션 -> @component 어노테이션 변경 * refactor: MemberQueryRepository, MemberQueryRepositoryAdapter에서 findMemberRoleById 메서드 반환 타입 변경 * refactor: TripCount -> TripCountInfo 이름 변경 * refactor: BatchJobConfig에서 Step/Tasklet 처리 로직을 제거하고, BatchStepConfig 및 HardDeleteTasklet 추가 * refactor: SchedulerConfig에서 배치 스케줄러 작업 로직을 BatchJobScheduler로 분리 * refactor: RedisCacheConfig 추가 및 TTL 전략 반영 * refactor: CacheNameConstants, CacheKeyFactory 추가 * test: MemberServiceTest를 삭제하고, MemberQueryServiceTest, MemberCommandServiceTest 단위 테스트 추가 * test: TripServiceTest를 삭제하고, TripQueryServiceTest, TripCommandServiceTest 단위 테스트 추가 * test: StampServiceTest를 삭제하고, StampQueryServiceTest, StampCommandServiceTest 단위 테스트 추가 * test: MissionServiceTest를 삭제하고, MissionQueryServiceTest, MissionCommandServiceTest 단위 테스트 추가 * test: StudyLogServiceTest를 삭제하고, StudyLogQueryServiceTest, StudyLogCommandServiceTest 단위 테스트 추가 * test: DailyMissionTest를 삭제하고, DailyMissionQueryServiceTest, DailyMissionCommandServiceTest 단위 테스트 추가 * test: StudyLogDailyMissionServiceTest를 삭제하고, StudyLogDailyMissionQueryServiceTest, StudyLogDailyMissionCommandServiceTest 단위 테스트 추가 * test: DailyGoalServiceTest를 삭제하고, DailyGoalQueryServiceTest, DailyGoalCommandServiceTest 단위 테스트 추가 * test: PomodoroServiceTest를 삭제하고, PomodoroQueryServiceTest, PomodoroCommandServiceTest 단위 테스트 추가 * test: UpdateStampOrderRequestFixture 추가 * test: DailyGoalFixture에서 createDeletedDailyGoal 메서드 삭제
1 parent 6ddacdb commit 9b80c2c

69 files changed

Lines changed: 3656 additions & 2790 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/com/ject/studytrip/auth/application/facade/AuthFacade.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import com.ject.studytrip.auth.presentation.dto.request.KakaoSignupRequest;
1212
import com.ject.studytrip.auth.presentation.dto.request.LogoutRequest;
1313
import com.ject.studytrip.member.application.dto.CreateMemberCommand;
14-
import com.ject.studytrip.member.application.service.MemberService;
14+
import com.ject.studytrip.member.application.service.MemberCommandService;
15+
import com.ject.studytrip.member.application.service.MemberQueryService;
1516
import com.ject.studytrip.member.domain.model.Member;
1617
import com.ject.studytrip.member.domain.model.SocialProvider;
1718
import lombok.RequiredArgsConstructor;
@@ -23,12 +24,13 @@ public class AuthFacade {
2324
private final KakaoLoginService kakaoLoginService;
2425
private final KakaoSignupProfileService kakaoSignupProfileService;
2526
private final TokenService tokenService;
26-
private final MemberService memberService;
27+
private final MemberQueryService memberQueryService;
28+
private final MemberCommandService memberCommandService;
2729

2830
public OAuthLoginOutcome kakaoLogin(KakaoLoginRequest request, String origin) {
2931
KakaoUserInfoResponse info = kakaoLoginService.getKakaoUserInfo(request.code(), origin);
3032

31-
return memberService
33+
return memberQueryService
3234
.getMemberBySocialProviderAndSocialId(SocialProvider.KAKAO, info.kakaoId())
3335
// 가입되어 있는 사용자인 경우 토큰 발급
3436
.map(
@@ -49,7 +51,7 @@ public TokenInfo kakaoSignup(String signupKey, KakaoSignupRequest request) {
4951
request.nickname(),
5052
request.category());
5153

52-
Member member = memberService.createMemberFromKakao(command);
54+
Member member = memberCommandService.createMemberFromKakao(command);
5355

5456
kakaoSignupProfileService.deleteBySignupKey(signupKey);
5557

@@ -58,7 +60,7 @@ public TokenInfo kakaoSignup(String signupKey, KakaoSignupRequest request) {
5860

5961
public TokenInfo reissueToken(String refreshToken) {
6062
String memberId = tokenService.getMemberIdByRefreshToken(refreshToken);
61-
String role = memberService.getRoleByMemberId(memberId);
63+
String role = memberQueryService.getRoleByMemberId(memberId);
6264

6365
return tokenService.reissueToken(refreshToken, memberId, role);
6466
}
Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
package com.ject.studytrip.cleanup.application.facade;
22

33
import com.ject.studytrip.cleanup.application.executor.HardDeleteExecutor;
4-
import com.ject.studytrip.member.application.service.MemberService;
5-
import com.ject.studytrip.mission.application.service.DailyMissionService;
6-
import com.ject.studytrip.mission.application.service.MissionService;
7-
import com.ject.studytrip.pomodoro.application.service.PomodoroService;
8-
import com.ject.studytrip.stamp.application.service.StampService;
9-
import com.ject.studytrip.studylog.application.service.StudyLogDailyMissionService;
10-
import com.ject.studytrip.studylog.application.service.StudyLogService;
11-
import com.ject.studytrip.trip.application.service.DailyGoalService;
12-
import com.ject.studytrip.trip.application.service.TripService;
4+
import com.ject.studytrip.member.application.service.MemberCommandService;
5+
import com.ject.studytrip.mission.application.service.DailyMissionCommandService;
6+
import com.ject.studytrip.mission.application.service.MissionCommandService;
7+
import com.ject.studytrip.pomodoro.application.service.PomodoroCommandService;
8+
import com.ject.studytrip.stamp.application.service.StampCommandService;
9+
import com.ject.studytrip.studylog.application.service.StudyLogCommandService;
10+
import com.ject.studytrip.studylog.application.service.StudyLogDailyMissionCommandService;
11+
import com.ject.studytrip.trip.application.service.DailyGoalCommandService;
12+
import com.ject.studytrip.trip.application.service.TripCommandService;
1313
import java.util.LinkedHashMap;
1414
import java.util.Map;
1515
import lombok.RequiredArgsConstructor;
1616
import org.springframework.stereotype.Component;
17+
import org.springframework.transaction.annotation.Propagation;
18+
import org.springframework.transaction.annotation.Transactional;
1719

1820
@Component
1921
@RequiredArgsConstructor
2022
public class HardDeleteFacade {
21-
private final PomodoroService pomodoroService;
22-
private final StudyLogDailyMissionService studyLogDailyMissionService;
23-
private final StudyLogService studyLogService;
24-
private final DailyMissionService dailyMissionService;
25-
private final MissionService missionService;
26-
private final StampService stampService;
27-
private final TripService tripService;
28-
private final DailyGoalService dailyGoalService;
29-
private final MemberService memberService;
23+
private final MemberCommandService memberCommandService;
24+
private final TripCommandService tripCommandService;
25+
private final StampCommandService stampCommandService;
26+
private final MissionCommandService missionCommandService;
27+
private final StudyLogCommandService studyLogCommandService;
28+
private final DailyMissionCommandService dailyMissionCommandService;
29+
private final StudyLogDailyMissionCommandService studyLogDailyMissionCommandService;
30+
private final DailyGoalCommandService dailyGoalCommandService;
31+
private final PomodoroCommandService pomodoroCommandService;
3032

3133
private final HardDeleteExecutor executor;
3234

@@ -59,6 +61,7 @@ public class HardDeleteFacade {
5961
private static final String DAILY_GOALS = "dailyGoals";
6062
private static final String MEMBERS = "members";
6163

64+
@Transactional(propagation = Propagation.REQUIRES_NEW)
6265
public void hardDeleteAll() {
6366
Map<String, Long> phases = new LinkedHashMap<>();
6467

@@ -78,97 +81,101 @@ private void deletePomodoros(Map<String, Long> phases) {
7881
POMODOROS_OWNED_BY_DELETED_DAILY_GOAL,
7982
executor.run(
8083
POMODOROS_OWNED_BY_DELETED_DAILY_GOAL,
81-
pomodoroService::hardDeletePomodorosOwnedByDeletedDailyGoal));
82-
phases.put(POMODOROS, executor.run(POMODOROS, pomodoroService::hardDeletePomodoros));
84+
pomodoroCommandService::hardDeletePomodorosOwnedByDeletedDailyGoal));
85+
phases.put(POMODOROS, executor.run(POMODOROS, pomodoroCommandService::hardDeletePomodoros));
8386
}
8487

8588
private void deleteStudyLogDailyMissions(Map<String, Long> phases) {
8689
phases.put(
8790
STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_MISSION,
8891
executor.run(
8992
STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_MISSION,
90-
studyLogDailyMissionService
93+
studyLogDailyMissionCommandService
9194
::hardDeleteStudyLogDailyMissionsOwnedByDeletedDailyMission));
9295
phases.put(
9396
STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_STUDY_LOG,
9497
executor.run(
9598
STUDY_LOG_DAILY_MISSIONS_OWNED_BY_DELETED_STUDY_LOG,
96-
studyLogDailyMissionService
99+
studyLogDailyMissionCommandService
97100
::hardDeleteStudyLogDailyMissionsOwnedByDeletedStudyLog));
98101
phases.put(
99102
STUDY_LOG_DAILY_MISSIONS,
100103
executor.run(
101104
STUDY_LOG_DAILY_MISSIONS,
102-
studyLogDailyMissionService::hardDeleteStudyLogDailyMissions));
105+
studyLogDailyMissionCommandService::hardDeleteStudyLogDailyMissions));
103106
}
104107

105108
private void deleteDailyMissions(Map<String, Long> phases) {
106109
phases.put(
107110
DAILY_MISSIONS_OWNED_BY_DELETED_MISSION,
108111
executor.run(
109112
DAILY_MISSIONS_OWNED_BY_DELETED_MISSION,
110-
dailyMissionService::hardDeleteDailyMissionsOwnedByDeletedMission));
113+
dailyMissionCommandService::hardDeleteDailyMissionsOwnedByDeletedMission));
111114
phases.put(
112115
DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL,
113116
executor.run(
114117
DAILY_MISSIONS_OWNED_BY_DELETED_DAILY_GOAL,
115-
dailyMissionService::hardDeleteDailyMissionsOwnedByDeletedDailyGoal));
118+
dailyMissionCommandService
119+
::hardDeleteDailyMissionsOwnedByDeletedDailyGoal));
116120
phases.put(
117121
DAILY_MISSIONS,
118-
executor.run(DAILY_MISSIONS, dailyMissionService::hardDeleteDailyMissions));
122+
executor.run(DAILY_MISSIONS, dailyMissionCommandService::hardDeleteDailyMissions));
119123
}
120124

121125
private void deleteStudyLogs(Map<String, Long> phases) {
122126
phases.put(
123127
STUDY_LOGS_OWNED_BY_DELETED_MEMBER,
124128
executor.run(
125129
STUDY_LOGS_OWNED_BY_DELETED_MEMBER,
126-
studyLogService::hardDeleteStudyLogsOwnedByDeletedMember));
130+
studyLogCommandService::hardDeleteStudyLogsOwnedByDeletedMember));
127131
phases.put(
128132
STUDY_LOGS_OWNED_BY_DELETED_DAILY_GOAL,
129133
executor.run(
130134
STUDY_LOGS_OWNED_BY_DELETED_DAILY_GOAL,
131-
studyLogService::hardDeleteStudyLogsOwnedByDeletedDailyGoal));
132-
phases.put(STUDY_LOGS, executor.run(STUDY_LOGS, studyLogService::hardDeleteStudyLogs));
135+
studyLogCommandService::hardDeleteStudyLogsOwnedByDeletedDailyGoal));
136+
phases.put(
137+
STUDY_LOGS, executor.run(STUDY_LOGS, studyLogCommandService::hardDeleteStudyLogs));
133138
}
134139

135140
private void deleteDailyGoals(Map<String, Long> phases) {
136141
phases.put(
137142
DAILY_GOALS_OWNED_BY_DELETED_TRIP,
138143
executor.run(
139144
DAILY_GOALS_OWNED_BY_DELETED_TRIP,
140-
dailyGoalService::hardDeleteDailyGoalsOwnedByDeletedTrip));
141-
phases.put(DAILY_GOALS, executor.run(DAILY_GOALS, dailyGoalService::hardDeleteDailyGoals));
145+
dailyGoalCommandService::hardDeleteDailyGoalsOwnedByDeletedTrip));
146+
phases.put(
147+
DAILY_GOALS,
148+
executor.run(DAILY_GOALS, dailyGoalCommandService::hardDeleteDailyGoals));
142149
}
143150

144151
private void deleteMissions(Map<String, Long> phases) {
145152
phases.put(
146153
MISSIONS_OWNED_BY_DELETED_STAMP,
147154
executor.run(
148155
MISSIONS_OWNED_BY_DELETED_STAMP,
149-
missionService::hardDeleteMissionsOwnedByDeletedStamp));
150-
phases.put(MISSIONS, executor.run(MISSIONS, missionService::hardDeleteMissions));
156+
missionCommandService::hardDeleteMissionsOwnedByDeletedStamp));
157+
phases.put(MISSIONS, executor.run(MISSIONS, missionCommandService::hardDeleteMissions));
151158
}
152159

153160
private void deleteStamps(Map<String, Long> phases) {
154161
phases.put(
155162
STAMPS_OWNED_BY_DELETED_TRIP,
156163
executor.run(
157164
STAMPS_OWNED_BY_DELETED_TRIP,
158-
stampService::hardDeleteStampsOwnedByDeletedTrip));
159-
phases.put(STAMPS, executor.run(STAMPS, stampService::hardDeleteStamps));
165+
stampCommandService::hardDeleteStampsOwnedByDeletedTrip));
166+
phases.put(STAMPS, executor.run(STAMPS, stampCommandService::hardDeleteStamps));
160167
}
161168

162169
private void deleteTrips(Map<String, Long> phases) {
163170
phases.put(
164171
TRIPS_OWNED_BY_DELETED_MEMBER,
165172
executor.run(
166173
TRIPS_OWNED_BY_DELETED_MEMBER,
167-
tripService::hardDeleteTripsOwnedByDeletedMember));
168-
phases.put(TRIPS, executor.run(TRIPS, tripService::hardDeleteTrips));
174+
tripCommandService::hardDeleteTripsOwnedByDeletedMember));
175+
phases.put(TRIPS, executor.run(TRIPS, tripCommandService::hardDeleteTrips));
169176
}
170177

171178
private void deleteMembers(Map<String, Long> phases) {
172-
phases.put(MEMBERS, executor.run(MEMBERS, memberService::hardDeleteMembers));
179+
phases.put(MEMBERS, executor.run(MEMBERS, memberCommandService::hardDeleteMembers));
173180
}
174181
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.ject.studytrip.global.batch.scheduler;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.batch.core.Job;
5+
import org.springframework.batch.core.JobParameters;
6+
import org.springframework.batch.core.JobParametersBuilder;
7+
import org.springframework.batch.core.launch.JobLauncher;
8+
import org.springframework.context.annotation.Profile;
9+
import org.springframework.scheduling.annotation.Scheduled;
10+
import org.springframework.stereotype.Component;
11+
12+
@Profile("!test")
13+
@Component
14+
@RequiredArgsConstructor
15+
public class BatchJobScheduler {
16+
private final JobLauncher jobLauncher;
17+
private final Job hardDeleteJob;
18+
19+
// 매일 04:00 (Asia/Seoul)
20+
@Scheduled(cron = "0 0 4 * * *", zone = "Asia/Seoul")
21+
public void runHardDeleteJob() throws Exception {
22+
JobParameters jobParameters =
23+
new JobParametersBuilder()
24+
.addLong("ts", System.currentTimeMillis())
25+
.toJobParameters();
26+
27+
jobLauncher.run(hardDeleteJob, jobParameters);
28+
}
29+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.ject.studytrip.global.batch.tasklet;
2+
3+
import com.ject.studytrip.cleanup.application.facade.HardDeleteFacade;
4+
import lombok.RequiredArgsConstructor;
5+
import org.springframework.batch.core.StepContribution;
6+
import org.springframework.batch.core.scope.context.ChunkContext;
7+
import org.springframework.batch.core.step.tasklet.Tasklet;
8+
import org.springframework.batch.repeat.RepeatStatus;
9+
import org.springframework.stereotype.Component;
10+
11+
@Component
12+
@RequiredArgsConstructor
13+
public class HardDeleteTasklet implements Tasklet {
14+
private final HardDeleteFacade hardDeleteFacade;
15+
16+
@Override
17+
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)
18+
throws Exception {
19+
hardDeleteFacade.hardDeleteAll();
20+
21+
return RepeatStatus.FINISHED;
22+
}
23+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.ject.studytrip.global.common.constants;
2+
3+
public final class CacheNameConstants {
4+
5+
private CacheNameConstants() {}
6+
7+
public static final String MEMBER = "member";
8+
public static final String TRIP = "trip";
9+
public static final String TRIPS = "trips";
10+
public static final String STAMP = "stamp";
11+
public static final String STAMPS = "stamps";
12+
public static final String MISSIONS = "missions";
13+
public static final String DAILY_GOAL = "dailyGoal";
14+
public static final String STUDY_LOGS = "studyLogs";
15+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.ject.studytrip.global.common.factory;
2+
3+
import lombok.AccessLevel;
4+
import lombok.NoArgsConstructor;
5+
6+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
7+
public class CacheKeyFactory {
8+
public static String member(Long memberId) {
9+
return "member:" + memberId;
10+
}
11+
12+
public static String trips(Long memberId, int page, int size) {
13+
return "member:" + memberId + ":page:" + page + ":size:" + size;
14+
}
15+
16+
public static String trip(Long memberId, Long tripId) {
17+
return "member:" + memberId + ":trip:" + tripId;
18+
}
19+
20+
public static String stamps(Long memberId, Long tripId) {
21+
return "member:" + memberId + ":trip:" + tripId;
22+
}
23+
24+
public static String stamp(Long memberId, Long tripId, Long stampId) {
25+
return "member:" + memberId + ":trip:" + tripId + ":stamp:" + stampId;
26+
}
27+
28+
public static String missions(Long memberId, Long tripId, Long stampId) {
29+
return "member:" + memberId + ":trip:" + tripId + ":stamp:" + stampId;
30+
}
31+
32+
public static String dailyGoal(Long memberId, Long tripId, Long dailyGoalId) {
33+
return "member:" + memberId + ":trip:" + tripId + ":dailyGoal:" + dailyGoalId;
34+
}
35+
36+
public static String studyLogs(Long memberId, Long tripId, int page, int size) {
37+
return "member:" + memberId + ":trip:" + tripId + ":page:" + page + ":size:" + size;
38+
}
39+
}
Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,26 @@
11
package com.ject.studytrip.global.config;
22

3-
import com.ject.studytrip.cleanup.application.facade.HardDeleteFacade;
43
import lombok.RequiredArgsConstructor;
54
import org.springframework.batch.core.Job;
65
import org.springframework.batch.core.Step;
76
import org.springframework.batch.core.job.builder.JobBuilder;
87
import org.springframework.batch.core.launch.support.RunIdIncrementer;
98
import org.springframework.batch.core.repository.JobRepository;
10-
import org.springframework.batch.core.step.builder.StepBuilder;
11-
import org.springframework.batch.core.step.tasklet.Tasklet;
12-
import org.springframework.batch.repeat.RepeatStatus;
139
import org.springframework.context.annotation.Bean;
1410
import org.springframework.context.annotation.Configuration;
15-
import org.springframework.transaction.PlatformTransactionManager;
1611

1712
@Configuration
1813
@RequiredArgsConstructor
1914
public class BatchJobConfig {
20-
private final JobRepository jobRepository;
21-
private final HardDeleteFacade hardDeleteFacade;
22-
private final PlatformTransactionManager transactionManager;
15+
private final Step hardDeleteStep;
2316

2417
private static final String HARD_DELETE_JOB_NAME = "hardDeleteJob";
25-
private static final String HARD_DELETE_STEP_NAME = "hardDeleteStep";
2618

2719
@Bean
28-
public Job hardDeleteJob(Step hardDeleteStep) {
20+
public Job hardDeleteJob(JobRepository jobRepository) {
2921
return new JobBuilder(HARD_DELETE_JOB_NAME, jobRepository)
3022
.incrementer(new RunIdIncrementer())
3123
.start(hardDeleteStep)
3224
.build();
3325
}
34-
35-
@Bean
36-
public Step hardDeleteStep(Tasklet hardDeleteTasklet) {
37-
return new StepBuilder(HARD_DELETE_STEP_NAME, jobRepository)
38-
.tasklet(hardDeleteTasklet, transactionManager)
39-
.build();
40-
}
41-
42-
@Bean
43-
public Tasklet hardDeleteTasklet() {
44-
return (contribution, chunkContext) -> {
45-
hardDeleteFacade.hardDeleteAll();
46-
return RepeatStatus.FINISHED;
47-
};
48-
}
4926
}

0 commit comments

Comments
 (0)