140 lines
5.2 KiB
Markdown
140 lines
5.2 KiB
Markdown
# common module 허용 기준
|
|
|
|
## 목적
|
|
|
|
`common` 모듈은 공유 편의 때문에 만드는 기본 모듈이 아니다.
|
|
기본값은 **만들지 않음** 이다.
|
|
|
|
이 문서의 목적은:
|
|
- “일단 common으로 보내자”를 막고
|
|
- 모듈 경계를 흐리는 공유를 줄이고
|
|
- 정말 필요한 공용 코드만 예외적으로 허용하는 것이다
|
|
|
|
## 공식/원전 기준 요약
|
|
|
|
- 모듈은 외부에 노출하는 API와 내부 구현을 구분해야 한다.
|
|
- 다른 모듈은 공개된 API에만 의존해야 하며, 내부 구현 의존은 막아야 한다.
|
|
- 헥사고날 아키텍처의 핵심도 경계와 역할 분리다.
|
|
- 겉보기 중복만 보고 조기 추상화를 만들면 이후 변경 비용이 커질 수 있다.
|
|
|
|
## 기본 규칙
|
|
|
|
### 1. `common`은 기본 금지
|
|
새 코드를 추가할 때 기본 선택지는 아래 순서다.
|
|
|
|
1. owning layer/module 내부에 둔다
|
|
2. 경계가 필요하면 해당 module의 API로 노출한다
|
|
3. 그래도 어느 곳에도 자연스럽게 속하지 않을 때만 `common`을 검토한다
|
|
|
|
즉 “어디 둘지 애매하면 common”을 금지한다.
|
|
|
|
### 2. `common`은 아래 조건을 모두 만족할 때만 허용
|
|
다음이 모두 참일 때만 `common` 도입을 검토한다.
|
|
|
|
- 3개 이상 모듈에서 실제로 재사용된다
|
|
- 변화 이유가 같다
|
|
- 특정 모듈이 소유한다고 보기 어렵다
|
|
- `common`으로 옮겨도 의존 방향이 더 나빠지지 않는다
|
|
- 모듈 API를 좁게 노출하는 방식으로 해결하기 어렵다
|
|
|
|
하나라도 애매하면 기존 owning module에 둔다.
|
|
|
|
### 3. “중복 존재”만으로는 common 근거가 아니다
|
|
코드가 비슷해 보여도 다음이면 `common`으로 보내지 않는다.
|
|
|
|
- 서로 다른 비즈니스 문맥에서 독립적으로 변할 가능성이 큼
|
|
- 지금은 비슷하지만 미래 요구가 갈라질 가능성이 큼
|
|
- common으로 모으면 이름과 책임이 모호해짐
|
|
|
|
즉 진짜 공통 원인(common cause)일 때만 허용한다.
|
|
|
|
### 4. 레이어 경계를 깨는 공유 금지
|
|
`common`이 아래를 섞는 것을 금지한다.
|
|
|
|
- presentation + domain
|
|
- domain + infrastructure
|
|
- application + web transport
|
|
- persistence 모델 + API 모델
|
|
- 외부 API payload + 내부 domain meaning
|
|
|
|
공유보다 경계 보존이 우선이다.
|
|
|
|
### 5. `common`에는 business policy를 두지 않는다
|
|
다음은 `common`에 두지 않는다.
|
|
|
|
- 도메인 규칙
|
|
- 상태 전이 규칙
|
|
- 권한 판정
|
|
- 에러 코드 정책
|
|
- 외부 연동별 특화 규칙
|
|
|
|
이런 것은 반드시 owning module 또는 boundary contract가 소유한다.
|
|
|
|
### 6. `common`에 둘 수 있는 것
|
|
예외적으로 허용 가능한 후보:
|
|
|
|
- 경량 value type
|
|
- 순수하고 작은 utility
|
|
- 여러 모듈이 같은 이유로 쓰는 매우 안정적인 helper
|
|
- 모듈 경계를 깨지 않는 공통 annotation / marker / tiny abstraction
|
|
- 명확한 소유자가 없는 순수한 language-level helper
|
|
|
|
단, 이것도 실제 재사용과 변화 이유가 검증되어야 한다.
|
|
|
|
### 7. `common`의 utility는 특히 좁게 제한
|
|
utility는 아래 조건을 만족할 때만 허용한다.
|
|
|
|
- side effect 없음
|
|
- framework/business/persistence 의존 없음
|
|
- 이름만 보고 역할이 분명함
|
|
- 단순 문자열/시간/컬렉션 helper라도 owning type 안에 둘 수 없는 이유가 있음
|
|
|
|
`StringUtils`, `DateUtils`, `CommonUtils`, `AppUtils` 같은 잡동사니 묶음은 금지한다.
|
|
|
|
### 8. 모듈 API 노출이 common보다 우선
|
|
Spring Modulith의 방향처럼, 공용화가 필요해 보일 때 먼저 검토할 것은:
|
|
|
|
- 해당 모듈의 공개 API로 노출할 수 있는가
|
|
- named interface처럼 노출 범위를 좁게 지정할 수 있는가
|
|
- explicit dependency로 필요한 부분만 허용할 수 있는가
|
|
|
|
즉 “common으로 이동”보다 “모듈 API 설계 개선”을 먼저 본다.
|
|
|
|
### 9. 공용 타입은 더 보수적으로 관리
|
|
`common`으로 이동한 타입은 사실상 여러 모듈이 기대는 기반이 된다.
|
|
|
|
기본:
|
|
- 변경에 더 보수적이어야 한다
|
|
- naming을 더 명확히 해야 한다
|
|
- Javadoc/문서가 더 중요하다
|
|
- examples/tests가 함께 있어야 한다
|
|
|
|
### 10. `common`은 dump zone이 아니다
|
|
다음 징후가 보이면 잘못된 `common`이다.
|
|
|
|
- 이름이 `Common*`, `Util*`, `Base*`, `Helper*` 위주
|
|
- business/domain/web/persistence 코드가 섞여 있음
|
|
- 모듈 간 순환 의존을 가리기 위해 common을 사용
|
|
- “일단 여기 두자”가 반복됨
|
|
|
|
이 경우 common을 늘리는 대신 다시 소유 모듈로 분해한다.
|
|
|
|
### 11. common 도입은 문서화한다
|
|
새로운 `common` 타입/패키지를 추가할 때는 최소한 아래를 설명한다.
|
|
|
|
- 왜 owning module에 둘 수 없는가
|
|
- 어떤 모듈들이 실제로 재사용하는가
|
|
- 같은 이유로 어떻게 함께 바뀌는가
|
|
- 어떤 경계를 깨지 않는가
|
|
|
|
설명할 수 없으면 common으로 보내지 않는다.
|
|
|
|
## 프로젝트 기준 요약
|
|
|
|
- `common` 기본 금지
|
|
- 재사용만으로는 부족하고 “같은 이유로 함께 변함”이 필요
|
|
- 모듈 API 설계 개선이 common보다 우선
|
|
- 경계/레이어를 섞는 공용화 금지
|
|
- business policy는 common 금지
|
|
- utility/common dump zone 금지
|
|
- 예외적 허용 시에도 좁고 순수하고 안정적인 타입만 |