-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BE] feat: 의존성을 정리한다(#643) #644
Conversation
Test Results 92 files 92 suites 7s ⏱️ Results for commit 2796e7c. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 푸우!
몇 가지 리뷰 남겼습니다.
조회가 목적인 Query 서비스와 비즈니스 로직이 담긴 명령이 목적인 Command 서비스(이름이 Command가 아니더라도)를 분리하면 좋겠네요.
지금 PR의 목적은 의존성의 정리이니, 엔티티의 변경이 생기는 부분은 추후 새로운 이슈를 만들어 해결하면 좋을 것 같습니다!
(MemberTicket의 Ticket 의존성 추가, Stage 의존성 제거)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
@Async | ||
@Transactional | ||
public void deleteMember(DeleteMemberEvent event) { | ||
memberFCMRepository.deleteAllByMemberId(event.memberId()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberRegisterEventListner
클래스의 역할과 달라보여서, 해당 이벤트 핸들러만 구현하는 새로운 클래스를 만드는 건 어떤가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋습니다 반영했어요!!
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
@Async | ||
@Transactional | ||
public void registerNewMember(MemberRegisterEvent event) { | ||
if (event.isNew()) { | ||
saveNewMemberFCM(event.accessToken(), event.fcmToken()); | ||
return; | ||
} | ||
saveOriginMemberFCM(event.accessToken(), event.fcmToken()); | ||
} | ||
|
||
private void saveNewMemberFCM(String accessToken, String fcmToken) { | ||
Long memberId = extractMemberId(accessToken); | ||
memberFCMRepository.save(new MemberFCM(memberId, fcmToken)); | ||
} | ||
|
||
private Long extractMemberId(String accessToken) { | ||
AuthPayload authPayload = authExtractor.extract(accessToken); | ||
return authPayload.getMemberId(); | ||
} | ||
|
||
private void saveOriginMemberFCM(String accessToken, String fcmToken) { | ||
Long memberId = extractMemberId(accessToken); | ||
Optional<MemberFCM> memberFCM = memberFCMRepository.findMemberFCMByMemberIdAndFcmToken(memberId, fcmToken); | ||
if (memberFCM.isEmpty()) { | ||
memberFCMRepository.save(new MemberFCM(memberId, fcmToken)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추후 #579 이슈가 처리된다면 해당 코드는 크게 바뀔 가능성이 높지만..
해당 로직을 단순화하면 다음과 같이 리팩터링 할 수 있을 것 같네요.
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Async
@Transactional
public void registerNewMember(MemberRegisterEvent event) {
Long memberId = extractMemberId(event.accessToken());
String fcmToken = event.fcmToken();
memberFCMRepository.findByMemberIdAndFcmToken(memberId, fcmToken)
.orElseGet(() -> memberFCMRepository.save(new MemberFCM(memberId, fcmToken)));
}
private Long extractMemberId(String accessToken) {
AuthPayload authPayload = authExtractor.extract(accessToken);
return authPayload.getMemberId();
}
여기서 봐야할 건 MemberRegisterEvent
가 isNew 라는 필드를 가져야하나 싶어요.
메타데이터로 사용된다면 isNew가 true일 때 SELECT 쿼리를 날리지 않고, 바로 save를 하여 성능을 조금 챙길 수 있다는 장점이 있겠네요.
isNew 필드를 생략한다면 AuthController
의 로직 또한 다음과 같이 변경이 가능해요.
@PostMapping("/oauth2")
@Operation(description = "소셜 엑세스 토큰을 기반으로 로그인 요청을 보낸다.", summary = "OAuth2 로그인")
public ResponseEntity<LoginResponse> login(@RequestBody @Valid LoginRequest request) {
LoginResponse response = authFacadeService.login(request.socialType(), request.accessToken());
publisher.publishEvent(new MemberRegisterEvent(request.accessToken(), request.fcmToken()));
return ResponseEntity.ok()
.body(response);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
원래는 조회 쿼리 한 번을 줄이자는 생각이었는데 큰 연산이 아니란 생각이드네요!
작성 당시에는 어차피 새로운 멤버 여부를 알 수 있는 데이터가 있으니 주어진 데이터를 활용해서 최적화해보자 라는 생각이었지만
오히려 복잡해보이는 부분이 더 큰 단점으로 다가오네요 반영했습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PostMapping("/oauth2")
@Operation(description = "소셜 엑세스 토큰을 기반으로 로그인 요청을 보낸다.", summary = "OAuth2 로그인")
public ResponseEntity<LoginResponse> login(@RequestBody @Valid LoginRequest request) {
LoginResponse response = authFacadeService.login(request.socialType(), request.accessToken());
publisher.publishEvent(new MemberRegisterEvent(request.accessToken(), request.fcmToken()));
return ResponseEntity.ok()
.body(response);
}
여기서
new MemberRegisterEvent(response.accessToken(), request.fcmToken());
request -> response 오타 수정해서 반영했어요
|
||
private void saveOriginMemberFCM(String accessToken, String fcmToken) { | ||
Long memberId = extractMemberId(accessToken); | ||
Optional<MemberFCM> memberFCM = memberFCMRepository.findMemberFCMByMemberIdAndFcmToken(memberId, fcmToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memberFCMRepository
의 findMemberFCMByMemberIdAndFcmToken()
메서드 명을 findByMemberIdAndFcmToken()
로 줄일 수 있겠네요.
import com.festago.festival.dto.DetailFestivalResponse; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
인터페이스에 붙은 @Component
는 지워도 좋을 것 같아요!
public interface FestivalStageService { | ||
|
||
DetailFestivalResponse findDetail(Long festivalId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 인터페이스가 필요한 이유가 Festival
이 Stage
에 대한 의존이 없어야 하지만, 조회 기능 때문에 반드시 Stage
에 대한 의존이 필요하니 해당 의존성을 분리하려고 만든 특수한 목적의 인터페이스라고 이해했습니다!
하지만 단순히 이름만 보았을 때 해당 인터페이스가 조회만 하는 특수한 목적의 인터페이스라고 알기 힘드니, FestivalStageQueryService
같이 명확한 이름을 부여해주는 것은 어떤가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 결국 모든 조회 로직마다 의존성을 없애는게 어렵다
라는 이야기를 회의 때 했던 것처럼
(이론적으로 가능은 한데, 하나 하나 인터페이스가 생겨야한다.)
조회는 별도의 QueryService로 하고, QueryService에서만 양방향 의존성을 감수하는게 좋을 것 같다는 생각이 드네요.
즉, 변경이 여러 지점에 있지만, 그 클래스는 최소화 하는 형태로?
명확하게 할려면 application/query 이런식으로 패키지를 한번 더 나눌 수도 있을 거같은데, 어차피 application에 존재하는 serivce가 몇개 없을거라 패키지를 안나눠도 괜찮다는 생각은 듭니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쿼리 서비스 분리는 작업 단위가 커지는 것 같아 별도의 Issue로 처리할게요!
.orElseThrow(() -> new NotFoundException(ErrorCode.FESTIVAL_NOT_FOUND)); | ||
} | ||
|
||
private DetailStageResponse makeResponse(Stage stage) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make라는 이름보다는 create는 어떤가요?
private Function<Ticket, DetailTicketResponse> makeTicketResponse() { | ||
return ticket -> { | ||
TicketAmount ticketAmount = ticket.getTicketAmount(); | ||
return new DetailTicketResponse( | ||
ticket.getId(), | ||
ticket.getTicketType().name(), | ||
ticketAmount.getTotalAmount(), | ||
ticketAmount.calculateRemainAmount() | ||
); | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return 타입을 DetailTicketResponse
로 반환하는건 어떤가요?
private DetailTicketResponse createDetailTicketResponse(Ticket ticket) {
TicketAmount ticketAmount = ticket.getTicketAmount();
return new DetailTicketResponse(
ticket.getId(),
ticket.getTicketType().name(),
ticketAmount.getTotalAmount(),
ticketAmount.calculateRemainAmount()
);
}
public class ReservationSequence { | ||
|
||
private static final int MOST_FAST_SEQUENCE = 1; | ||
private final int value; | ||
|
||
public ReservationSequence(int value) { | ||
validate(value); | ||
this.value = value; | ||
} | ||
|
||
private void validate(int sequence) { | ||
if (sequence < MOST_FAST_SEQUENCE) { | ||
throw new InternalServerException(ErrorCode.TICKET_SEQUENCE_DATA_ERROR); | ||
} | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 값 타입 객체를 만들어 사용해주신 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reservation 이란 값을 Service 에서 MemberTicket -> Ticket 의 메서드까지 타고가면서 잘못된 값이 들어갔을 때 검증이 너무 늦게 발생한다는 문제가 발생해서 값 객체로 선언했습니다!
public class TicketInfo { | ||
|
||
private final Stage stage; | ||
private final ReservationSequence sequence; | ||
private final LocalDateTime entryTime; | ||
private final TicketType ticketType; | ||
|
||
public TicketInfo(Stage stage, ReservationSequence sequence, LocalDateTime entryTime, | ||
TicketType ticketType) { | ||
validate(stage, sequence, entryTime, ticketType); | ||
this.stage = stage; | ||
this.sequence = sequence; | ||
this.entryTime = entryTime; | ||
this.ticketType = ticketType; | ||
} | ||
|
||
private void validate(Stage stage, ReservationSequence sequence, LocalDateTime entryTime, TicketType ticketType) { | ||
validateStage(stage); | ||
validateSequence(sequence); | ||
validateEntryTime(entryTime); | ||
validateTicketType(ticketType); | ||
} | ||
|
||
private void validateStage(Stage stage) { | ||
Validator.notNull(stage, "stage"); | ||
} | ||
|
||
private void validateSequence(ReservationSequence sequence) { | ||
Validator.notNull(sequence, "sequence"); | ||
} | ||
|
||
private void validateEntryTime(LocalDateTime entryTime) { | ||
Validator.notNull(entryTime, "entryTime"); | ||
} | ||
|
||
private void validateTicketType(TicketType ticketType) { | ||
Validator.notNull(ticketType, "ticketType"); | ||
} | ||
|
||
public Stage getStage() { | ||
return stage; | ||
} | ||
|
||
public ReservationSequence getSequence() { | ||
return sequence; | ||
} | ||
|
||
public LocalDateTime getEntryTime() { | ||
return entryTime; | ||
} | ||
|
||
public TicketType getTicketType() { | ||
return ticketType; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 클래스는 DTO 용도의 클래스로 보이는데 record로 변환한다면 다음과 같이 변경할 수 있습니다!
compact construtor를 사용하여 코드를 최소화 할 수 있습니다.
자세한 내용은 링크 참조하시면 좋을 것 같네요.
public record TicketInfo(
Stage stage,
ReservationSequence sequence,
LocalDateTime entryTime,
TicketType ticketType
) {
public TicketInfo {
validate(stage, sequence, entryTime, ticketType);
}
private void validate(Stage stage, ReservationSequence sequence, LocalDateTime entryTime, TicketType ticketType) {
validateStage(stage);
validateSequence(sequence);
validateEntryTime(entryTime);
validateTicketType(ticketType);
}
private void validateStage(Stage stage) {
Validator.notNull(stage, "stage");
}
private void validateSequence(ReservationSequence sequence) {
Validator.notNull(sequence, "sequence");
}
private void validateEntryTime(LocalDateTime entryTime) {
Validator.notNull(entryTime, "entryTime");
}
private void validateTicketType(TicketType ticketType) {
Validator.notNull(ticketType, "ticketType");
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한 이름을 TicketInfo
보단, 예매를 위한 정보를 전송하므로 TicketReserveInfo
와 같이 명확하게 변경해도 좋을 것 같네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
record 를 안 썼던 이유가 해당 객체를 domain 간에 주고 받기 때문에 값 객체라 생각하였었습니다.
record 로 선언해도 좋을 것 같네요!
public static MemberTicket createMemberTicket(Ticket ticket, Member member, ReservationSequence sequence, | ||
LocalDateTime currentTime) { | ||
if (ticket.alreadyStart(currentTime)) { | ||
throw new BadRequestException(ErrorCode.TICKET_CANNOT_RESERVE_STAGE_START); | ||
} | ||
|
||
TicketInfo ticketInfo = extractTicketInfo(ticket, sequence); | ||
return new MemberTicket( | ||
member, | ||
ticketInfo.getStage(), | ||
ticketInfo.getSequence().getValue(), | ||
ticketInfo.getEntryTime(), | ||
ticketInfo.getTicketType() | ||
); | ||
} | ||
|
||
private static TicketInfo extractTicketInfo(Ticket ticket, ReservationSequence sequence) { | ||
return ticket.extractTicketInfo(sequence); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 메서드가 필요한 이유는 MemberTicket
을 생성할 때 Ticket의 Stage가 시작되었는지 확인하기 위해서 같네요.
MemberTicket
의 생성자로 Stage가 아닌 Ticket을 받는게 더 적절하지 않나 싶어요.
왜냐하면 MemberTicket
가 참조하는 Stage는 단순히 조회에서 사용되더군요. (그것도 식별자만)
그렇게 한다면 TicketInfo
라는 DTO는 사용하지 않아도 될 것 같네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 굳이 이제 이 로직이 static factory method일 필요가 있나 싶어요.
Ticket이 아니라 Stage가 필요한거라 TicketInfo를 받거나 아니면 getter로 하나씩 꺼내줘서 생성자로 만들어주는게 좋다는 생각이 드네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 메서드가 필요한 이유는 MemberTicket을 생성할 때 Ticket의 Stage가 시작되었는지 확인하기 위해서 같네요.
MemberTicket의 생성자로 Stage가 아닌 Ticket을 받는게 더 적절하지 않나 싶어요.
왜냐하면 MemberTicket가 참조하는 Stage는 단순히 조회에서 사용되더군요. (그것도 식별자만)
그렇게 한다면 TicketInfo라는 DTO는 사용하지 않아도 될 것 같네요.
맞아요.. 그래서 저희 MemberTicket 에 Ticket 참조를 갖도록 변경하는 작업을 진행하는 것이 좋을까요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 굳이 이제 이 로직이 static factory method일 필요가 있나 싶어요.
Ticket이 아니라 Stage가 필요한거라 TicketInfo를 받거나 아니면 getter로 하나씩 꺼내줘서 생성자로 만들어주는게 좋다는 생각이 드네요.
Service 에서 MemberTicket을 만드는 것을 생각하신 건가요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberTicket의 생성자로 만들되 필요한 인자를 꺼내서 넣어주는 걸 생각했었어요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
간단한 코멘트를 남겨보았습니다.
public static MemberTicket createMemberTicket(Ticket ticket, Member member, ReservationSequence sequence, | ||
LocalDateTime currentTime) { | ||
if (ticket.alreadyStart(currentTime)) { | ||
throw new BadRequestException(ErrorCode.TICKET_CANNOT_RESERVE_STAGE_START); | ||
} | ||
|
||
TicketInfo ticketInfo = extractTicketInfo(ticket, sequence); | ||
return new MemberTicket( | ||
member, | ||
ticketInfo.getStage(), | ||
ticketInfo.getSequence().getValue(), | ||
ticketInfo.getEntryTime(), | ||
ticketInfo.getTicketType() | ||
); | ||
} | ||
|
||
private static TicketInfo extractTicketInfo(Ticket ticket, ReservationSequence sequence) { | ||
return ticket.extractTicketInfo(sequence); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 굳이 이제 이 로직이 static factory method일 필요가 있나 싶어요.
Ticket이 아니라 Stage가 필요한거라 TicketInfo를 받거나 아니면 getter로 하나씩 꺼내줘서 생성자로 만들어주는게 좋다는 생각이 드네요.
public interface FestivalStageService { | ||
|
||
DetailFestivalResponse findDetail(Long festivalId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 결국 모든 조회 로직마다 의존성을 없애는게 어렵다
라는 이야기를 회의 때 했던 것처럼
(이론적으로 가능은 한데, 하나 하나 인터페이스가 생겨야한다.)
조회는 별도의 QueryService로 하고, QueryService에서만 양방향 의존성을 감수하는게 좋을 것 같다는 생각이 드네요.
즉, 변경이 여러 지점에 있지만, 그 클래스는 최소화 하는 형태로?
명확하게 할려면 application/query 이런식으로 패키지를 한번 더 나눌 수도 있을 거같은데, 어차피 application에 존재하는 serivce가 몇개 없을거라 패키지를 안나눠도 괜찮다는 생각은 듭니다.
@@ -24,7 +26,7 @@ | |||
public class AuthController { | |||
|
|||
private final AuthFacadeService authFacadeService; | |||
private final MemberFCMService memberFCMService; | |||
private final ApplicationEventPublisher publisher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApplicationEventPublisher
가 AuthFacadeService
내부에서 이벤트를 발행해주는 건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동의합니다! 이벤트 발행은 서비스 내부에서 하는게 통일성있을 것 같아요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그러면 Auth Service 가 fcm 을 알게되는데 괜찮을까용??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신경 쓰이긴한데, 사실 Controller에서 알기 때문에 패키지 의존성은 상관 없지않을까요..?
public DetailFestivalResponse findDetail(Long festivalId) { | ||
Festival festival = findFestival(festivalId); | ||
return stageRepository.findAllDetailByFestivalId(festivalId).stream() | ||
.sorted(comparing(Stage::getStartTime)) | ||
.map(this::makeResponse) | ||
.collect(collectingAndThen( | ||
toList(), | ||
stageResponses -> new DetailFestivalResponse( | ||
festivalId, | ||
festival.getSchool().getId(), | ||
festival.getName(), | ||
festival.getStartDate(), | ||
festival.getEndDate(), | ||
festival.getThumbnail(), | ||
stageResponses | ||
) | ||
)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분을 Festival이랑 List를 DetailFestivalResponse 생성자로 받아서 처리하는 것도 괜찮겠네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DetailFestivalResposne, DetailStageResponse, DetailTicketResponse는 양방향 참조를 피하기 위해 festival 패키지에 모두 같이 있어야합니다!!
그래서 해당 Dto 에 Stage, Ticket이 넘어올 경우 festival <-> Stage, Ticket 에 대한 의존성이 생기기 때문에 이렇게 Service 에서 직접 만들어주었습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
글렌과 오리가 자세하게 코멘트 남겨주셨네요 😁
간단한 코멘트 남기고 갑니다!!
public record DeleteMemberEvent( | ||
Long memberId | ||
) { | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Event 이름이 얘는 DeleteMemberEvent
이고 다른 이벤트는 MemberRegisterEvent
인데 통일하면 좋을 것 같아요!
@@ -24,7 +26,7 @@ | |||
public class AuthController { | |||
|
|||
private final AuthFacadeService authFacadeService; | |||
private final MemberFCMService memberFCMService; | |||
private final ApplicationEventPublisher publisher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동의합니다! 이벤트 발행은 서비스 내부에서 하는게 통일성있을 것 같아요!
public record DetailFestivalResponse( | ||
Long id, | ||
Long schoolId, | ||
String name, | ||
LocalDate startDate, | ||
LocalDate endDate, | ||
String thumbnail, | ||
List<DetailStageResponse> stages) { | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에 보면 헷갈릴 수 있다는 글렌의 말씀에 동의합니다!
다만 글렌이 제시해주신 방법처럼 한다면 외부에서 StageResponse
를 사용할 때, 실제 StageResponse.java 클래스의 응답객체와 DeatilFestivalResponse 내부 StageResponse 둘 다 노출되어 헷갈릴수도 있겠네요..
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class FestivalStageServiceImpl implements FestivalStageService { | ||
|
||
private final StageRepository stageRepository; | ||
private final FestivalRepository festivalRepository; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 서비스를 보니까, 조회와 명령 서비스를 분리하는 이유가 더 잘 보이긴하네요..
다른 비즈니스로직을 수행하는 메서드와 얘가 섞여있었다면 비즈니스로직이 눈에 보이지 않을 것 같네요
private static final int MOST_FAST_SEQUENCE = 1; | ||
private final int value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상수와 변수 사이 한 칸 공백 넣어주는건 어떨까용
public class EntryPoint { | ||
|
||
} | ||
// TODO: Cucumber 어쩔티비냐 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥒
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 반영 모두 확인했습니다!
고생하셨습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 푸우
코멘트 달아주신 거 덕분에 구현 방식에 관한 부분이 대부분 공감됐습니다.
@@ -24,7 +26,7 @@ | |||
public class AuthController { | |||
|
|||
private final AuthFacadeService authFacadeService; | |||
private final MemberFCMService memberFCMService; | |||
private final ApplicationEventPublisher publisher; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신경 쓰이긴한데, 사실 Controller에서 알기 때문에 패키지 의존성은 상관 없지않을까요..?
public static MemberTicket createMemberTicket(Ticket ticket, Member member, ReservationSequence sequence, | ||
LocalDateTime currentTime) { | ||
if (ticket.alreadyStart(currentTime)) { | ||
throw new BadRequestException(ErrorCode.TICKET_CANNOT_RESERVE_STAGE_START); | ||
} | ||
|
||
TicketInfo ticketInfo = extractTicketInfo(ticket, sequence); | ||
return new MemberTicket( | ||
member, | ||
ticketInfo.getStage(), | ||
ticketInfo.getSequence().getValue(), | ||
ticketInfo.getEntryTime(), | ||
ticketInfo.getTicketType() | ||
); | ||
} | ||
|
||
private static TicketInfo extractTicketInfo(Ticket ticket, ReservationSequence sequence) { | ||
return ticket.extractTicketInfo(sequence); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MemberTicket의 생성자로 만들되 필요한 인자를 꺼내서 넣어주는 걸 생각했었어요!
📌 관련 이슈
✨ PR 세부 내용
의존성 정리를 진행하였고,
AuthController 에서 evnet 를 발행하고 있는데 이는 AuthServier 에 fcm 을 모르도록 만들기 위함입니다.
추후에 fcm 분리하면 생성 시점에는 해결될 것이고 삭제에는 마찬가지로 event 를 발행해야할 것 같습니다.