한 서비스에서 여러 레포지토리를 참조하고 있는 게 서비스 입장에서 과도한 책임을 가지게 된다는 것을 알게 되었다.
이를 해결하기 위해 디자인 패턴 중 하나인 Facade Pattern을 적용하였다.
🍗 Facade Pattern 적용 후
PaymentService
- Facade Pattern을 적용한 후 PaymentFacadeRepository클래스만 참조하면 되기 때문에 책임이 줄어들었다.
- Interface를 만들지 고민했는데 기능을 확장할 가능성이 없으면, 구현체를 사용하는 것만으로 충분하다고 생각했다.
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentsConfirmClient paymentsConfirmClient;
private final PaymentsCancelClient paymentsCancelClient;
private final PaymentFacadeRepository paymentFacadeRepository;
@Transactional
public void callTossPayConfirm(ConfirmPaymentsRequest confirmPaymentsRequest) {
PaymentsResponse paymentsResponse =
paymentsConfirmClient.execute(
ConfirmPaymentsRequest.builder()
.paymentKey(confirmPaymentsRequest.getPaymentKey())
.orderId(confirmPaymentsRequest.getOrderId())
.amount(confirmPaymentsRequest.getAmount())
.build());
// PaymentsResponse paymentsResponse = PaymentsResponse.builder()
// .paymentKey(confirmPaymentsRequest.getPaymentKey())
// .requestedAt(ZonedDateTime.now())
// .approvedAt(ZonedDateTime.now())
// .build();
PaymentMethod paymentMethod = paymentFacadeRepository.findPaymentMethod();
PaymentStatus paymentStatus = paymentFacadeRepository.findPaymentStatus();
paymentFacadeRepository.savePayment(
Payment.createPayment(
confirmPaymentsRequest.getId(),
paymentsResponse,
paymentMethod,
paymentStatus));
}
@Transactional
public void callTossPayRefund(RefundOrderRequest refundOrderRequest) {
log.info(
"취소처리 "
+ refundOrderRequest.getOrderId()
+ " : "
+ refundOrderRequest.getCancelReason());
Payment payment =
paymentFacadeRepository.findPaymentByOrderId(refundOrderRequest.getOrderId());
PaymentsResponse paymentsResponse =
paymentsCancelClient.execute(
UUID.randomUUID().toString(), // Redis에 저장하는 로직으로 바꾸기
payment.getPaymentKey(),
CancelPaymentsRequest.builder()
.cancelReason(refundOrderRequest.getCancelReason())
.build());
paymentFacadeRepository.savePaymentCancel(payment, paymentsResponse);
payment.changePaymentStatus(paymentFacadeRepository.findPaymentStatusById());
}
}
PaymentFacadeRepository
- Repository들을 직접적으로 참조하는 클래스
@Repository
@RequiredArgsConstructor
public class PaymentFacadeRepository {
private final PaymentCancelRepository paymentCancelRepository;
private final PaymentRepository paymentRepository;
private final PaymentStatusRepository paymentStatusRepository;
private final PaymentMethodRepository paymentMethodRepository;
public PaymentMethod findPaymentMethod() {
return paymentMethodRepository
.findById(1)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_METHOD));
}
public PaymentStatus findPaymentStatus() {
return paymentStatusRepository
.findById(4)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_STATUS));
}
public void savePayment(Payment payment) {
paymentRepository.save(payment);
}
public Payment findPaymentByOrderId(final Long orderId) {
return paymentRepository
.findByOrderId(orderId)
.orElseThrow(() -> NotFoundPaymentException.EXCEPTION);
}
public void savePaymentCancel(final Payment payment, final PaymentsResponse paymentsResponse) {
paymentCancelRepository.save(
PaymentCancel.builder()
.payment(payment)
.approvedAt(paymentsResponse.getApprovedAt().toLocalDateTime())
.requestedAt(paymentsResponse.getRequestedAt().toLocalDateTime())
.amount(paymentsResponse.getTotalAmount())
.build());
}
public PaymentStatus findPaymentStatusById() {
return paymentStatusRepository
.findById(5)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_STATUS));
}
}
🐈⬛ Facade Pattern 적용 전
PaymentService
- 여러 레포지토리들을 직접 참조하니까 코드도 복잡해보인다.
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentsConfirmClient paymentsConfirmClient;
private final PaymentsCancelClient paymentsCancelClient;
private final PaymentCancelRepository paymentCancelRepository;
private final PaymentRepository paymentRepository;
private final PaymentStatusRepository paymentStatusRepository;
private final PaymentMethodRepository paymentMethodRepository;
@Transactional
public void callTossPayConfirm(ConfirmPaymentsRequest confirmPaymentsRequest) {
PaymentsResponse paymentsResponse =
paymentsConfirmClient.execute(
ConfirmPaymentsRequest.builder()
.paymentKey(confirmPaymentsRequest.getPaymentKey())
.orderId(confirmPaymentsRequest.getOrderId())
.amount(confirmPaymentsRequest.getAmount())
.build());
// PaymentsResponse paymentsResponse = PaymentsResponse.builder()
// .paymentKey(confirmPaymentsRequest.getPaymentKey())
// .requestedAt(ZonedDateTime.now())
// .approvedAt(ZonedDateTime.now())
// .build();
PaymentMethod paymentMethod =
paymentMethodRepository
.findById(1)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_METHOD));
PaymentStatus paymentStatus =
paymentStatusRepository
.findById(4)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_STATUS));
paymentRepository.save(
Payment.createPayment(
confirmPaymentsRequest.getId(),
paymentsResponse,
paymentMethod,
paymentStatus));
}
@Transactional
public void callTossPayRefund(RefundOrderRequest refundOrderRequest) {
log.info(
"취소처리 "
+ refundOrderRequest.getOrderId()
+ " : "
+ refundOrderRequest.getCancelReason());
Payment payment =
paymentRepository
.findByOrderId(refundOrderRequest.getOrderId())
.orElseThrow(() -> NotFoundPaymentException.EXCEPTION);
PaymentsResponse paymentsResponse =
paymentsCancelClient.execute(
UUID.randomUUID().toString(), // Redis에 저장하는 로직으로 바꾸기
payment.getPaymentKey(),
CancelPaymentsRequest.builder()
.cancelReason(refundOrderRequest.getCancelReason())
.build());
paymentCancelRepository.save(
PaymentCancel.builder()
.payment(payment)
.approvedAt(paymentsResponse.getApprovedAt().toLocalDateTime())
.requestedAt(paymentsResponse.getRequestedAt().toLocalDateTime())
.amount(paymentsResponse.getTotalAmount())
.build());
payment.changePaymentStatus(
paymentStatusRepository
.findById(5)
.orElseThrow(() -> new BadRequestException(NOT_FOUND_PAYMENT_STATUS)));
}
}
참고 👇👇👇
https://www.youtube.com/watch?v=mQlOqyFE3oI
반응형
'JAVA' 카테고리의 다른 글
설계 품질과 트레이드오프 | 책임 할당하기 | 메시지와 인터페이스 (0) | 2024.08.25 |
---|---|
이상한 나라의 객체 | 역할, 책임, 협력 (0) | 2024.07.15 |
HttpURLConnection | RestTemplate | WebClient (0) | 2023.12.28 |
enums(열거형) (0) | 2023.05.22 |
Thread(쓰레드) (1) | 2023.04.24 |