50 KiB
title, source_type, status, branch, parent_branch, related_projects, tags, created, last_reviewed, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
| title | source_type | status | branch | parent_branch | related_projects | tags | created | last_reviewed | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | contract_packet_sha256 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-architecture-enforcement-rules | branch-note | verified | feature-architecture-enforcement-rules |
|
|
2026-05-21 | 2026-06-04 | master | review | BR-CA-SKELETON-OPERATIONAL-CONTRACT-018 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-018 |
|
1 | a1912f62e082b02487a0a332eef101b12c056ac485ec9d1bbb42e4e1590ecb05 |
Ground-truth 대조 (2026-06-04, ca-tmpl
@db61075): 본 branch가 정의한 enforcement rule이 실제 레포에 반영됨을 확인.app-bootstrap/.../architecture/CleanArchitectureTest.java에domain_is_pure(Lombok ban 포함, D3),application_does_not_depend_on_adapters_or_transport,application_does_not_use_spring_transactional_annotation,application_does_not_depend_on_application_context(D11, banned-class), adapter-adapter 격리 3종,web_dtos_stay_in_web_adapter,shared_contract_contains_only_operational_contract_packages,production_code_does_not_depend_on_sample_portfolio존재.src/build.gradle:53verifyCleanArchitectureDependencies+allowedProjectDependenciesmatrix(9 module) 존재.ArchitectureViolationFixtureTest+architecture/violations/에 negative fixture 존재(SpringDependentDomainFixture·ApplicationContextDependentFixture·TransactionalAnnotatedFixture포함). D11 string-key bypass(D12)는 rule 주석에 한계로 명시됨 —getBean(Class)까지만 catch.wiki/projects/ca-tmpl/clean-architecture-package-layout에 enforcement dimension 추출 완료. ⚠️ ground-truthCleanArchitectureTest는 이후 다른 branch slice rule도 다수 포함(현재 30+ rule)하므로, 추출은 본 branch 소유 항목만 한정함.
branch: feature-architecture-enforcement-rules
Layer:
raw/branch-notes/— Clean Architecture 경계와 skeleton 계약을 architecture test로 강제하는 기준을 정의합니다.
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 §20 Skeleton Blueprint Contract 와 §25 Critical Defaults 의 architecture enforcement 영역을 정제한다.
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: forbidden module/import fixture가 ArchUnit gate에서 실패한다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1 |
Gradle multi-module에서 domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1 |
architecture test는 archunit-junit5 1.3.0을 사용한다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
목표
문서 기준만으로는 시간이 지나면 module dependency, application boundary, domain purity, adapter boundary, sample isolation 이 무너집니다. feature-skeleton-package-blueprint-contract가 Gradle multi-module 구조를 기본값으로 고정했으므로, 본 branch는 그 구조가 실제 코드에서 깨지면 Gradle/ArchUnit test가 실패하도록 강제 기준을 정의합니다.
- 이슈: (없음 — local branch, 이슈 트래커 미사용)
- PR: (미생성 — local verification only, not merged)
범위
포함 범위
- Gradle multi-module dependency rule.
domain-coreframework import 금지.application-core->adapter-*/app-bootstrap의존 금지.- adapter module 간 직접 의존 금지.
shared-contractbusiness/domain concept 오염 방지.sample-portfolioproduction 역수입 금지.- mapper boundary rule.
- transaction annotation forbidden import rule.
- ArchUnit rule 위치와 실행 기준.
제외 범위
- formatter / style lint 규칙.
- business package naming 강제.
- Spring Modulith verifier 도입.
- SonarQube custom rule 구현.
- CI workflow job 분리 구현. CI 실행 시점은
feature-ci-quality-gates-contract에서 최종화.
근거 (필수, 최소 1개+)
본 branch의 결정 근거. multi-module 기본값은 company-case-study 근거가 중심이므로, 공식 best practice가 아니라 사례 기반 프로젝트 결정으로 취급한다.
| Source | 정당화하는 결정 |
|---|---|
| raw/official-docs/archunit-user-guide | ArchUnit rule / @ArchTest / dependency check 구현 근거 |
| raw/official-docs/governance-archunit-official | architecture rule을 test로 강제하는 기본 근거 |
| raw/official-docs/archunit-conditional-on-property-3-layer-pattern | predicate/condition 기반 ArchUnit fitness function 근거 |
| raw/official-docs/arch-clean-architecture-uncle-bob | Dependency Rule 및 framework-independent domain 사고 근거 (engineering-blog, official standard 아님) |
| raw/official-docs/arch-hexagonal-cockburn | ports/adapters inside/outside asymmetry 근거 (engineering-blog, official standard 아님) |
| raw/company-tech-blogs/woowahan-hexagonal-multimodule | Domain / Application / Framework / Bootstrap multi-module hexagonal 사례 |
| raw/company-tech-blogs/modulith-kakaobank-techblog-2025 | Gradle multi-module + Hexagonal 위에서 application/adapter 물리 분리와 Port 통신 사례 |
| raw/official-docs/modulith-spring-official-doc | Spring Modulith verifier 대안. Phase C2 기본값은 아니며 후속 검토 후보 |
| raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011 | feature/use-case 중심 구조가 framework 중심 구조보다 의도를 드러낸다는 보조 근거 |
| raw/company-tech-blogs/feature-first-sahibinden-package-by-layer-vs-feature | layer-first 대비 feature 응집도 사례. module 내부 package 책임 참고용 |
| raw/official-docs/mapstruct-generated-annotation-official | D9: MapStruct generated mapper에 @Generated annotation이 붙는다는 공식 근거 (MS-ANNOT-C1, MS-ANNOT-C2) |
| raw/official-docs/lombok-builder-data-features-official | D3: domain-core Lombok 금지 결정 — @Builder 가 inner static class·setter 등 7가지를 생성하고 @Data 가 setter 를 포함한 full boilerplate 를 생성함을 공식 문서로 뒷받침 (LMB-C1~LMB-C5) |
| raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data | D9 corroboration: Spring Modulith 자체가 annotatedWith(Generated.class) ArchUnit predicate 를 production 코드에 사용 (SPRING-MOD-AU-C1). S1 negative test fixture pattern: detectViolations() returns Violations as data + example/ninvalid fixture package (SPRING-MOD-AU-C2). D8 CONTRARY: @ApplicationModuleListener meta-annotation (SPRING-MOD-TX-C1) |
| raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional | D3 CONTRARY: Buckpal domain purity ArchUnit rule 이 lombok.. 명시 allowlist (BUCKPAL-LOMBOK-C1, C2). D8 CONTRARY: @Component @Transactional 직접 부착 (BUCKPAL-TX-C1, C2). Hexagonal 공식 reference 가 ca-tmpl 결정과 정반대 방향임을 기록 |
TODO
각 항목 옆에 증거 등급 표기: actually-implemented | locally-verified | prod-verified | documented-only | planned | needs-confirmation
- Gradle project dependency rule을
domain-core,application-core,adapter-*,app-bootstrap,sample-portfolio기준으로 정리 — 등급:locally-verified - ArchUnit
domain-coreforbidden import rule 정의 — 등급:actually-implemented - ArchUnit
application-coreadapter dependency forbidden rule 정의 — 등급:actually-implemented - adapter module 간 직접 의존 금지 rule 정의 — 등급:
actually-implemented shared-contract허용 package scope rule 정의 — 등급:locally-verifiedsample-portfolioproduction 역수입 금지 rule 정의 — 등급:locally-verified- mapper boundary / direct domain response 금지 rule 정의 — 등급:
locally-verified - transaction annotation forbidden import rule 정의 — 등급:
locally-verified
진행 중 메모
- architecture test 기본 도구는 ArchUnit으로 둔다.
- Gradle dependency graph 검증은
feature-skeleton-package-blueprint-contract의verifyCleanArchitectureDependencies와 같은 방향으로 둔다. - Spring Modulith verifier는 기본값이 아니라 후속 검토 후보로 둔다. 현재 기본 강제선은 Gradle dependency rule + ArchUnit rule이다.
- 2026-05-28 구현 반영: ca-tmpl
src/build.gradle의verifyCleanArchitectureDependencies를 보강하고,src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java에 transaction annotation, controller direct domain response, mapper boundary, shared-contract package allowlist 규칙을 추가했다. - 2026-05-28 red/green 검증: 임시 위반 코드로
application @Transactional, controller domain return, mapper -> application dependency,shared.worklogpackage 위반이CleanArchitectureTest에서 실패함을 확인한 뒤 임시 파일을 제거했다. 임시app-bootstrap -> sample-portfolioproject dependency도verifyCleanArchitectureDependencies에서 실패함을 확인한 뒤 제거했다. - 2026-05-28 전체 검증:
cd src && ./gradlew :app-bootstrap:test verifyCleanArchitectureDependencies,cd src && ./gradlew test모두 성공. Gradle 10 호환성 deprecation warning은 기존 빌드 경고로 남아 있다. - 2026-05-28 워크플로우 반영: ca-tmpl repo 내부
AGENTS.md,CLAUDE.md,.agents/plugins/ca-superpowers/,.claude/,.codex/지침에 구현 완료 후 LLM Wiki branch-note 갱신과raw/errors,raw/interviews,raw/blog-topics파생 문서 캡처 규칙을 추가했다. 등급:documented-only(repo-local workflow docs; 자동 강제 장치 아님). - 2026-05-28 round 2 구현 반영 (D3 Lombok + D11 ApplicationContext + violations-as-data fixture):
domain_is_purerule 의 forbidden packages 에lombok..추가 (D3) → Lombok 사용 시 ArchUnit 실패.application_does_not_depend_on_application_contextArchUnit rule 신규 추가 (D11) →getBean(Class)class-literal 호출까지 catch. String-key bypass (getBean(String),Class.forName(String)) 는 D12 의 code review checklist 한계로 명시.src/app-bootstrap/src/test/java/.../violations/패키지에 의도된 위반 fixture 6종 +ArchitectureViolationFixtureTest6 negative test 추가 → 각 rule 이 실제로 위반을 catch 하는지 commit 된 negative test 로 보증 (Spring Modulithexample/ninvalid패턴).testCompileOnly 'org.springframework:spring-tx'를app-bootstrap/build.gradle에 추가 (TransactionalAnnotatedFixture가@Transactional을 import 하기 위해 — production 영향 없음).- 전체 검증:
cd src && ./gradlew checkPASS,CleanArchitectureTest14 tests +ArchitectureViolationFixtureTest6 tests.
- 2026-06-30 develop 머지 충돌 및 규칙 수정:
master에 직접 커밋된mappers_do_not_depend_on_web_or_application_boundaries규칙의 패키지 필터(..mapper..)가develop에 추가된 웹 매퍼(WorkLogWebMapper,FeatureAggregateResponseMapper등)를 침범하여 테스트가 실패하는 현상이 발생함.- 웹 매퍼는 프레임워크/웹 DTO와 애플리케이션 커맨드를 매핑해야 하므로 웹/애플리케이션 의존성이 허용되어야 함.
- 따라서 해당 규칙의 타겟 패키지를
..adapter.persistence..mapper..(영속성 매퍼)로 제한함. - 또한 Clean Architecture 상 영속성 어댑터는 애플리케이션 코어 레이어를 의존할 수 있으므로(예: 멱등성 매퍼가 애플리케이션 레코드 타입을 참조하는 경우), 영속성 매퍼가 금지해야 할 대상에서
..application..을 제외하고..adapter.web..과..bootstrap..만 금지하도록 규칙을 수정함. - 수정 후
CleanArchitectureTest54개 테스트 통과 완료.
결정 사항
-
2026-05-21: CA 경계는 문서가 아니라 테스트로 강제되어야 함. / 이유: 문서만으로는 시간이 지나며 boundary drift가 발생함. / 검토한 대안: (a) 문서 + PR 리뷰만으로 강제 — boundary drift 누적, (b) SonarQube custom rule — out of scope §범위, (c) Spring Modulith verifier — out of scope §범위. / 근거: raw/official-docs/governance-archunit-official.
-
2026-05-27: package rule은 기존
features.{featureName}.{presentation,application,domain,infrastructure}기준에서 Gradle multi-module 기준으로 수정. / 이유: Phase C2 기본 구조가domain-core/application-core/adapter-*/shared-contract/app-bootstrap/sample-portfolio로 바뀜. / 검토한 대안: 기존features.{featureName}.{layer}package-convention 유지 (single-module 가정) — 채택 안 함. company-case-study (woowahan, kakaobank) 가 모두 module boundary 분리를 택했음. / 근거: raw/branch-notes/feature-skeleton-package-blueprint-contract. -
2026-05-27:
domain-core는 Spring/JPA/HTTP/adapter type import 금지. / 이유: domain model을 framework-neutral POJO로 유지하기 위함. / 검토한 대안: (a) framework 허용 + DI 패턴으로만 격리 — domain lifecycle 이 framework 에 결합, (b) package-private convention 만 사용 — multi-module 환경에서는 module boundary 가 더 강한 격리 제공. / 근거: raw/company-tech-blogs/woowahan-hexagonal-multimodule, raw/official-docs/arch-clean-architecture-uncle-bob. -
2026-05-27:
application-core는adapter-*와app-bootstrap에 의존하면 안 됨. / 이유: application core가 outbound implementation을 직접 알면 port boundary가 무너짐. / 검토한 대안: Spring Modulith@ApplicationModulenamed interface 로 module 내부 의존 허용 + 외부 노출만 차단 — out of scope §범위. / 근거: raw/company-tech-blogs/modulith-kakaobank-techblog-2025, raw/official-docs/arch-hexagonal-cockburn. -
2026-05-27: adapter module끼리 직접 의존하지 않음. / 이유: adapter 간 공유가 필요하면 application port 또는 shared operational contract로 승격해야 함. / 검토한 대안: (a)
adapter-commonshared module 생성 — common dumping ground 위험 (D6 와 동일 risk), (b) Spring Modulith named interface — out of scope §범위. / 근거: raw/branch-notes/feature-skeleton-package-blueprint-contract. -
2026-05-27:
shared-contract는 response/error/header/logging/tracing/metrics/registry/annotation 같은 skeleton-wide operational contract만 허용. / 이유: business common dumping ground를 막기 위함. / 검토한 대안:shared-business별도 module 신설하여 business common 허용 — 채택 안 함. 사례(woowahan, kakaobank) 모두 shared = operational contract 만 정의. / 근거: raw/branch-notes/feature-skeleton-package-blueprint-contract. -
2026-05-27:
sample-portfolio은 production module이 import하거나 dependency로 선언하면 실패. / 이유: sample은 production feature가 아니라 contract fixture임. / 검토한 대안: sample 을 production module 과 통합 (sample 분리 안 함) — 채택 안 함. sample 코드가 production 코드 경로에 섞이면 제거 시점 식별 불가. / 근거: raw/branch-notes/feature-skeleton-package-blueprint-contract. -
2026-05-22: application layer의 Spring
@Transactional직접 import는 금지하고 transaction abstraction 사용 여부를 검증. / 이유: transaction boundary를 application use case 책임으로 두되 Spring annotation 의존을 숨기기 위함. / 검토한 대안: (a)@Transactional직접 허용 — Spring 공식 지원, ca-tmpl 은 격리를 위한 소수파 선택(D8 Open Risk), (b) AOP custom annotation 으로 동일 효과 — 추가 추상화 비용, (c)TransactionTemplateprogrammatic — boilerplate 증가. / 근거: raw/branch-notes/feature-application-port-usecase-contract. -
2026-05-22: MapStruct 사용 시 generated mapper package/path exemption을 명시해야 하며 exemption 없는 generated code 우회는 실패. / 이유: generated code가 architecture rule을 무력화하지 않게 하기 위함. / 검토한 대안: MapStruct generated code 에도 rule 적용 (exemption 없음) — build path 분리 검사 필요, 실현 가능성 미검증. / 근거: raw/official-docs/mapstruct-generated-annotation-official MS-ANNOT-C1 (MapStruct가
@Generatedannotation을 generated mapper에 부착함을 공식 확인). ArchUnit predicate 구현 방법은 raw/official-docs/archunit-user-guide 보강 필요. ca-tmpl 실제 generated path 확인은needs-confirmation. -
2026-05-22: ArchUnit fail mode = strict-break for new violations. legacy 코드 적용 시 FreezingArchRule baseline 1회 capture 허용, baseline 외 새 violation은 PR block. / 이유: strict-break 가 boundary drift 누적 차단의 핵심. legacy baseline 은 도입 비용을 줄이는 한시적 타협. / 검토한 대안: (a) warning-only mode (CI 비차단) — drift 누적 위험, (b) report-only baseline (legacy 전체 면제) — 신규 위반 강제 불가. / 근거: raw/official-docs/archunit-user-guide.
-
2026-05-28: non-trivial 구현 종료 조건에 LLM Wiki capture를 포함한다. / 이유: 코드 구현 후 branch-note와 파생 자료 작성을 매번 대화로 요청해야 하는 반복 비용을 줄이고, 구현 사실·검증·트러블슈팅·면접/블로그 후보를 누락 없이 raw 계층에 남기기 위함. / 검토한 대안: (a) 사용자가 매번 수동 요청 — 누락 위험, (b) LLM Wiki vault 규칙만 유지 — ca-tmpl 작업자가 종료 조건으로 인식하지 못함, (c) ca-tmpl repo-local rule로 연결 — 채택. / 근거: 사용자 워크플로우 요구 + raw/branch-notes/feature-architecture-enforcement-rules 본 작업 기록.
결정-근거 매핑
각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시한다.
Decision ID는 이 branch-note 안에서 안정적으로 유지한다.
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | CA 경계는 architecture test로 강제 | raw/official-docs/governance-archunit-official.md#AU-OFF-C1, raw/official-docs/governance-archunit-official.md#AU-OFF-C2, raw/official-docs/archunit-conditional-on-property-3-layer-pattern.md#AUCP-C5 |
official-vendor-doc + engineering-blog |
ArchUnit은 정적 검사만 가능. runtime lookup / reflection 우회는 별도 보완 필요 |
| D2 | package rule은 Gradle multi-module boundary 기준 | raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1, raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4, raw/branch-notes/feature-skeleton-package-blueprint-contract.md |
company-case-study + project-decision |
company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |
| D3 | domain-core forbidden import rule (Lombok 포함) |
raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1, raw/official-docs/arch-clean-architecture-uncle-bob.md, raw/official-docs/lombok-builder-data-features-official.md#LMB-C1, raw/official-docs/lombok-builder-data-features-official.md#LMB-C2, raw/official-docs/lombok-builder-data-features-official.md#LMB-C4, raw/official-docs/lombok-builder-data-features-official.md#LMB-C5 / CONTRARY EVIDENCE: raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1, #BUCKPAL-LOMBOK-C2 (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 lombok.. 명시 allowlist — 정반대 방향) |
company-case-study + engineering-blog + official-vendor-doc + UNSUPPORTED_DECISION (CONTRARY) |
LMB-C1~C5 는 @Builder/@Data 가 무엇을 생성하는지만 증명. Lombok 금지 자체는 OSS best practice 가 아님 — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |
| D4 | application-core -> adapter-* / app-bootstrap 의존 금지 |
raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2, raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4, raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3 |
company-case-study + engineering-blog |
repository port가 아직 domain/repository에 남은 부분은 feature-application-port-usecase-contract와 동기화 필요 |
| D5 | adapter module 간 직접 의존 금지 | raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C2, raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4 |
company-case-study |
Spring Modulith 없이 public API/named interface 강제는 약함. ArchUnit/package-private convention 필요 |
| D6 | shared-contract business/domain concept 금지 |
raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1, raw/branch-notes/feature-skeleton-package-blueprint-contract.md |
engineering-blog + project-decision |
shared module이 커질수록 common dumping ground가 될 위험 |
| D7 | sample-portfolio production 역수입 금지 |
raw/branch-notes/feature-skeleton-package-blueprint-contract.md |
project-decision |
외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |
| D8 | application @Transactional 직접 import 금지 |
raw/branch-notes/feature-application-port-usecase-contract.md, raw/official-docs/spring-tx-management-reference.md / CONTRARY EVIDENCE: raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-TX-C1 (Spring Modulith 공식 incubator 가 @ApplicationModuleListener 로 @Transactional 을 meta-annotation 재노출 — Spring 팀 방향과 정면 충돌), raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-TX-C1 (hex-arch 공식 reference 가 @Component @Transactional 직접 부착) |
project-decision + official-vendor-doc + UNSUPPORTED_DECISION (CONTRARY) |
Spring 공식은 @Transactional 을 지원하고 Spring Modulith 는 meta-annotation 으로 재노출. Buckpal hex-arch reference 도 직접 부착. ca-tmpl D8 forbidden 정책은 OSS best practice 가 아님 — multi-Gradle-module Hexagonal context 에서 application 모듈 순수성을 위한 자체 stricter 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |
| D9 | MapStruct generated exemption은 @Generated annotation 기반 ArchUnit predicate 로 허용 |
raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1, #MS-ANNOT-C2 (MapStruct @Generated annotation 부착 공식 + suppressGeneratorTimestamp 옵션) + raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1 (Spring Modulith 자체가 annotatedWith(Generated.class) predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) |
official-vendor-doc + company-case-study |
annotation FQN 주의: MapStruct = javax.annotation.processing.Generated, Spring AOT = org.springframework.aot.generate.Generated — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: .and().areNotAnnotatedWith(javax.annotation.processing.Generated.class). ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (needs-confirmation from planned → actually-implemented 승급 가능) |
| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6, raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5 |
official-vendor-doc |
FreezingArchRule baseline 정책은 별도 claim 보강 전까지 needs-confirmation으로 둠 |
| D11 | application-core 가 org.springframework.context.ApplicationContext 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 getBean(Class<T>) 호출까지는 catch 가능 |
raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5 (JavaMethodCall / JavaConstructorCall bytecode 기반 access analysis) |
official-vendor-doc |
string-key bean lookup (getBean(String)) 과 Class.forName(String) 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |
| D12 | string-key bean lookup / Class.forName(String) / BeanFactory#getBeansOfType 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 |
raw/official-docs/archunit-user-guide.md (§6.2 "accesses ... bytecode offers all this information" — bytecode 가 string content 자체를 노출하지 않음) |
official-vendor-doc (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) |
runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator /beans, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |
검증해야 할 주장
공식 문서나 사례는 근거지만, 내 프로젝트에서의 동작을 자동으로 보장하지 않는다. 구현 전/중/후에 실제로 검증해야 하는 주장을 분리한다.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
domain-core가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 |
rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → ./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest' 실패 확인 + ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package negative test 통과 확인 |
actually-implemented (2026-05-28 round 2) — Lombok forbidden 추가됨 (lombok.. package). violations-as-data fixture SpringDependentDomainFixture 가 domain_is_pure rule 의 실 catch 동작을 commit 된 negative test 로 보증. |
application-core가 adapter-* project dependency를 선언하면 Gradle 검증이 실패한다 |
Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | application-core에 implementation project(':adapter-persistence') 추가 → ./gradlew verifyCleanArchitectureDependencies 실패 확인 |
actually-implemented |
| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | adapter-web -> adapter-persistence dependency 추가 → Gradle/ArchUnit 실패 확인 |
actually-implemented |
shared-contract에 domain-specific package/class가 들어오면 실패한다 |
shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | shared-contract/src/main/java/.../shared/worklog/ 추가 → ArchUnit package scope rule 실패 확인 |
locally-verified |
production module이 sample-portfolio에 의존하면 실패한다 |
sample은 편의상 import되기 쉬움 | production module에 implementation project(':sample-portfolio') 추가 → Gradle dependency rule 실패 확인 |
locally-verified |
| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | adapter-web controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 |
locally-verified |
application use case가 @Transactional을 직접 import하면 실패한다 |
Spring 공식은 @Transactional을 지원하므로 ca-tmpl 자체 결정임 |
violating use case 추가 → ArchUnit forbidden import 실패 확인 | locally-verified |
| MapStruct generated mapper exemption이 의도한 package/path에만 적용된다 | generated source path가 build tool 설정에 따라 달라질 수 있음 | MapStruct sample mapper 추가 → generated path 확인 → exemption 외 경로 import 시 실패 확인 | needs-confirmation |
| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | ApplicationContext#getBean 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 |
actually-implemented (2026-05-28 round 2) — D11 application_does_not_depend_on_application_context ArchUnit rule 작성. ApplicationContextDependentFixture negative test 가 catch 동작 commit 보증. getBean(String) string-key bypass 와 Class.forName(String) 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — application-core/CLAUDE.md forbidden 섹션에 명시. |
| ca-tmpl repo-local workflow docs가 non-trivial 구현 후 LLM Wiki branch-note와 derived raw notes 캡처를 요구한다 | 문서 규칙 반영만으로 실제 에이전트 실행을 자동 보장하지는 않음 | AGENTS.md, CLAUDE.md, .agents/.claude/.codex 지침에서 llm-wiki-capture 및 Wiki capture 문구 검색 |
documented-only |
ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — violations-as-data pattern 채택 가능 |
현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 modules.detectViolations().getMessages() + example/ninvalid fixture package 가 production precedent |
src/app-bootstrap/src/test/.../violations/ 패키지에 의도된 위반 fixture class 추가 + assertThatThrownBy(rule::check) 또는 evaluationResult.getFailureReport().getDetails() assertion 으로 exact match 검증. 근거: raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2 |
actually-implemented (2026-05-28 round 2) — src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/ 에 6 fixture (1 domain + 5 application) + ArchitectureViolationFixtureTest 에 6 negative test 작성. 각 test 가 ClassFileImporter().importPackages("...violations") 로 fixture 만 로드한 뒤 해당 rule 의 EvaluationResult.hasViolation() == true assert. fixture 는 src/test/... 위치라 main @AnalyzeClasses(importOptions = DoNotIncludeTests.class) 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith example/ninvalid 패턴의 ca-tmpl 채택. |
테스트 계약
domain-core가 Spring / JPA / HTTP DTO / adapter module type을 참조하면 실패.application-core가adapter-web,adapter-persistence,adapter-outbound,app-bootstrap에 의존하면 실패.- adapter module끼리 직접 의존하면 실패.
- production module이
sample-portfolio을 import하거나 dependency로 선언하면 실패. shared-contract에 business/domain package 또는 domain-specific class가 추가되면 실패.adapter-webcontroller가 domain object를 response로 직접 반환하면 실패.application-core가org.springframework.transaction.annotation.Transactional을 직접 import하면 실패.- MapStruct generated exemption 밖의 generated code 우회가 있으면 실패.
application-core가org.springframework.context.ApplicationContext를 직접 의존하면 실패 (D11 banned-class rule).getBean(Class)class-literal 호출도 이 rule 로 catch.domain-core가 Lombok generated bytecode 를 포함하면 실패 (@Builder/@Data/@Getter/@Setter등 Lombok annotation 사용 금지 —feature-skeleton-package-blueprint-contractOption A 채택).
구현 가이드
본 branch 의 결정 → 구현 위치 명세. 각 row 는 본 branch 의
Decision ID+Supporting Claimreference 를 가진다(CLAUDE.md §15.5 R1). 근거가 원칙 만 권고하고 detail 은 구현자 trade-off 인 cell 은UNSUPPORTED_IMPL_DECISION라벨을 단다(R2). 본 branch 범위 밖 detail 은 남기지 않는다(R3).⚠️ 이 명세는 사후 정제 다 — 본 branch 는 2026-05-28 시점에 이미 구현·검증 완료(§진행 중 메모)되었고, 본 section 은 ground-truth(
@db61075) 와 대조해 실제 구현 위치를 역으로 명세화한 것이다.
| Decision | 구현 위치 (ground-truth @db61075) |
메커니즘 detail | Trace |
|---|---|---|---|
| D1 (test 강제) | app-bootstrap/.../architecture/CleanArchitectureTest.java |
@AnalyzeClasses(packages = "dev.caskeleton", importOptions = DoNotIncludeTests.class) + @ArchTest static final ArchRule 필드들. @AnalyzeClasses import scope 선택은 UNSUPPORTED_IMPL_DECISION — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (dev.caskeleton root 단일 scan 으로 결정) |
D1 / ARCHUNIT-UG-C5,C6 |
| D2 (build-graph) | src/build.gradle:53 verifyCleanArchitectureDependencies task |
allowedProjectDependencies Map<String,Set> 화이트리스트 9 module + configurations(api/implementation/compileOnly/runtimeOnly).dependencies.withType(ProjectDependency) 차집합 → GradleException. matrix 구체 값은 UNSUPPORTED_IMPL_DECISION — blueprint 결정(raw/branch-notes/feature-skeleton-package-blueprint-contract)의 module 목록에서 도출한 trade-off |
D2 / WW-HEX-C1, KAKAOBANK-MOD-C4 |
| D3 (domain purity + Lombok) | domain_is_pure rule |
noClasses().that().resideInAPackage("..domain..").should().dependOnClassesThat().resideInAnyPackage(..., "lombok..", ...) + .allowEmptyShould(true). forbidden package 목록 구체값은 UNSUPPORTED_IMPL_DECISION (CONTRARY: Buckpal 은 lombok.. allowlist — D3 Open Risk) |
D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |
| D4 (application 격리) | application_does_not_depend_on_adapters_or_transport rule |
noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().resideInAnyPackage("..adapter..","..bootstrap..","org.springframework.web..",...) |
D4 / WW-HEX-C2, HEX-COCKBURN-ORIG-C3 |
| D5 (adapter-adapter 격리) | web_/persistence_/outbound_adapter_does_not_depend_on_* 3 rule |
각 adapter package 가 sibling adapter package 에 의존 금지. ArchUnit package glob 으로 구현 (Spring Modulith named interface 미사용 — D5 Open Risk) | D5 / KAKAOBANK-MOD-C2,C4 |
| D6 (shared scope) | shared_contract_contains_only_operational_contract_packages rule |
classes().that().resideInAPackage("..shared..").should().resideInAnyPackage(<operational allowlist>). allowlist package 집합은 UNSUPPORTED_IMPL_DECISION — blueprint 의 operational contract 목록에서 도출 |
D6 / SCREAM-C1 |
| D7 (sample 역수입 금지) | production_code_does_not_depend_on_sample_portfolio rule |
noClasses().that().resideOutsideOfPackage("..sample.portfolio..").should().dependOnClassesThat().resideInAPackage("..sample.portfolio..") |
D7 (project-decision) |
| D11 (ApplicationContext banned-class) | application_does_not_depend_on_application_context rule |
noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().haveFullyQualifiedName("org.springframework.context.ApplicationContext"). FQN 단일 class 선택은 bytecode access analysis(ARCHUNIT-UG-C5)로 getBean(Class) 까지만 catch |
D11 / ARCHUNIT-UG-C5 |
| S1 (violations-as-data) | ArchitectureViolationFixtureTest + architecture/violations/ package |
본 branch fixture: SpringDependentDomainFixture(D3), ApplicationContextDependentFixture(D11), TransactionalAnnotatedFixture. 각 test 가 rule.evaluate(VIOLATION_CLASSES).hasViolation()==true assert. fixture 가 src/test/ 위치라 main DoNotIncludeTests 분석 제외 → vacuous pass 방지 |
S1 / SPRING-MOD-AU-C2 |
OUT_OF_BRANCH_SCOPE (R3): ground-truth
CleanArchitectureTest의 boundary-validation(B1/B2/B4/B5/B6/B7, D5 ProblemDetail), streaming(D3 SSE/WebSocket), serialization(BigDecimal), resource-identifier(D17 no_long_id_pk 등), api-contract(D19 AIP-122), business-rule-validation(C1/D1 jakarta.validation) rule 들은 각각 다른 branch 소유다. 본 §에는 남기지 않으며 해당 branch ingest 에서 명세한다. @Transactional ban rule(application_does_not_use_spring_transactional_annotation)은 코드 attribution 상 raw/branch-notes/feature-application-port-usecase-contract D3 소유지만 본 branch 테스트 계약에도 포함되어 red/green 확인됨 — SSOT 는 그 branch.
엣지·실패·의존
본 branch 구현의 경계 조건 · 알려진 실패 모드 · 외부 의존. ArchUnit 정적 분석의 한계를 정직하게 남긴다.
- Edge — empty anchor: skeleton 단계의 빈 module 은
that()매칭 대상이 0개라 ArchUnit 기본 동작상failed to check any classes로 실패한다. 의도된 빈 anchor rule 에allowEmptyShould(true)를 명시해 허용. 근거 사실: raw/errors/archunit-empty-should-anchor-2026-05-27. - Failure — vacuous pass (import scope 누락): 검사 대상 class 가
@AnalyzeClassesimport scope 밖이면 위반이 있어도 rule 이 조용히 통과(BUILD SUCCESSFUL, 에러 신호 없음)한다.allowEmptyShould(true)도 이 false-negative 를 막지 못함. 보완: sample/fixture 를 test import scope 에 포함 + violations-as-data negative fixture(S1) 로 catch 동작을 commit 보증. 근거 사실: raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28. - Failure — D11/D12 string-key bypass (알려진 한계, 보완 불가): D11 banned-class rule 은 class-literal
getBean(Class<T>)까지만 catch 한다. string-keygetBean(String)·Class.forName(String)·BeanFactory#getBeansOfType같은 reflection-style bypass 는 bytecode 가 문자열 내용을 노출하지 않아 ArchUnit 정적 분석으로 잡을 수 없다(D12).application-core/CLAUDE.mdforbidden 섹션의 code review checklist 로만 보완 — 자동 강제 장치 아님. runtime 검증(Actuator/beans, Modulith verifier)은 후속 후보(D1/D12 Open Risk). - Dependency — testCompileOnly:
TransactionalAnnotatedFixture가@Transactional을 import 하기 위해app-bootstrap/build.gradle에testCompileOnly 'org.springframework:spring-tx'추가(production 영향 없음). 관련 class-loading 이슈: raw/errors/archunit-testcompileonly-class-loading-2026-06-02. - Dependency — sandbox/Gradle: Gradle wrapper 가 sandbox 기본 권한에서
~/.gradlelock 파일 생성 실패 → escalated 실행으로 해결. 근거: raw/errors/gradle-wrapper-readonly-cache-2026-05-28. - Edge — MapStruct exemption (미검증): D9 generated mapper
@Generatedexemption 은 ca-tmpl 에 실제 MapStruct mapper 가 아직 없어needs-confirmation— sample mapper 추가 후 통합 검증 필요.
마주친 문제
-
2026-05-28: Gradle wrapper sandbox 권한 문제
- 원인: sandbox 기본 권한 정책 상
~/.gradle디렉터리 쓰기가 차단되어 wrapper 가 lock/cache 파일 생성 실패. - 시도: 기본 권한으로
./gradlew :app-bootstrap:test실행 →Read-only file system오류. - 해결: 사용자 승인된 escalated 실행으로 동일 명령 재수행 → 성공. 검증 결과는 본 branch-note
진행 중 메모2026-05-28 항목 참조. - 별도 에러 노트: raw/errors/gradle-wrapper-readonly-cache-2026-05-28
- 원인: sandbox 기본 권한 정책 상
-
2026-05-28: 계획 문서가
.gitignore의/docs규칙에 가려져 git untracked- 원인: ca-tmpl
.gitignore가/docs디렉터리를 전면 제외 (operational docs 는 별도 repo 분리 정책). - 시도:
docs/superpowers/plans/2026-05-28-architecture-enforcement-rules.md작성 →git status에 미포함 확인. - 해결: 계획 문서는 작업용으로만 유지하고 최종 기록은 본 branch-note 의
진행 중 메모/결정 사항/Closure섹션에 통합. 계획 문서 위치는 untracked 로 두되 본 메모에서만 참조. - 별도 에러 노트: (해당 없음 — 운영 메모, 재발 시 동일 정책 적용)
- 원인: ca-tmpl
-
2026-05-28: repo-local workflow 문서 패치 중 자동 승인 검토 차단
- 원인:
.agents/.claude/.codex프롬프트 반영 패치 도중 도구의 automatic approval review가 patch 적용을 차단. - 시도: 먼저
AGENTS.md,CLAUDE.md,llm-wiki-capture.md까지 반영한 뒤 남은 plugin/agent prompt 반영을 진행하려 했으나 중단. - 해결: 사용자에게 차단 상태와 부분 반영 범위를 보고하고 명시 승인을 받은 뒤 남은 파일을 계속 반영.
- 별도 에러 노트: raw/errors/apply-patch-auto-approval-rejected-2026-05-28
- 원인:
-
2026-06-30: develop 머지 과정에서의 매퍼 아키텍처 규칙 오탐지
- 원인:
..mapper..패키지 규칙이 영속성 매퍼뿐만 아니라 웹 매퍼까지 과도하게 필터링하여 웹/애플리케이션 레이어 의존성을 차단함. - 해결: 영속성 매퍼(
..adapter.persistence..mapper..)로 대상을 좁히고, Clean Architecture 의존성 방향(영속성 -> 애플리케이션 허용)에 맞춰 금지 목록에서..application..을 제외함.
- 원인:
묶음 (이 branch에서 파생된 자료)
- raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional
- raw/company-tech-blogs/feature-first-sahibinden-package-by-layer-vs-feature
- raw/company-tech-blogs/hexagonal-woowahan-techblog-2023
- raw/company-tech-blogs/layer-first-kamilmazurek-github-template
- raw/company-tech-blogs/modulith-arawn-github-modular-monoliths-spring
- raw/company-tech-blogs/modulith-kakaobank-techblog-2025
- raw/company-tech-blogs/onion-allegro-tech-blog-2023
- raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data
- raw/official-docs/adapter-spring-boot-autoconfig-custom-starter
- raw/official-docs/arch-clean-architecture-uncle-bob
- raw/official-docs/arch-hexagonal-cockburn
- raw/official-docs/archunit-user-guide
- raw/official-docs/domain-fowler-anemic-vs-rich-model
- raw/official-docs/domain-vaughn-vernon-aggregate-root
- raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011
- raw/official-docs/hexagonal-cockburn-wikipedia-summary
- raw/official-docs/hexagonal-thombergs-buckpal-github
- raw/official-docs/layer-first-baeldung-clean-architecture-spring-boot
- raw/official-docs/lombok-builder-data-features-official
- raw/official-docs/mapstruct-generated-annotation-official
- raw/official-docs/modulith-spring-official-doc
- raw/official-docs/onion-palermo-original-2008
- raw/official-docs/persistence-osiv-antipattern-hibernate-vladmihalcea
- raw/interviews/archunit-manual-importer-vs-analyzeclasses
- raw/interviews/archunit-static-analysis-limits
- raw/interviews/clean-architecture-boundary-enforcement
- raw/interviews/post-implementation-knowledge-capture
- raw/errors/apply-patch-auto-approval-rejected-2026-05-28
- raw/errors/gradle-wrapper-readonly-cache-2026-05-28
- raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28
- raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28
- raw/blog-topics/post-implementation-knowledge-capture-workflow-2026-05-28
이 섹션은 이 branch-note에서 실제로 파생된 raw/wiki 문서가 생겼을 때 링크한다. 구현 결과와 검증 증거는
TODO,진행 중 메모,Claims To Verify,Closure에 기록한다.
근거 자료
- raw/official-docs/mapstruct-generated-annotation-official — D9: MapStruct
@Generatedannotation 공식 근거 (MS-ANNOT-C1, MS-ANNOT-C2) - raw/official-docs/lombok-builder-data-features-official — D3:
domain-coreLombok 금지 결정 공식 근거 (@Builder7가지 생성 요소 +@Datasetter 생성 범위, LMB-C1~LMB-C5) - raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data — D9 corroborate: Spring Modulith 자체가
annotatedWith(Generated.class)predicate 를 production 에서 사용 (SPRING-MOD-AU-C1). S1 negative test fixture:detectViolations()violations-as-data 패턴 (SPRING-MOD-AU-C2) - raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional — D3 CONTRARY evidence: Buckpal domain purity ArchUnit rule 이
lombok..를 명시적 allowlist 함 (BUCKPAL-LOMBOK-C1, BUCKPAL-LOMBOK-C2). ca-tmpl D3 가 OSS 다수파가 아닌 stricter stance 임을 뒷받침
Sub-branches (세부 작업)
- (없음 — 이 branch는 별도 sub-branch 없이 ca-tmpl 코드 변경 2개 파일로 진행)
오류 기록 (이 branch 작업 중 발생)
- raw/errors/gradle-wrapper-readonly-cache-2026-05-28 — Gradle wrapper가 sandbox 기본 권한에서
~/.gradlelock 파일을 만들지 못한 문제 - raw/errors/apply-patch-auto-approval-rejected-2026-05-28 — repo-local workflow 문서 패치 중 자동 승인 검토가 차단되어 사용자 명시 승인 후 재개한 문제
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- raw/interviews/clean-architecture-boundary-enforcement — Clean Architecture 경계를 Gradle/ArchUnit으로 자동 검증한 경험에서 파생된 예상 질문
- raw/interviews/post-implementation-knowledge-capture — 구현 완료 후 branch-note와 파생 raw 문서를 어떻게 남길지에 대한 예상 질문
- raw/interviews/archunit-static-analysis-limits — ArchUnit static analysis 의 한계 (string-key bypass / vacuous pass / generated code) 와 violations-as-data 보완 패턴 (round 2 D11/D12 + Claims to Verify).
강의 (이 작업을 위해 학습한 강의)
- (없음 — 이번 구현 중 새 lecture note 생성 없음)
job-posting tie-ins (이 작업에서 파생된 글감)
- raw/blog-topics/clean-architecture-boundary-enforcement-2026-05-28 — Clean Architecture 경계를 Gradle/ArchUnit rule로 자동 검증한 경험에서 파생된 블로그 글감
- raw/blog-topics/post-implementation-knowledge-capture-workflow-2026-05-28 — 구현 완료 후 지식 캡처를 repo-local workflow로 강제하는 설계 글감
- raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28 — Spring Modulith 의
example/ninvalid패턴을 차용해 6 fixture + 6 negative test 로 ArchUnit rule 의 실 catch 동작을 commit 보증한 작업 글감 (round 2). - (job-posting 없음 — 이번 작업에는 연결할 실제 채용공고 원문/URL이 없음)
관련 일일 노트
완료 후 정리
머지/종료 시점에 채움.
/ingest가 이 섹션을 기준으로 wiki/projects/에 추출.
- PR 링크: (미생성 — local branch
feature/architecture-enforcement-rules, not merged) - 리뷰 메모: 2026-05-28 local branch
feature/architecture-enforcement-rules에서 Gradle dependency verifier와 ArchUnit rule을 구현/보강했다. 구현 파일은 ca-tmplsrc/build.gradle,src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java. - 머지 결과 / 배포 환경: not merged. local verification only.
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:- Gradle
verifyCleanArchitectureDependencies가 모든 declared module이 정책에 포함되는지 검사하고, 허용되지 않은 project dependency를 실패 처리한다. CleanArchitectureTest가 domain purity (Spring/JPA/Hibernate/Lombok 모두 forbidden), application -> adapter/bootstrap 금지, adapter 간 직접 의존 금지, web DTO boundary, production -> sample-portfolio dependency 금지, application@Transactional금지, applicationApplicationContext금지 (D11), inbound use-case naming + capability mandatory + KEYED idempotency freeze (D14) 를 검사한다.ArchitectureViolationFixtureTest가 위 rule 6종의 실 catch 동작을 violations-as-data fixture 로 보증한다 (src/app-bootstrap/src/test/java/.../violations/).
- Gradle
locally-verified항목:- 임시
shared.worklogpackage 추가 시 shared-contract package allowlist rule이 실패함을 확인했다. - 임시 controller가 production domain object를 직접 반환할 때 ArchUnit rule이 실패함을 확인했다.
- 임시 mapper가 application boundary에 의존할 때 mapper boundary rule이 실패함을 확인했다.
- 임시 application class가 Spring
@Transactional을 import할 때 ArchUnit rule이 실패함을 확인했다. - 임시
app-bootstrap -> sample-portfolioproject dependency 선언 시verifyCleanArchitectureDependencies가 실패함을 확인했다. cd src && ./gradlew :app-bootstrap:test verifyCleanArchitectureDependencies성공.cd src && ./gradlew test성공.
- 임시
documented-only항목:- ca-tmpl repo-local 문서에 non-trivial 구현 후 LLM Wiki branch-note와 derived raw notes 캡처를 수행하도록
documented-onlyworkflow rule을 추가했다.
- ca-tmpl repo-local 문서에 non-trivial 구현 후 LLM Wiki branch-note와 derived raw notes 캡처를 수행하도록
prod-verified항목:- (없음)
- 추출하지 않을 항목 (planned / documented-only / abandoned):
- MapStruct generated mapper exemption은 아직
needs-confirmation이다. - runtime lookup / reflection 우회 false-pass 확인은 아직
planned이다. - Spring Modulith verifier 도입은 out of scope 후속 후보로 유지한다.
- SonarQube custom rule 구현과 CI workflow job 분리는 out of scope다.
- MapStruct generated mapper exemption은 아직