47 lines
3.8 KiB
Markdown
47 lines
3.8 KiB
Markdown
---
|
|
title: interview / domain-modeling-guardrails-archunit-2026-06-05
|
|
source_type: interview
|
|
status: raw
|
|
related_branches: [feature-domain-modeling-guardrails]
|
|
related_projects: [ca-skeleton]
|
|
tags: [interview, ca-skeleton, archunit, ddd, value-object, aggregate, domain-event, clean-architecture]
|
|
created: 2026-06-05
|
|
status_label: captured
|
|
---
|
|
|
|
# interview: domain-modeling-guardrails-archunit-2026-06-05
|
|
|
|
> Layer: `raw/interviews/` — 이 작업에서 정직하게 도출 가능한 면접 질문. canonical 승급 전 raw.
|
|
|
|
## Parent
|
|
|
|
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
|
|
|
|
## 질문 목록
|
|
|
|
**Q1.** DDD 전술 패턴(값 객체/애그리거트/도메인 이벤트)을 "문서 권고"가 아니라 빌드에서 강제하려면 어떻게 하나?
|
|
- A: stereotype 마커 애너테이션(`@ValueObject`/`@AggregateRoot`/`@DomainEvent`)을 도메인 코어에 두고, ArchUnit fitness function 이 그 마커를 키로 규칙을 평가. 값 객체 = public no-arg 생성자 부재, 애그리거트 = `set*` 비공개, 도메인 이벤트 = record + transport 패키지 의존 금지.
|
|
|
|
**Q2.** "도메인 순수성(framework-neutral)" 규칙과 "도메인 logger 금지" 규칙을 왜 한 규칙으로 합치지 않고 분리했나?
|
|
- A: owner 경계. 도메인 순수성(`domain_is_pure`)은 `feature-architecture-enforcement-rules` 가 소유. 거기에 logging 패키지를 끼우면 한 branch 의 결정이 다른 branch owner 규칙에 섞여 위반 메시지·소유권이 흐려진다. 별도 `domain_has_no_logger` 로 두면 위반 사유가 명확하고 owner 가 분리된다.
|
|
|
|
**Q3.** 도메인 logger 금지의 "공식 표준 출처"가 있나?
|
|
- A: 없다. clean-architecture 통념이지 RFC/vendor 표준이 아니다. 그래서 프로젝트 자체 규약(UNSUPPORTED_DECISION)으로 확정하고, 외부(면접/README)에서 "표준이라 막았다"고 말하지 않는다. 사실 등급을 격상하지 않는 정직성.
|
|
|
|
**Q4.** 불변식 위반을 도메인에서 어떻게 표현하나? 왜 로그가 아니라 예외인가?
|
|
- A: 안전한 명사형 reason enum 을 가진 도메인 예외(`WorkLogInvariantException(Reason)`). 도메인은 로그/운영 에러코드를 모르고(D2/D3), application/web 이 `reason()` 을 error.category·로그로 번역. security-sensitive 사유는 일반화된 category 로만 노출해 단서 누출 방지.
|
|
|
|
**Q5.** 값 객체 불변식을 단위 테스트 몇 개로 "충분히" 검증했다고 할 수 있나?
|
|
- A: 못 한다. 예시 기반 테스트는 저자가 고른 케이스만 본다. jqwik property-based test 로 입력 공간 전체(canonical ULID, 비-canonical, 제외문자/소문자)를 무작위 생성해 불변식이 유일 생성 경로에서 항상 강제됨을 검증.
|
|
|
|
**Q6.** 도메인 이벤트를 "transport-free" 로 둔다는 게 무슨 의미이고, 통합(integration) 이벤트와 어떻게 분리하나?
|
|
- A: 도메인 이벤트는 도메인 타입만 담는 immutable record. Kafka/HTTP/JAX-RS 타입을 참조하면 안 됨(ArchUnit `domain_events_are_transport_free`). wire 표현으로의 변환(값 객체 → primitive flatten, 직렬화 포맷 선택)은 application 경계의 mapper 책임 → `WorkLogReserved`(domain) → `WorkLogReservedIntegrationEvent`(application).
|
|
|
|
**Q7.** ArchUnit 로 강제 가능한 범위의 한계는?
|
|
- A: `set*` prefix 같은 정적 시그니처는 잡지만, `applyXxx`/`markAsXxx` 같은 임의 상태변경 메서드나 Kotlin `copy()`/record wither 우회는 정적으로 못 잡는다. 그 부분은 코드리뷰·네이밍 컨벤션으로 보완하고 Open Risk 로 명시.
|
|
|
|
## Cross-links
|
|
|
|
- [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]] — fixture 작성 중 부딪힌 JUnit discovery 함정
|
|
- [[raw/interviews/archunit-static-analysis-limits]] — ArchUnit 정적 분석 한계 일반론
|