𝑺𝒕𝒖𝒅𝒚/디자인 패턴

[JAVA/Spring Boot] Facade 패턴으로 트랜잭션 타임아웃 처리 개선하기

📝 Facade Pattern(퍼사드 패턴)

  • (사전적 의미) 건축물의 정면
  • 복잡한 서브 클래스의 기능을 정리해서 상위 수준의 인터페이스를 제공하는 디자인 패턴
역할 설명
Facade 서브시스템 기능을 편리하게 사용할 수 있도록 복잡한 로직을 정리해 상위 수준의 인터페이스를 구성
Additional Facade 퍼사드 내부 코드의 양 증가 등으로 분리가 필요할 경우 도메인 별로 추가적인 퍼사드 객체를 생성해 사용
SubSystem 라이브러리 또는 클래스
Client 서브시스템에 직접 접근하지 않고 퍼사드에 접근해서 사용

⌨️ 코드 구현

문제 상황

하나의 서비스에서 외부 API 호출과 데이터 저장이 하나의 트랜잭션으로 묶여 있어 트랜잭션 시간 제한(3초)을 초과하는 상황

  • 외부 API 호출 포트로 외부 시스템에 데이터 요청 (3.5초 소요)
  • 저장 포트로 데이터베이스에 데이터 저장(예시에서는 로그로 대체)
@Slf4j
@Service
@RequiredArgsConstructor
public class DataFacade implements DataUseCase {

    private static final long TRANSACTION_TIMEOUT_SECONDS = 3L;

    private final ExternalApiPort externalApiPort;
    private final SavePort savePort;

    @Override
    public Data processData(String content) {
        log.info("트랜잭션 시작 - 내용: {}", content);

                /*
                (예제 코드) 외부 API 호출과 저장 로직이 하나의 트랜잭션으로 연결되어있다는 것을 표현
                */
        try {
            return CompletableFuture
                    .supplyAsync(() -> {
                            // callExternalApi 호출 시 3.5초 시간 소요
                        String id = externalApiPort.callExternalApi(content);
                        Data result = new Data(id, content);
                        savePort.save(result);
                        return result;
                    })
                    .orTimeout(TRANSACTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                    .join();
        } catch (Exception e) {
            log.error("트랜잭션 롤백", e);
            if (e.getCause() instanceof TimeoutException) {
                throw new RuntimeException("트랜잭션 시간 제한 초과: " + TRANSACTION_TIMEOUT_SECONDS + "초", e);
            }
            throw new RuntimeException("데이터 처리 실패", e);
        }
    }
}

제약사항

  • 트랜잭션 시간 변경 금지
  • 어댑터 수정 금지

전체 흐름

외부 API 호출을 트랜잭션 밖으로 분리하고, Facade가 전체 흐름을 관리하는 형태로 변경

Facade(DataFacade)

@Slf4j
@Service
@RequiredArgsConstructor
public class DataFacade implements DataUseCase {

    private static final long TRANSACTION_TIMEOUT_SECONDS = 3L;

    private final DataProcessService dataProcessService;
    private final DataSaveService dataSaveService;

    @Override
    public Data processData(String content) {
        log.info("트랜잭션 시작 - 내용: {}", content);

        try {
            Data result = dataProcessService.processData(content);
            dataSaveService.save(result);

            return result;
        } catch (Exception e) {
            log.error("트랜잭션 롤백", e);
            if (e.getCause() instanceof TimeoutException) {
                throw new RuntimeException("트랜잭션 시간 제한 초과: " + TRANSACTION_TIMEOUT_SECONDS + "초", e);
            }
            throw new RuntimeException("데이터 처리 실패", e);
        }
    }
}

SubSystem(DataProcessService, DataSaveService)

@Slf4j
@Service
@RequiredArgsConstructor
public class DataProcessService {

    private final ExternalApiPort externalApiPort;

    String getContentId(String content) {
        return externalApiPort.callExternalApi(content);
    }

    public Data processData(String content) {

        String id = this.getContentId(content);
        return new Data(id, content);
    }
}
@Slf4j
@Service
@RequiredArgsConstructor
public class DataSaveService {

    private final SavePort savePort;

    // 실제 DB를 사용하는 경우 이 메서드에 @Transactional 적용
    public void save(Data data) {
        savePort.save(data);
    }
}

🔗 참고 자료

 

💠 퍼사드(Facade) 패턴 - 완벽 마스터하기

Facade Pattern 퍼사드 패턴(Facade Pattern)은 사용하기 복잡한 클래스 라이브러리에 대해 사용하기 편하게 간편한 인터페이스(API)를 구성하기 위한 구조 패턴 이다. 예를들어 라이브러리의 각 클래스

inpa.tistory.com

 

[Design Pattern] Facade Pattern (퍼사드 패턴)

퍼사드 패턴(Facade Pattern)은 구조 패턴(Structural Pattern)의 한 종류로써, 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 패턴이다.퍼사드 객체(Facade Object)는

velog.io

 

## COMMENTS
> SEARCH: