ADR-005 FR-7 정적 검증 = ArchUnit fitness function

  • 상태: 채택
  • 날짜: 2026-07-03
  • 근거: PRD FR-7, FR-8, NFR(스캔 2분 이내) / TDD “FR-7 정적 검증 방법”

맥락

도메인 간 참조 규칙(domain 레이어 교차 import 금지, common 예외) 위반을 정적으로 탐지할 방법이 없다. 현재 위반 0건(베이스라인)이며 이 베이스라인 유지가 목표다. 스캔은 domain 레이어 Entity/VO의 타 도메인 import로 한정(application 조합은 정상).

결정

ArchUnit 규칙 테스트(Kotest)로 실행 가능하게 구현한다. 규칙 4종:

규칙내용
R1domain.Xdomain.Y(Y≠X, Y≠common)를 import하지 않는다 (FR-7 핵심, 베이스라인 0건)
R2domain.**infrastructure·application·presentation를 import하지 않는다 (레이어 방향)
R3지원/서브시스템(operator/mcp/weather)은 코어를 동기 의존하지 않는다
R4common은 어떤 도메인도 import하지 않는다 (공유 커널 순수성)

R3 화이트리스트 (명시 예외 — 규칙에서 제외):

예외방향성격근거
dashboardapplication.dashboard → 코어(booking/facility/goods/ticketing/user)읽기 조합Conformist read model (ADR-002 규칙 4)
partner (② B2B)application.partner → domain.useradmin 프로비저닝 쓰기 오케스트레이션CreatePartnerUseCase가 연동 전용 User 계정 생성 — ADR-002 rule #2 범위. 이 예외가 없으면 ② 구현 시 R3가 false RED

partner 예외는 사전 등록한다 — 현 시점 application.partner 패키지는 미존재(② 과제에서 신설)이나, ArchUnit 규칙(resideInAnyPackage(...).ignoreDependency)에 partner→user 예외를 미리 배선해 ② 착수 시 규칙 수정 없이 통과하게 한다. application.partner → 코어(user 제외) 접근은 여전히 R3 위반.

  • 구현: ClassFileImportercom.sportsapp 임포트 → 스펙 간 캐싱 공유 → Kotest FunSpec에서 assert.
  • 별도 Gradle 태스크 archTest로 분리. 초기 관찰 모드(check 미포함) → 베이스라인 0건 확인 후 게이트 승격.

근거

  • 벤치마크 정합: Shopify Packwerk·Spring Modulith fitness function이 “물리 분리 없이 경계만 정적 강제”하는 접근. 본 과제 Non-Goals와 일치.
  • 회귀 방지: 문서·리뷰만으로는 0건 회귀를 못 막음. 테스트가 위반을 빌드에서 잡는다.
  • NFR 충족: ClassFileImporter 결과 캐싱으로 전체 스캔 2분 이내.
  • FR-8 갱신 절차 연동: 패키지 prefix 스캔이라 신규 도메인 자동 포함. 예외(dashboard 읽기·partner→user 쓰기 화이트리스트)만 수동 갱신.

대안 (미채택)

  • grep/셸 스크립트: import 문자열 매칭은 오탐·미탐(별칭·와일드카드 import) 위험. 유지보수 취약.
  • Konsist: ArchUnit과 등가지만 팀 표준(테스트=Kotest)과 통합·성숙도에서 ArchUnit 우선. 필요 시 대체 가능.
  • CI 게이트 즉시 강제: 예상 못한 위반이 CI를 막을 위험 → 관찰 모드 1단계 후 승격(Release Scenario).

영향

  • Open Question(CI 게이트 vs 체크리스트)을 “게이트 강제(관찰 후 승격)“로 확정.
  • BE-01(공통 fixture)·BE-02~05(규칙별 스펙)·BE-07(Gradle 태스크 와이어업) 티켓으로 구현.