이번 프로젝트에는 여행 기록(Record)을 저장할 수 있는 기능이 있었다.
이 여행 기록에는 0 ~ 10개의 이미지(RecordImage)가 포함되어있고, 여행 기록을 생성하면 사용자에게 뱃지(Badge)가 부여되어야 했다.
결국 한 기능에서 Record, RecordImage, Badge 세 종류의 데이터를 모두 다뤄야 하는 상황이었다.
처음에는 당연히 책임 분리를 위해 3개의 Service로 분리하여 아래와 같은 구조를 생각했지만, 생각하면 할 수록 이게 맞는 구조인가에 대한 의문이 생기기 시작했다.
이러한 의문은 Layered Architecture의 단방향 의존 원칙으로부터 시작됐다.
Layered Architecture의 단방향 의존 원칙에 따르면, 상위 계층은 하위 계층에만 의존하도록 설계해야 한다.
이때, Service 계층이 다른 Service 계층에 의존하는 구조는 같은 계층 간의 의존으로, 단방향 의존 원칙에 위배되는 것이 아닌가 하는 의문이 들었다.
이에 대해 고민하다 보니, 다음과 같은 결론에 이르렀다.
원칙이라는 것은 결국 잘못된 구조로 인해 발생할 수 있는 문제를 예방하기 위해 존재하는 것일 것이다.
따라서 원칙에 위배되는지 여부보다는, 이 구조가 잠재적으로 문제를 유발할 가능성이 있는지가 더 중요한 것 아닐까 ?
그래서 단방향 의존 원칙이 어떤 문제를 해결하고자 탄생한 원칙인지 알아보았다.
1. 순환 의존성 문제 방지
- 상위 계층이 하위 계층에 의존하고, 다시 하위 계층이 상위 계층에 의존할 경우 재사용성을 저해하고 오류가 발생할 가능성이 높아진다.
- 단방향 원칙을 통해 이러한 순환 의존 문제를 방지할 수 있다.
2. 계층 간 결합도 상승 완화
- 계층 간 결합도가 높아지면, 한 계층의 변경이 다른 계층으로 쉽게 전파되어 유지보수성을 저해한다.
- 단방향 원칙을 통해 결합도를 낮추고, 변경 전파를 완화할 수 있다.
- 다만, 완전 방지는 불가능하므로 인터페이스, 추상화 등을 통해 영향을 최소화하는 추가적인 조치가 필요하다.
3. 복잡한 의존 관계 방지
- 복잡한 의존 관계는 가독성을 저해하고, 독립적인 테스트를 어렵게 한다.
- 단방향 의존 원칙을 통해 의존 관계의 방향을 명확히 분리하여, 구조를 쉽게 이해할 수 있게 하고 테스트를 용이하게 할 수 있다.
따라서, 처음 고민의 시작이 되었던 Record, RecordImage, Badge에 대한 구조는 Service 간의 의존 관계가 큰 문제를 유발할 가능성은 현저히 낮아보인다.
그러나 많은 Service들이 서로를 의존하여 복잡한 구조가 된다면 역방향 의존 구조만큼은 아니지만, 위 3가지 문제들, 특히 순환 의존 문제를 모두 유발할 가능성이 있다.
하지만, 비즈니스 로직들은 서로 관련될 수 밖에 없는 경우가 많다.
그리고 책임 분리를 명확히 하기 위해서는 이러한 구조를 피하기 힘들다.
그럼 위 문제들을 예방하면서도 책임 분리를 명확히 할 수 있는 방법은 없을까 ?
이를 해결할 수 있는 방법을 조사하다가, Facade 패턴에 대해 알게 되었다.
Facade 패턴은 복잡한 하위 시스템에 대해 통합적인 인터페이스를 제공하는 디자인 패턴이다.
기존 3-Tier Layered Architecture에 Facade 패턴을 조합하면 책임 분리를 명확하게 하면서도 단방향 의존 원칙을 지킬 수 있을 것 같다.
단순한 방법이지만, Presentation 계층과 Business Logic 계층 사이에 하나의 계층(Facade)을 추가함으로써 Business Logic 계층 간의 복잡한 의존 관계를 Facade가 중재해준다.
이를 통해 단방향 의존 원칙을 준수하면서도 Business Logic 계층의 책임 분리 또한 챙길 수 있다.
[Spring Boot] Service에서 다른 Service 의존에 대하여
Service에서 다른 Service를 의존하게 된다면 ?