Facade 디자인 패턴
클라이언트 코드에서 임의의 기능을 호출하기 할 때, 보다 단순한 인터페이스를 제공함으로써 클라이언트 코드에 부담을 줄여줄 수 있고,
결합도를 낮추어, 퍼사드 클래스가 사용하는 서브 시스템 클래스들의 기능이 수정되더라도, 클라이언트 코드를 수정하지 않아도 된다.
Facade 디자인 패턴은 서브 시스템 클래스를 캡슐화 하는 것이 목적이 아니고 그저 단순한 인터페이스를 제공하는 것이 목적이기 때문에, 클라이언트 코드에서 각 서브 시스템 클래스의 기능을 직접 호출해도 무관하다.
단점은 Wrapper 클래스가 많아져 그 개수가 많아질 경우 메모리 용량을 차지할 수 있다는 점이다.
이 디자인 패턴을 스프링에 적용한 예시를 살펴보자.
Facade 패턴 in Spring ! 😎
위와 같이 구성된 서비스 클래스가 있다고 하자.
구성도만 봐도 각 레파지토리에 대한 단위 테스트가 쉽지 않아 보인다.
또한 만약 아래와 같이 여러 서비스에서 A,B,C 레파지토리 들을 사용할 경우,
레파지토리 질의 결과 값에 대한 Optional 처리나 예외 처리코드가 A 서비스 클래스와 B 서비스 클래스에 중복으로 작성될 것이다.
그렇다면, 레파지토리 대신에 서비스를 주입 받으면 어떨까 ??
B 서비스에서 A 서비스가 필요해 주입 받을 경우, 순환 참조가 발생할 수 있다.
다시 문제로 돌아와서,
이 경우 Facade 패턴으로 풀어낼 수 있다.
각 레파지토리에 대한 단위 테스트가 보장되고, 질의 결과 처리에 대해 중복으로 코드를 작성하지 않아도 되며,
각 서비스 클래스들이 퍼사드 클래스를 주입 받을 경우가 없으므로, 순환 참조도 해결이 된다.
또한 퍼사드 패턴에서 비즈니스적으로 의미있는 메소드 명을 사용한다면, 클라이언트 측에서는 더 직관적으로 코드를 이해할 수 있다.
예시 코드를 보겠습니다!
퍼사드 패턴을 적용하기 전,
@Service
@RequiredArgsConstructor
@Transactional(readOnly=true)
public Class OrderService {
private final ItemRepository itemRepository;
private final UserRepository userRepository;
private final OrderRepository orderRepository;
@Transactional
public OrderDto createOrder ( OrderDto dto ) {
Item orderItem = findItem( dto.getItemId() );
User orderUSer = findUser( dto.getUserId() );
//Order entity 객체 생성
Order order = orderRepository.save( order );
//order 객체로 OrderDto 생성
return dto;
}
//itemRepo와 userRepo를 사용하는 서비스에서 중복될 코드
public Item findItem( String itemId ) {
return itemRepository.findByItemId( itemId ).orElseThrow( ()-> new Exception("물건 정보가 존재하지 않습니다"));
}
public User findUser ( String userId ) {
return userRepository.findByUserId( userId ).orElseThrow( () -> new Exception("일치하는 사용자 정보가 존재하지 않습니다"));
}
퍼사드 패턴 사용 후,
변경된 OrderService 코드
@Service
@RequiredArgsConstructor
public Class OrderService {
private final OrderRepository orderRepository;
@Transactional
public OrderDto createOrder ( OrderDto dto, Item item, User user ) { //직접 조회에서 param으로 정보를 받도록 변경
//Order entity 객체 생성
Order order = orderRepository.save( order );
//order 객체로 OrderDto 생성
return dto;
}
컨트롤러에서 호출할 퍼사드 서비스 클래스 코드
@Service
@RequiredArgsConstructor
public Class OrderFacadeService {
private final ItemService itemService;
private final UserService userService;
private final OrderService orderService;
public OrderDto makingUserOrder ( OrderDto dto ) {
Item orderItem = itemService.findItem( dto.getItemId() );
User orderUSer = userService.findUser( dto.getUserId() );
OrderDto dto = orderService.createOrder( dto, Item, User );
return dto;
}