-
Facade 패턴을 통해 코드의 복잡함을 숨겨서 간단해 보이도록 해보자Design Patterns 2023. 11. 8. 17:02
들어가기 전
이전 과정은 뇌를 빼고 작성했던 코드를 리팩토링 하는 과정을 거쳐봤다.
해당 글에서는 3가지 일을 맡고 계신 비즈니스 로직의 한 메소드를 디자인 패턴을 통해 간단하게 보이도록 해줄 예정이다.Facade 디자인 패턴
복잡한 로직을 하나로 합쳐서 외부에서는 간단하게 사용할 수 있도록 만드는 것이다.
즉 복잡한 로직을 하나의 인터페이스로 정리해 주는 것이다.장점은?
-
복잡한 시스템을 단순화하여 클라이언트가 쉽게 이해하고 사용할 수 있다.
-
각 도메인 로직에 대한 독립성을 높여 유지보수와 확장성을 향상시킬 수 있다.
-
도메인 서비스 간의 느슨한 결합을 통해 시스템 변경에 대한 클라이언트의 영향을 최소화한다.
단점은?
Facade 패턴을 구현한 클래스가 너무 많은 책임을 가지게 되면 복잡성이 증가할 수 있다.
정리해보면
여러 도메인의 서비스 계층의 독립성을 유지하여 단위 테스트를 쉽게 할 수 있도록 해준다.
그러면서 수행 해야할 동작은 하나로 합쳐서 관리를 할 수 있으니 코드의 응집도는 높아진다.도메인 자체의 코드는 간단해지고 여러 도메인이 필요해서 복잡해질 코드는 따로 빼서 관리할 수 있다는 것이다.
그럼 적용해보자
@Service public class AnswerService { private final AnswerRepository answerRepository; private final MemberService memberService; private final QuestionService questionService; private final AnswerMapper answerMapper; public AnswerService(AnswerRepository answerRepository, MemberService memberService, QuestionService questionService, AnswerMapper answerMapper) { this.answerRepository = answerRepository; this.memberService = memberService; this.questionService = questionService; this.answerMapper = answerMapper; } @Transactional public void create(AnswerRequest request, Long memberId) { Member findedMember = memberService.findById(memberId); Question findedQuestion = questionService.findById(request.questionId()); answerRepository.save(answerMapper.answerRequestToEntity(request, findedQuestion, findedMember)); } }
지금 create 메소드 하나 때문에 외부 도메인 서비스 로직이 2개에 의존성을 가지고 있다.
이 부분을 따로 Facade 패턴으로 정리해볼 것이다.
적용 후
@Service public class AnswerService { private final AnswerRepository answerRepository; private final AnswerFacade answerFacade; public AnswerService(AnswerRepository answerRepository, AnswerFacade answerFacade) { this.answerRepository = answerRepository; this.answerFacade = answerFacade; } @Transactional public void create(AnswerRequest request, Long memberId) { Answer answer = answerFacade.create(request, memberId); answerRepository.save(answer); } }
public interface AnswerFacade { Answer create(AnswerRequest request, Long memberId); }
@Component public class AnswerFacadeImpl implements AnswerFacade { private final MemberService memberService; private final QuestionService questionService; private final AnswerMapper answerMapper; public AnswerFacadeImpl(MemberService memberService, QuestionService questionService, AnswerMapper answerMapper) { this.memberService = memberService; this.questionService = questionService; this.answerMapper = answerMapper; } @Override @Transactional public Answer create(AnswerRequest request, Long memberId) { Member findedMember = memberService.findById(memberId); Question findedQuestion = questionService.findById(request.questionId()); return answerMapper.answerRequestToEntity(request, findedQuestion, findedMember); } }
answerService는 answerRepository와 answerFacade에만 의존성을 가지고 있고, 외부 도메인 서비스들과 의존성은 Facade쪽에서 담당하게 되었다.
여기서 @Transactional 어노테이션을 새로 달아준다.
Transactional은 propagation의 기본값은 REQUIRED 설정으로 부모 메소드가 Transactional이 설정되어있다면 새로운 트랙잭션을 만들지 않는다.
하지만 Facade 내의 메소드가 다른 곳에서 쓰일 수 있는점을 고려해서 새로 설정해준다.고민점
일단 Facade 패턴을 통해 외부 도메인과 분리할 곳은 분리하고 합칠 곳은 합치는 방법을 통해 정리를 해봤는데, AnswerMapper의 위치가 고민이 된다.
둘 다 위치해도 괜찮을지 좀 더 고민해보고 다시 정리해봐야겠다. -