--- title: blog-topic / domain-modeling-guardrails-as-archunit-fitness-functions-2026-06-05 source_type: blog-topic status: raw related_branches: [feature-domain-modeling-guardrails] related_projects: [ca-tmpl] tags: [blog-topic, ca-tmpl, archunit, fitness-function, ddd, value-object, aggregate, domain-event, jqwik, clean-architecture] created: 2026-06-05 status_label: ready-for-canonical target_audience: backend-engineer inspiration_url: archive_url: --- # blog-topic: domain-modeling-guardrails-as-archunit-fitness-functions-2026-06-05 > Layer: `raw/blog-topics/` — 작업·트러블슈팅에서 나온 글감 원석. canonical 정제 전 raw. ## Parent - [[raw/branch-notes/feature-domain-modeling-guardrails]] ## 한 줄 글감 "DDD 전술 패턴을 README 권고가 아니라 빌드 깨짐으로 강제하기 — stereotype 애너테이션 + ArchUnit fitness function." ## 본문 뼈대 (초안) 1. **문제**: rich domain model / 값 객체 불변식 / transport-free 도메인 이벤트는 보통 "문서 권고"로 남고 시간이 지나면 침식된다(anemic 회귀, public setter, 도메인에 Kafka 타입 누출). 2. **접근**: 의미를 드러내는 마커 애너테이션을 도메인 코어에 둔다 — `@ValueObject`/`@AggregateRoot`/`@DomainEvent` (java.lang.annotation 만 의존, 프레임워크 0). 3. **강제**: ArchUnit 규칙이 마커를 키로 평가 - `value_objects_have_no_public_no_arg_constructor` — 빈 생성자 = 불변식 우회 백도어 차단. record(컴포넌트 보유)는 자동 충족. - `aggregate_root_setters_are_not_public` — `set*` 비공개 강제(Vernon Option A: ORM 외부 매핑 가시성). - `domain_events_are_records` + `domain_events_are_transport_free` — immutable record + Kafka/HTTP/JAX-RS 패키지 의존 금지. - `domain_has_no_logger` — 도메인은 로그 대신 안전한 명사형 reason enum 예외로 위반을 표현. 4. **owner 경계 교훈**: "도메인 순수성" 규칙(다른 branch 소유)에 logger 금지를 끼워넣지 않고 별도 규칙으로 분리한 이유 — 규칙 소유권/위반 메시지 명확성. 5. **정직성 교훈**: logger 금지는 공식 표준이 아니라 프로젝트 자체 규약. 사실 등급을 격상하지 않는다. 6. **비공허(non-vacuous) 증명**: 규칙마다 의도적 위반 fixture + 격리 코퍼스로 "실제로 잡는다"를 테스트(violations-as-data). transport glob 은 broker별 격리 증명. 7. **함정**: `testCompileOnly` 타입을 record component 로 쓰면 JUnit *discovery* 가 죽는다 → method body `.class` 참조로 회피([[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]]). 8. **불변식 검증의 깊이**: 예시 테스트 대신 jqwik property-based test 로 값 객체 입력 공간 전체를 무작위 검증. 9. **이벤트 경계 PoC**: `WorkLogReserved`(domain) → `WorkLogReservedIntegrationEvent`(application mapper) — 도메인은 wire 를 모른다. ## Cross-links - [[raw/blog-topics/archunit-testcompileonly-fixture-annotation-pattern-2026-06-02]] - [[raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28]] - [[raw/interviews/domain-modeling-guardrails-archunit-2026-06-05]] ## 트리거 / Trigger - 트리거 유형: `branch-work` - 트리거 날짜: 2026-06-05 - 트리거 연결 노트: [[raw/branch-notes/feature-domain-modeling-guardrails]] ## 글감 / Topic seed - 한 문장 요지: DDD tactical pattern을 README 권고가 아니라 marker annotation + ArchUnit fitness function으로 빌드 단계에서 강제한다. - 예상 제목 후보: - DDD guardrail을 ArchUnit fitness function으로 만들기 - Value Object와 Domain Event 규칙을 빌드에서 검증하기 ## 핵심 주장 후보 / Claim candidates - 사실 후보: - `@ValueObject`, `@AggregateRoot`, `@DomainEvent` 같은 marker를 ArchUnit rule의 평가 key로 쓴다. - 의견/해석 후보: - domain modeling 규칙은 공식 표준이 아니라 project-local guardrail이므로 사실 등급을 조심해야 한다. ## Outline seed 1. tactical DDD rule이 문서 권고로만 남을 때 침식되는 경로를 설명한다. 2. framework-free marker annotation과 ArchUnit rule의 역할을 나눈다. 3. violations-as-data와 jqwik property test로 guardrail이 실제로 bite하는지 확인한다. ## Canonical 전환 후보 / Canonical extraction candidates - `wiki/projects/ca-tmpl/clean-architecture-package-layout.md` 후보: - domain modeling guardrail / ArchUnit fitness function 글감. - 필요한 추가 검증: - 현재 marker annotation, ArchUnit rule, jqwik test 존재 여부. ## Sources / 근거 후보 - [[raw/branch-notes/feature-domain-modeling-guardrails]] - [[raw/blog-topics/archunit-violations-as-data-pattern-2026-05-28]] - [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]] ## 미해결 / Unknown - 아직 확인해야 할 사실: domain guardrail 구현과 property-based test의 현재 상태. - 과장하면 안 되는 부분: logger ban이나 marker taxonomy를 DDD 공식 표준처럼 쓰지 않는다. ## Decision / 처리 결정 - 액션: `promote-to-canonical` - 이유: `wiki/projects/ca-tmpl/clean-architecture-package-layout.md` 에 domain modeling guardrail 글감으로 반영한다. - 다음 단계: blogify 전 project-local rule과 구현 증거를 분리한다. ## Related / 관련 - 관련 branch: [[raw/branch-notes/feature-domain-modeling-guardrails]]