기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
Decision ID
Decision
Relation
Supporting Claims
Status
선언한 예외
Override ID
Overrides
Reason
Approval
Status
목표
notification 을 단일-provider 바이너리 플래그(app.notification.<kind>.provider=<id>) 에서
제네릭 NotificationPort + (channel, route) 키 레지스트리 + 외부 routes 바인딩 으로 전환.
cache(CacheStoreRouter)·messaging 패턴의 notification 이식.
채널별 분리 포트(EmailNotifier/SlackNotifier)·단일 selector 폐기.
멀티-provider fan-out, 채널/route 기반 라우팅(설정만으로), 부팅 시 일관성 검증.
2026-06-16: RoutingNotifier registry를 Map<Channel, Map<String, FailOpenNotificationProvider>>로 타입화해 fan-out 루프의 try/catch 제거 / 이유: 빈 catch 블록은 quality gate에서 차단되며 FailOpenNotificationProvider.send()가 throws 선언 없음 → 컴파일러가 예외 불가 증명 / 검토한 대안: NotificationProvider로 유지 + try/catch(empty) — 타입 안전성 부족, quality gate 차단 / 근거: 코드 내 FailOpenNotificationProvider.send() 시그니처
2026-06-16: 활성화 플래그 key 를 app.notification.google-email.enabled / app.notification.slack-webhook.enabled 로 통일 (cache의 app.cache.redis.enabled 패턴 미러) / 이유: provider id 를 플래그 이름에 직접 반영해 enabled flag → providerId 명확성 / 이전 설계(app.notification.email.provider=google-email) 폐기
2026-06-16: Notification 값 타입 app-core 이동 / 이유: use case가 port 인자를 구성할 때 adapter 타입 import 금지(CA HARD-STOP #3) / 대안: adapter에 유지 → HARD-STOP 위반
2026-06-16 (follow-up): 폴더를 <channel>/<tech> 구조로 이동 (googleemail→email/google, slack/*→slack/webhook) / 이유: 사용자가 채널/기술 분리 구조를 명시 선호 + 1차 패스가 남긴 빈 타겟 폴더 잔재 정리 / 영향: package 선언 6개, OptionalAdapterBeanGatingTest import 4개, DisabledAdapterArchitectureTest 패키지 패턴(googleemail..→email..; slack..는 webhook 하위 포함이라 무변경), adapter-outbound/CLAUDE.md 예시 경로 / 검증: 603/603 green / 클래스명 중복(email.google.GoogleEmailProvider)은 cosmetic churn 회피로 보류
adapter-outbound/CLAUDE.md No disabled-sentinel bean
rule-derived + locally-verified
없음
D5
폴더를 <channel>/<tech> 구조로 이동(googleemail→email/google, slack/*→slack/webhook) — 1차 지연 후 사용자 요청으로 후속 완료
사용자가 notification/email/google 구조 명시 선호; 코어 green 확보 후 저위험 시점
사용자 지시 + 패키지 이동 코드(grep 잔여 0)
user-directed + locally-verified
없음 (603 green)
구현 가이드
1. 활성화 플래그 명명 규칙
Trace: D2 + cache RedisCacheAdapterConfig 미러
UNSUPPORTED_IMPL_DECISION: app.notification.<providerId>.enabled 형식 선택 (Redis는 기술명 사용; notification은 providerId로 통일) — 확장 시 명확성 우선 trade-off.
provider send 실패: FailOpenNotificationProvider가 관측(logFailure) 후 삼킴 — fan-out 나머지 계속 (D3).
zero providers + zero routes: 깨끗이 생성 (L262 — optional module).
PII: Notification이 OutboundDependencyLogger에 전달되지 않음 — 생성자 타입 시그니처로 보장.
relaxed binding Channel key: Spring Boot 3.4 ApplicationConversionService가 email→EMAIL 변환 — OptionalAdapterBeanGatingTest에서 app.notification.routes.slack.default=slack-webhook 로 검증됨.
검증해야 할 주장
Claim
Why uncertain
How to verify
Status
relaxed binding이 email→EMAIL 변환
Spring 내부 동작
OptionalAdapterBeanGatingTest.slack_webhook_enabled_* / google_email_enabled_* green
locally-verified
RoutingNotifier 빈 catch 없이 컴파일러 증명
FailOpen.send() no-throws 가정
compileJava green
locally-verified
모든 구 타입 참조 0
11파일 삭제 후 잔여 임포트 없어야
compileTestJava green (모든 test 모듈)
locally-verified
전체 테스트 green
광범위한 변경
./gradlew :application-core:test :adapter-outbound:test verifyCleanArchitectureDependencies :app-bootstrap:test --tests '*CleanArchitectureTest' all green
GoogleEmailClient·SlackClient seam이 구 adapter-outbound.notification.Notification을 임포트하고 있었음 — GoogleEmailProvider 작성 후 IDE 진단에서 발견. 두 seam 인터페이스의 임포트를 application.notification.Notification으로 교체해 해소.
RoutingNotifier 생성자가 Collection<? extends NotificationProvider>를 받아 fan-out 루프에 try/catch(empty)가 필요했음 — 생성자 타입을 Collection<? extends FailOpenNotificationProvider>로 변경해 try/catch 완전 제거. RoutingNotifierTest의 raw stub도 failOpen() 헬퍼로 래핑.
묶음
Sub-branches
없음
오류 기록
없음 (마주친 문제는 위 §에 기록, 재발성 오류 없음)
면접 준비
후보: "SPI + 레지스트리 + fail-open 데코레이터 패턴을 adapter layer 에 적용하는 방법과 장단점" (messaging/cache/notification 3개에 반복 적용 — 패턴 재사용 근거)
후보: "멀티-provider 선택을 supports() 술어(코드) 대신 외부 routes 바인딩(설정)으로 둔 이유 — adapter 에 도메인 정책이 새면 CA HARD-STOP #4 위반; 설정 기반은 어댑터가 도메인 미열람이라 구조적으로 위반 불가" (Novu/AWS SNS/cache 수렴 근거)
후보: "라우팅(키→1개) vs 팬아웃(1→N) 구분과, 바인딩 값을 providerId 리스트로 두어 둘을 한 메커니즘으로 통합한 설계"
Blog topics
후보: "CA 스켈레톤에서 notification 을 멀티-provider 라우팅으로 확장하기 — 빈 catch 없는 타입 안전 fail-open 구현"
진행 중 메모
SPI·registry·router 구현과 검증 상태는 TODO와 Verification 절을 기준으로 추적한다.
관련 일일 노트
별도 일일 노트 없음.
완료 후 정리
PR 링크: 미완료
머지 결과: locally-verified, unstaged (사용자 커밋 대기)
wiki 추출 대상: D1-D4, RoutingNotifier 타입화 결정, 활성화 플래그 명명 규칙