Post

패키지, 모듈, 서버 나누는 기준은?

패키지 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

즉… 보통 코드베이스가 너무 비대해져서 분리를 고려 할 때 보통은 비즈니스 단위로 모듈을 나누게 되나,
비즈니스는 패키지 단위로 나누어도 충분하고, 비즈니스와 관련 없는 것들을 별도 모듈로 나누는 것이 더 효과적이었다.

This post is licensed under CC BY 4.0 by the author.