JAVA

Facade Pattern

잔망루피 2024. 7. 3. 23:02

 

한 서비스에서 여러 레포지토리를 참조하고 있는 게 서비스 입장에서 과도한 책임을 가지게 된다는 것을 알게 되었다.
이를 해결하기 위해 디자인 패턴 중 하나인 Facade Pattern을 적용하였다.

 

 

 

🍗 Facade Pattern 적용 후

Facade Pattern을 적용한 PaymentService 클래스 다이어그램

 

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://kchung1995.github.io/posts/%ED%95%98%EB%82%98%EC%9D%98-Service%EA%B0%80-%EC%97%AC%EB%9F%AC-Repository%EC%97%90-%EC%9D%98%EC%A1%B4%ED%95%A0-%EB%95%8C/

 

하나의 Service가 여러 Repository에 의존할 때

부제: 역할이 모호한 코드 명확하게 리팩토링하기

kchung1995.github.io

 

https://www.youtube.com/watch?v=mQlOqyFE3oI

 

 

반응형