프로젝트 구조 변경을 고민하게 된 배경이 몇가지 있다.
(1) layer 는 구분이 가지만, 파일이 많아져서 해당 layer 안의 특정 도메인을 한눈에 찾기가 어려워지고 있다. 또 그렇다보니 어떤 도메인 모델이 어떤 service 와 controller 로 이어지는지 알기도 힘들어질 것 같았다. 특히 새로운 개발자가 들어왔을때는 확실히 눈에 안보일 거 같았다.
(2) 또 파일 업로드 및 다운로드를 동료 개발자가 맡아서 처리하고 있는데, 해당 기능은 완전히 독립적인 서비스로 빠져도 될 것 같다는 생각이 들었다. (원격 서버에 실제 파일을 업로드하고 다운로드 하게 해주는 파일 서버). 같은 모듈 내에 있는 것보다는, 적어도 다른 모듈로 독립적으로 구성할 필요가 있다.
(3) 빅데이터 및 ML 동아리이기 때문에, 추후에 다른 학우들이 만든 ML 서비스를 웹 서비스에 올려서 사용할 수도 있겠다는 생각이 있었다. 그래서 기존 서비스 바탕 위에 새로운 서비스를 쉽게 추가할 수 있는 좋은 구조를 고민하고 있었다.
그래서 도메인 별로 프로젝트를 구성해보는 건 어떨까? 라는 생각에 여러 아키텍쳐를 알아보기 시작했다... (이 때부터 혼돈 ㄷㄷ)
마구잡이로 찾아보니, 아래처럼 4가지의 구성으로 정리가 되었다.
- 모노리스 (가장 단순한 단일 프로젝트)
- 멀티 모듈을 layer 별로 구성(core, service, api, 등) => 모노리스 (하나의 JVM)
- 멀티 모듈을 도메인 별로 구성하지만 => 모노리스 (하나의 JVM)
- 멀티 모듈을 도메인 별로 구성 => MSA (여러 JVM)
1. 모노리스
장점
- 가장 단순한 형태로 쉽고 빠르게 개발할 수 있다. (진짜 개발할 때는 단순하고 쉬운게 최고)
단점
- scale up 이 어렵다. 특정 서비스만 정교하게 확장하고자 할 때, 프로젝트를 통째로 늘려야할 수 밖에 없을 듯 하다.
- 오류가 전파되기 쉽다. 하나의 서비스에서 쉽게 다른 컴포넌트를 가져다 사용할 수 있는 만큼, 의존성 관리가 잘 안되는 경우가 많다. 그래서 하나의 컴포넌트에서 오류가 발생했을 때, 관련 서비스들이 모두 장애를 겪을 수 있다.
2. layer 별로 구성한 멀티 모듈 프로젝트
장점
- 흔하게 보이는 프로젝트 구조이다.
- 개발이나 설정이 쉬운편이다. 물론 단순 모노리스에 비해서는 패키지 의존성 관리를 더 신경쓰면서 해주어야한다.
- 프로젝트 구조상 패키지 의존성이나 모듈간의 의존성을 주의하면서 설계할 수 밖에 없다. 그래서 모노리스에 비해 의존성이 더 잘 정리되는 느낌이 있다.
단점
- 패키지 의존성 관리를 잘 못하면 모노리스보다 더한 참사가 일어난다. 빌드도 꼬이고, 괜히 더 무거운 프로그램이 될 수 있다.
- 결국은 모노리스에 비해, 패키지 의존성과 모듈 의존성 관리를 해주어야 한다는 점에서 trade off 가 있는 것 같다. 의무적으로 관리를 하게 되면서 얻는 장점도 있지만, 계속 신경써야한다.
- 다른 말로 하면, 의존성 관리를 잘 못하면 결국 모노리스와 같은 처지이지만 관리는 좀 더 힘든 상황에 처할 수 있다.
- 도메인 간 분리가 어려울 수 있다. 해당 layer 의 도메인이 다른 layer 의 도메인과 어떻게 이어지는 지 모를 수 있다.
3. 멀티 모듈을 도메인 별로 구성하지만 => 모노리스 (하나의 JVM)
장점
- 프로젝트가 서비스 단위로 나뉜다. => 해당 서비스 모듈에 대한 책임자가 명확히 보인다.
- 프로젝트 내에서 파일 찾으려, 다른 폴더 안뒤지고 있어도 된다. 대부분 같은 도메인 내에서 해결될테니까?
- 새로운 서비스를 추가할 때, 모듈을 새로 생성해서 작업한다. => 기존의 파일들과 엮이지 않고 분리된 환경에서 개발이 가능하다.
단점
- 여러 개의 서비스 모듈을 하나의 JVM 에 띄우기 위한 추가 작업이 필요하다.
- 다른 서비스 모듈 하위 spring bean 을 등록하기 위한 추가 작업 : componentScan 하는 시점 때문에, 다른 모듈에 있는 스프링 빈을 단순하게 componentScan 으로 같이 등록할 수 없다. 이것 때문에 작업을 더 해주어야한다.)
- 서비스 모듈별 설정파일과 패키지 의존성 관리에도 신경을 좀 써야한다.
- 이게 진짜 애매한게, 예를 들어 동아리 홈페이지 내부의 거의 모든 서비스는 로그인한 유저 정보가 필요하다. 근데 게시글 서비스에서 로그인유저가 필요하다고 할 때, UserService 빈을 들고와서 직접 조회를 할건지, 회원 api 에 restTemplate 등으로 질의해서 해당 멤버를 가져올건지를 결정해야한다.
- 전자를 선택하게 되면 프로젝트 구성은 도메인 별로 구분되었지만 진정한 의미의 도메인 컨텍스트 분리는 아니다. 또 추후 서비스 규모가 커져 MSA 로 전환하게 될때 다른 서비스 코드를 다 들어내야하는 작업을 해야한다. 하지만 후자를 선택하자니 서비스간 통신으로 인한 데이터 동기화 문제가 있다.
- 어쨌든 모노리스로 하려고 한다면 서비스 규모가 그리 크지는 않을테고 앞으로도 그리 크지 않을거라고 한다면, 의존성을 잘 제어한다는 전제로 다른 서비스 모듈 코드를 직접 가져오는게 어느 정도 적절한 선일 것 같다.
4. 멀티 모듈을 도메인 별로 구성 => MSA (여러 JVM)
장점
- 서비스 간 의존도가 가장 낮은 구조이다.
- 그렇다보니 서비스 별로 scale up 하기가 용이하다.
단점
- 서비스 간 통신에 신경을 많이 써야한다.
- 데이터 동기화 문제, 메세지 큐 관리, 서킷 브레이커, 로드밸런싱, 분산 db 등등 오버헤드가 어마무시하게 늘어난다.
일주일 동안 맨 땅에 헤딩하듯이 무턱대고 찾아봤는데,,, 머리가 너무 아프다...
이것 때문에 책 3권을 뚝딱 읽어버리고
("도메인 주도 설계 철저 입문", "도메인 주도 설계로 시작하는 마이크로 서비스 개발", "마스터링 스프링 클라우드")
여러 깃헙 레파지토리도 찾아보고 (인상 깊었던 레파지토리 : https://github.com/piomin/sample-spring-microservices-new, https://github.com/arawn/building-modular-monoliths-using-spring)
기타 블로그 글 및 레퍼런스들을 많이 읽어보면서
(서비스 별 멀티모듈 분리 : https://daddyprogrammer.org/post/13156/spring-boot-change-multi-module/)
(DDD 글 설명 잘 되어있는 글: https://medium.com/raa-labs/part-1-domain-driven-design-like-a-pro-f9e78d081f10)
여러가지 시도를 해봤는데,, (https://github.com/Dong-Hyeon-Yu/Inhabas.com-api/tree/study/multi-module, https://github.com/Dong-Hyeon-Yu/spring-cloud-monitoring-example )
잘 모르겠다... 허허허
지금 아키텍쳐를 바꾸는 비용이 얻는 거에 비해 훨씬 많이 들 거 같다.
(내가 너무 지치고,, 1주일 째 고민중이어서 더이상 시간끌면 진행이 안될듯.)
굳이 이 문제 때문에 프로젝트가 진행이 아예 안되는 상황도 아니어서
일단 이런 고민들을 갖고 계속 진행해보기로 한다.
일단 당장 작업해야하는 파일 서버는 새로운 모듈로 만들고
추후에 OAuth2 인증 서버를 분리해내서 + cloud 게이트웨이랑 붙여야겠다.
그러면 (인증서버, 리소스서버, 파일서버) 이렇게 나중에 분리가 될 예정이다.
단일 모노리스 프로젝트라도,
bounded context 를 잘 정의하고
응집과 결합를 잘 다스린다면,
괜찮겠지 허허
도메인을 잘 나누고, 의존성을 어떻게 하면 낮추면서 좋은 아키텍쳐를 만들어 갈 수 있을지 계속 고민해봐야겠다.
힘들다. 이런 개발.