Code Organization Guidelines for Large Code Bases - 유겐 휄러
패키지 interdependencies
모듈 나누기와 레이어링
커다란 코드 기반 진화시키기
케이스: 스프링 진화
아키텍처 분석 도구
왜 코드 조직화를 걱정하는가?
- 전체 코드 이해를 돕고, 보다 편하게 네비게이션 하기 위해서.
- 자바에는 패키지와 서브 패키지 개념을 제공하긴 하지만, 어떻게 적용해야 할지에 대한 권고 같은건 없다.
신경써야 할 것들
- 코드는 원래 구조에서 계속 진화할 필요가 있다.
- 리팩터링과 애자일 개발은 좋다. 근데 그렇게 개발하면서도 이전에 배포한 코드와의 호환성은 어떻게 유지할 것인가..
- 원래 설계에서 의도하지 않았던 의존성들이 모듈을 나누다 보면 생길 수 있다.
- (점점 커지나까) API를 유지하면서 모듈을 좀 더 세밀한 모듈로 조갤 수 있을런지..
패키지 상호의존성
- 패키지 구조를 설계하는 건 놀랍도록 중요하다.
왜 단방향 의존성이 중요한가?
- 즉, 왜 순환 참조(CD)가 안 좋을까?
- 코드를 악화 시킨다,.
- 패키지 재사용에 제한이 생긴다.
- B를 사용하려면 A를 컴파일 해야 하는데 A는 다시 B를 컴파일 해야 하는 상황 발생한다.
그러니까
- CD를 없애라.
- 패키지들을 개념적인 모듈로 묶어라.
- 모듈을 만들어 낼 땐 개념적인 경계와 더불어 배포도 고려해야 한다.
모듈 decomposition과 레이어링
- 모듈의 특성(다른 모듈과 낮은 종속성, 모듈내에선 높은 결집도)
- 모듈은 소스 관리와 배포 단위 만듬이나 개념적인 단위다.
- cognitive 오버로드를 피하라.
- 개별적으로 사용 할 수 있어야 한다.(전체 시스템의 일부 기능)
- 모듈 간에 CD를 가지고 있으면 안 된다.
- 모듈 간에 의존성들은 그들이 가지고 있는 패키지에 의해 만들어진다.
- 레이어링은 패키지 구조에 있어서 중요한 논리 뷰다. 상위 계층에서 하위 계층을 참조하고 그 반대로는 안 되도록..
- 모듈 레이러링 보다는 개발 consideration에 의해 도출 된다.
그러니까
- 코드 베이스 내에서 자연스러운 개념적인 경계를 만들어라.
- 자연스러운 패키지 네이밍
- 편한 네비게이션
- 가장 힘든 도전은 코드를 진화시키는 거다.
- code deteriorate를 놓치지 않고..
- 아키텍처 질을 떨어뜨리지 않고..
- 하위 호환성과 아키텍처 품질 사이의 트레이드오프를 생각해야 한다.
- 완전 100% 하위 호환성은 보장할 수가 없게 된다.
- 물론 가끔 아키텍쳐 품질을 양보하지 않아도 되는 방법이 항상 있을 수 있음을 생각해야 한다.
- 물론 몇 가지는 쉽게 바꾸기가 힘들 것이다.
- public API에 있는 패키지 이름
- 그렇다고 정리하는걸 주저하면 안된다. 아키텍처 품질을 위해 deprecation 해버리자.
케이스 스터디: 스프링
- 스프링 코드 복잡한 SPI, 다양한 곳에서 사용하는 많은 API, 다양한 요구사항 속에서 3.5년을 어떻게 버텨왔을까?
- 매우 엄격한 아키텍쳐 관리
- loosely counpled package with welldefined interdependencies
- 패키지 단에서 CD가 없다. 만약에 CD가 생기면 다시 살펴보고 심각하게 고민하다.
- 스프링이 가지고 있던 CD (수정 후 dependency)
- core <-> util (core <- util)
- beans <-> aop (beans <- aop)
- beans <-> context (beans <- context)
- transaction <-> dao (transaction -> dao)
- transaction <-> jdbc (transaction -> jdbc)
(소스 코드로 좀 보여주지 말로만 설명하네. 힘들게 시리 ㅠ.ㅠ)
CD 확인하는 도구
- JDepend
- SonarJ
- 공식 배포 하기 전에 JDepend를 한 번 돌려보는 건 어떨까!!