패키지, 모듈, 서버 나누는 기준은?
패키지 vs 모듈 나누는 기준
패키지가 아니라 별도의 (멀티 모듈, 서브 모듈)로 구성했을 때의 장점은?
- ▲ 관심 분리를 통한 스파게티 코드 방지, 변경 범위 축소
- 왜 세모? 패키지만 나눠도 효과를 볼 수 있는 영역이기 때문.
- 단, 분리된 패키지 간 참조는 양방향이고, 분리된 모듈 간 참조는 단방향만 가능하다는 중요한 차이점이 있다.
- O 독립된 모듈(library)로서 여기저기서 가져다 쓸 수 있는 확장성
- O 독립된 jar로 패키징하므로 별도의 main 가지고 단독 실행 가능
- X 단독 실행을 위해 패키징 하는 경우 jar 용량 감소
- 어차피 용량이 커지는 주 요인은 라이브러리인데, 서브 모듈도 단독 실행을 위해 라이브러리를 포함해서 패키징 한다면 용량이 드라마틱하게 절감되지는 않는다.
- ▲ 구동 속도 향상
- 기본적으로 jvm 환경에서는 초기 구동 속도가 빠른 편은 아니지만 서브 모듈이 framework 사용하지 않는다면 단독 실행 시 1s 이내 수준으로는 만들 수 있다.
- ▲ 모듈 별로 사용하는 설정, 버전, 의존성을 다르게 관리 할 수 있다. (이는 명확한 장단점이 있다.)
- 장점 ) 모듈이 나뉘어져있지 않고 패키지만 나뉘어진 구조에서 시스템이 비대해졌다면, 의존성 버전 하나 바꿀 때 테스트 해야 하는 범위가 너무 커질 수 있음.
- 단점 ) 하단 참고
패키지가 아니라 별도의 (멀티 모듈, 서브 모듈)로 구성했을 때의 단점은?
- 비즈니스 모듈을 여러개로 분리하는 경우, 담당 개발자(또는 스쿼드/팀)가 모듈 별로 명확하게 지정되어 있어야 한다. 그렇지 않으면 오히려 개발 피로도가 더 높을 수 있다.
- 모듈 간 설정, 버전, 의존성을 모두 다르게 구성하는 경우 한 사람이 모듈을 옮겨다니면서 개발하기에는 이질감이 크기 때문이다.
- 한 사람이 전 모듈을 다 건드리기 보다는, 해당 비즈니스 모듈 담당자에게 업무 요청하는 방식으로 업무가 진행되어야 자연스럽다.
- 하지만 급하게 개발이 진행되어야 하는 경우 모듈 담당자에게 업무 요청/협의/전달 프로세스 자체가 비용이 될 수 있다.
- 이럴 때 개발자는 ‘그럴바에 내가 하지.’ 라고 생각하게 되는데 각 모듈의 이질감이 크면 속도있게 개발하기 어렵다. (당연히 있어야 할 것이 이 모듈에는 없다?)
- 애매한 오너십 관점의 문제도 있다.
- 이런 문제들 때문에, 명확하게 담당자 지정하고 타 모듈에 대한 수정사항은 해당 모듈 담당자에게 요청하는 방식으로 진행하도록 가이드라인을 세워야 한다.
- 공통 개선 사항에 대한 전파가 느리다.
- 모듈이 나뉘어져 있는 만큼 테스트 범위가 작아 개선 사항 적용이 수월하다는 것은 분명 장점이나 이에 대한 전파가 느린 것 또한 사실이다.
참고
모듈(lib) vs 서버 나누는 기준
- Monolitic vs MSA 이므로, 모놀리틱을 MSA로 분리하면 장단점은? 참고
- 즉 보통 사람들이 원하는 ‘분리’는 모듈 분리만으로도 충분히 얻어낼 수 있고
- 서버를 분리해서 진짜 MSA로 가야 하는 경우는 fault에 대한 robustness가 필요한 경우에 한정한다.
- MSA는 tx 제어, 배포 관리 등 여러 단점을 수반하므로 fault에 대한 robustness를 확보하는 것이 이러한 단점을 충분히 상회 할 때만 고려할만 하다.
- 특히 MSA로 역할이 나뉘어져 있는 구조에서, 어떤 데이터를 다른 서버에 REST call 해서 받아오려면 해당 서버에서 반드시 persistence->business->presentation을 거쳐 REST API를 제공해주어야 한다는 점이 낭비가 심하다.
보통 모듈과 서버는 아래와 같은 형태로 나누게 되는데…
- 모듈 의존 방향은
Application -> Domain -> Support
- Support layer : persistence IO, external IO, 기타 util
- Domain layer : 각 기능 집합 단위로 business module을 구성
- domain model -> external dto로 변환은 여기서 수행하게 된다. (모듈 참조 방향 때문에 external module에서는 domain model을 몰라 domain layer에서 할 수 밖에 없다.)
- Application layer : 외부에서 요구하는 애플리케이션 인터페이스를 만족하기 위해, API, Admin, Batch를 각각 독립된 서버로 구성하고, 필요한 Business module을 가져다 presentation DTO로 변환하여 인터페이스.
이 방식의 단점은
- 가장 큰 단점은 비즈니스 모듈 간 상호 참조가 불가능하다는 것이다.
- 모듈은 단방향 참조만 가능한데, 비즈니스 모델들이 항상 단방향 참조만 하는 것도 아니기 때문에…
- 단방향 참조만 가지고 비즈니스 모듈 간 상호 참조를 표현하는 것은 불가능하다.
- 프로젝트를 이런 방식으로 구성하고, API module server를 하나만 세팅하는 경우, 비즈니스 모듈 간 상호 참조를 통해 만들어지는 로직이 결국 API module 에 들어가서 구조가 이상해진다.
- 그래서 이렇게 비즈니스 모듈이 쪼개져 있는 경우 결국 MSA 구조(e.g., API1, API2, …)로 서버도 분리해야 한다. => MSA까지 고려 했을 때 이 구조가 우리 상황에 적합한지?를 생각해보아야 한다.
반대로 얘기해서, 비즈니스 간 상호 참조 할 일이 거의 없다면 서버를 나누기 적합한 단위 일 수 있다. (일부러 MSA라는 용어는 사용하지 않았다.)
비즈니스 모듈 간 상호 참조 필요한 경우를 커버하려면
- 모듈은 단방향 참조만 가능하기 때문에, 코드 수준에서 비즈니스 모듈 간 상호 참조가 필요한 경우, 모듈을 분리하지 않고 패키지 단위로만 나누는 것이 낫다.
application -> domain -> support
방향으로 의존하는 것이 일반적인 layered arch의 흐름이지만, 위와 같이support -> domain
방향으로 의존하면 business module을 pure하게 유지하도록 강제 할 수 있다. ( DIP )- data mapper는 support layer에서.
- business module에서는 interface만 가지고 있고, 런타임에 support layer의 구현체를 DI
DIP 참조 예시
https://learn.microsoft.com/ko-kr/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice#the-infrastructure-layer
즉… 보통 코드베이스가 너무 비대해져서 분리를 고려 할 때 보통은 비즈니스 단위로 모듈을 나누게 되나,
비즈니스는 패키지 단위로 나누어도 충분하고, 비즈니스와 관련 없는 것들을 별도 모듈로 나누는 것이 더 효과적이었다.
연동처가 1개여도 반드시 domain interface를 만들고 추상화 할 필요가 있을까? 그럴 필요는 없어 보인다.
이 단계에서는 어떻게 추상화 해야 할지 모호 할 수 있다.
ClientService로 두고 연동처가 2개 이상이 되면 domain layer에서 추상화 인터페이스를 생성하는 식으로 작업하는 경우가 많다.
관심 분리
관심 분리의 ‘힘’은 패키지 < 모듈 < 서버 순으로 크다.
- 강력한 관심 분리가 필요한 경우, 패키지를 나누는 것 만으로는 부족한 경우가 있다.
- 접근 제어에 한계가 있어서 두 도메인 사이의 상호 참조가 자유롭게 가능하기 때문이다.
- 패키지 단위로 관심 분리 했을 때, 코드 레벨에서 상호 참조가 불가능하도록 컨트롤 하는 것은 어렵다.
- 모듈은 패키지 보다 더 명확하나, 단방향 참조만 가능하다는 제약이 있다.
- 서버를 나누면 강력한 관심 분리를 만족 할 수 있다. (MSA)
- 도메인 간의 관심사를 서버 단위로 명시적으로 분리하기 때문에, 상호 참조로 인한 스파게티 코드를 예방 할 수 있다.
도메인을 별도 모듈로 나누면 강력한 의존성 분리, 그로 인한 스파게티 코드 예방 차원의 장점이 있다고는 하나, 상기한 것 처럼 rest call로 인해 발생하는 단점이 분명히 존재한다. 단일 팀 규모의 시스템이라면 보통은 도메인 모듈은 1-2개로, 그 안에서 패키지로 나누어도 충분했다.
This post is licensed under CC BY 4.0 by the author.