11 KiB
11 KiB
title, source_type, status, related_branches, related_projects, tags, created, status_label, target_audience, inspiration_url, archive_url
| title | source_type | status | related_branches | related_projects | tags | created | status_label | target_audience | inspiration_url | archive_url | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| blog-topic / archunit-violations-as-data-pattern-2026-05-28 | blog-topic | raw |
|
|
|
2026-05-28 | ready-for-canonical | backend-engineer |
blog-topic: archunit-violations-as-data-pattern-2026-05-28
Layer:
raw/blog-topics/— 채용공고가 아닌 작업·학습·트러블슈팅에서 나온 블로그 글감 원석. canonical 정제 전 raw 후보이며,wiki/blog/직접 생성 근거가 아니다.
Parent / 부모
- raw/branch-notes/feature-architecture-enforcement-rules —
violations-as-datapattern 을 Claims to Verify 의planned에서actually-implemented로 승급시킨 round 2 작업. - raw/branch-notes/feature-application-port-usecase-contract — D14 (KEYED idempotency freeze) 의 ArchUnit custom condition 도 같은 fixture 로 보증.
트리거 / Trigger
- 트리거 유형:
branch-work - 트리거 날짜: 2026-05-28
- 트리거 연결 노트: raw/branch-notes/feature-architecture-enforcement-rules — Claims to Verify 의 마지막 행 ("ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명") 을 Spring Modulith
example/ninvalid패턴으로 구현한 작업.
글감 / Topic seed
- 한 문장 요지: ArchUnit rule 은 없는 위반 에 대해 vacuously pass 한다 — production 코드에 위반이 우연히 없을 때도, 분석 scope 자체가 비어있을 때도 동일하게 SUCCESS. 위반 fixture + negative test 로 rule 이 실제로 catch 하는지 를 commit 으로 박아두지 않으면 silent regression 이 누적된다.
- 떠오른 계기: round 1 작업에서 발견한 raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28 (ArchUnit scope 가 production classpath 만 보고 vacuous pass 한 사례) + Spring Modulith 의
example/ninvalid패턴 발견. - 예상 제목 후보:
- ArchUnit rule 을 믿을 수 있게 만드는 violations-as-data 패턴
- Spring Modulith 의
example/ninvalid를 ca-skeleton 에 차용한 6개 negative test - "rule 이 작동하는지" 를 commit 으로 박아두기 — fitness function 의 self-verification
핵심 주장 후보 / Claim candidates
- 사실 후보:
- ArchUnit 의 vacuous pass 함정은 두 갈래로 발생 — (a) production code 에 위반이 우연히 없음, (b)
@AnalyzeClasses의 import scope 가 비어 있음 (classpath 누락) — 근거: raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28 §직접 원인. - Spring Modulith 공식 incubator 가 자기 rule 들을 검증 하기 위해
example/ninvalidfixture package 와modules.detectViolations().getMessages()assertion 을 사용 — 근거:raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2(SPRING-MOD-AU-C2). - ca-tmpl 의 적용:
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/에 6 fixture (domain 1 + application 5) +ArchitectureViolationFixtureTest에 6 negative test — 근거:feature-architecture-enforcement-rules.md구현 결과 round 2 + Claims to Verify 마지막 행 →actually-implemented. - fixture 는
src/test/...위치이므로 main@AnalyzeClasses(importOptions = DoNotIncludeTests.class)에 자동으로 제외됨 — main suite 가 fixture 때문에 실패하지 않음 — 근거:feature-architecture-enforcement-rules.md진행 중 메모 round 2. - negative test 는
new ClassFileImporter().importPackages("...violations")로 fixture 만 로드한 뒤 rule 의EvaluationResult.hasViolation() == true를 단순 assert — 근거:src/app-bootstrap/src/test/.../ArchitectureViolationFixtureTest.java.
- ArchUnit 의 vacuous pass 함정은 두 갈래로 발생 — (a) production code 에 위반이 우연히 없음, (b)
- 경험 후보:
- KEYED idempotency rule (D14) 은 ArchUnit DSL 로 표현 불가능 → custom
ArchCondition<JavaClass>작성.JavaAnnotation.get("idempotency")가JavaEnumConstant를 반환하므로 bytecode 만으로 enum value 검사 가능 (reflection 없음) — 근거:CleanArchitectureTest#notDeclareKeyedIdempotency메서드 + branch-note D14. TransactionalAnnotatedFixture가@Transactional을 import 하려면app-bootstrap/build.gradle의testCompileOnly 'org.springframework:spring-tx'가 필요. production scope 에 영향 없음 (test 만) — 근거:feature-architecture-enforcement-rules.mdround 2 메모.- fixture 클래스를
package-private으로 유지해 외부 사용 불가 명시 + ArchUnit fixture import 만 동작 — 의도 노이즈 차단.
- KEYED idempotency rule (D14) 은 ArchUnit DSL 로 표현 불가능 → custom
- 의견 / 해석 후보:
- ArchUnit rule 은 코드 다. 코드는 테스트 없이 믿으면 안 된다 — fitness function 도 동일.
- vacuous pass 는 "rule 이 안 잡혔다" 가 아니라 "rule 이 무엇을 잡는지 아무도 검증 안 했다" 의 신호. 본 패턴은 후자를 commit 으로 박는 게 목적.
- 간단한 rule (
noClasses().that(pkg).should().dependOn(pkg2)) 은 vacuous pass 위험이 제일 큼 — 술어가 단순할수록 production 매칭이 우연히 0개가 되기 쉽다. 복잡한 custom condition (D14) 은 명시적으로 짠 거니까 더 안전 이라는 직관과 반대. - Spring Modulith 가 자기 자신 을 검증하는 데 쓰는 패턴이라는 점이 글의 강한 thesis — "rule 의 production-readiness 의 골든 스탠다드".
Outline seed
- 동기 — ArchUnit 의 vacuous pass 함정 두 갈래 → rule 이 잡는다고 믿는 것과 증명 하는 것의 차이.
- Spring Modulith 의 self-verification 패턴 (
example/ninvalid+detectViolations().getMessages()) → OSS 공식 incubator 가 자기 rule 을 같은 방식으로 검증한다는 신뢰 신호. - ca-tmpl 의 차용 —
violations/package +ArchitectureViolationFixtureTest→ 6 fixture × 6 negative test 의 1:1 매칭. - fixture 의 위치 결정 —
src/test/...안에 두면 mainDoNotIncludeTests가 자동 제외 → main suite 와 negative test 가 서로를 깨뜨리지 않는 격리. - custom ArchCondition (D14) —
JavaAnnotation.get(...)로 enum value 검사 → reflection 없이 bytecode 만으로 annotation parameter catch 가능. - fixture 의 deps —
testCompileOnly 'org.springframework:spring-tx'의 비대칭 의존 →@Transactional을 import 만 하고 production scope 에는 안 들어감. - 한계 — string-key bean lookup /
Class.forName(String)의 bypass 는 여전히 catch 불가 (D12) → fitness function 의 정직한 한계. - 정리 — rule 은 코드다. 코드는 negative test 없이 믿지 말자.
Canonical 전환 후보 / Canonical extraction candidates
wiki/projects/ca-tmpl/archunit-violations-as-data.md후보:- 실제 6 fixture + 6 negative test 의 코드 발췌.
testCompileOnly 'org.springframework:spring-tx'비대칭 의존 패턴.JavaAnnotation.get("idempotency")custom condition 코드.
wiki/concepts/archunit-violations-as-data.md후보:- "vacuous pass 함정 두 갈래" 의 project-agnostic 정리.
- Spring Modulith
example/ninvalid패턴의 일반화. - test-scope fixture + main DoNotIncludeTests 의 격리 패턴.
- 필요한 추가 검증:
- 본 패턴이 큰 codebase (rule 수십 개) 에 적용했을 때 negative test 가 production rule 의 작은 변경에 같이 깨지는지 (regression sensitivity 측정).
feature-archunit-negative-fixture-baseline같은 후속 branch 를 분리할 가치가 있는지.
Sources / 근거 후보
- raw/branch-notes/feature-architecture-enforcement-rules — Claims to Verify 마지막 행
actually-implemented승급 + 진행 중 메모 round 2 + Closure 갱신. - raw/branch-notes/feature-application-port-usecase-contract — D14 의 custom ArchCondition 으로 KEYED enum value catch.
- raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28 — vacuous pass 의 두 번째 갈래 (classpath scope) 의 실 사례.
- raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data —
example/ninvalid패턴 (SPRING-MOD-AU-C2) +annotatedWith(Generated.class)예시 (SPRING-MOD-AU-C1). - raw/official-docs/archunit-user-guide —
JavaAnnotation/JavaEnumConstant/EvaluationResult.hasViolation()의 공식 API 근거 (ARCHUNIT-UG-C5). - raw/interviews/archunit-static-analysis-limits — 같은 작업에서 파생된 면접 질문.
미해결 / Unknown
- 아직 확인해야 할 사실: negative test 가 rule wording 변경 에 얼마나 민감하게 깨지는지 — false-positive regression 비용 vs 진짜 regression catch 비용의 균형.
- 아직 확인해야 할 사실: fixture 클래스의 수 가 늘어날 때 (예: 50 rule × 50 fixture) 관리 비용. Spring Modulith 의 실 fixture 디렉터리 크기와 비교 필요.
- 과장하면 안 되는 부분: 본 패턴은 ArchUnit static analysis 의 한계 (D12 string bypass) 를 보완하지 않는다. negative test 도 정적이라 reflection bypass 는 잡지 못함.
- 과장하면 안 되는 부분: ca-tmpl 의 6 fixture / 6 test 는 proof-of-concept 규모. 실 사업 도메인의 30+ rule 적용 시 관리 비용 측정 미수행.
- 블로그로 쓰기 전에 필요한 canonical 정제:
wiki/projects/ca-tmpl/archunit-violations-as-data.md+wiki/concepts/archunit-violations-as-data.md정제. 1~2개 추가 branch 적용 사례 누적.
Decision / 처리 결정
- 액션:
promote-to-canonical - 이유:
wiki/projects/ca-tmpl/clean-architecture-package-layout.md와wiki/projects/ca-tmpl/skeleton-governance-registry-verification-test-scorecard.md에 violations-as-data / negative fixture 글감으로 반영한다. - 다음 단계: blogify 전 적용 branch 수와 fixture 관리 비용을 확인한다.
Related / 관련
- 관련 branch: raw/branch-notes/feature-architecture-enforcement-rules (본 패턴의 직접 적용), raw/branch-notes/feature-application-port-usecase-contract (D14 custom condition 의 negative test).
- 관련 errors: raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28 (vacuous pass 의 다른 갈래).
- 관련 interview prep: raw/interviews/archunit-static-analysis-limits (static analysis 한계 + violations-as-data 보완), raw/interviews/clean-architecture-boundary-enforcement (선행 — boundary 자동 검증의 자매 토픽).
- 관련 blog topics: raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28 (boundary 자동 검증의 자매 글감).
- derived blog: 생성 전. 생성 시
wiki/blog/archunit-violations-as-data-pattern-YYYY-MM-DD.md후보.