feat: add production capability foundations

This commit is contained in:
donghyeon-ka
2026-07-31 23:50:44 +09:00
parent b3add0162d
commit 567422f2e5
757 changed files with 132385 additions and 2146 deletions
+49 -5
View File
@@ -1,13 +1,34 @@
# adapter:outbound:notification — 설계 결정 참조
# adapter:outbound:notification — R0 legacy truth
> 현재 구현 전체는 교체 전 호환성 기준선인 `R0 legacy`다. `GoogleEmailClient`와
> `SlackClient`는 project-supplied seam일 뿐 실제 Google Mail 또는 Slack 연동이 아니며,
> provider/card qualification evidence도 없다.
알림(email/Slack 등) 아웃바운드 어댑터 모듈. 패키지 루트:
`dev.caskeleton.adapter.outbound.notification`. `:adapter:outbound:support` 에 의존해 공유
correlation / fail-open 의존성 로깅을 재사용한다.
허용/금지 의존 정책은 `src/build.gradle`
`allowedProjectDependencies['adapter:outbound:notification']` 항목이 SSOT 다(이 모듈은 아직
별도 CLAUDE.md 를 두지 않았다). 이 문서는 코드 주석에서 덜어낸 **설계 결정의 근거**를 모아둔
참조용 기록이다.
허용/금지 의존 정책은 `src/config/architecture/modules.json`
`adapter-outbound-notification` row가 SSOT다. 이 문서는 코드 주석에서 덜어낸 **설계 결정의
근거**와 canonical 구현 전 삭제 대상을 모아둔 참조용 기록이다.
## Task 1 R0 truth table
| 축 | 현재 사실 | 증거와 한계 |
| --- | --- | --- |
| application contract | raw `NotificationPort.notify(Channel, route, Notification)` | mode, transaction, receipt, attempt certainty가 없는 R0 port |
| routing | `(channel, providerId)` registry + route별 provider ID list fan-out | `RoutingNotifierTest`; route list의 모든 provider를 순서대로 호출 |
| provider failure | 모든 provider를 중앙 `FailOpenNotificationProvider`로 감싸고 예외를 삼킴 | global fail-open이며 application kind별 정책이 아님 |
| unbound route | `AdapterDisabledException` fail-fast | `NotificationAdapterTest`; disabled sentinel은 없음 |
| email seam | `google-email` + `GoogleEmailClient` interface | module 안 production client/SDK/credential/protocol 구현 0 |
| Slack seam | `slack-webhook` + `SlackClient` interface | module 안 production client/SDK/credential/protocol 구현 0 |
| configuration | code는 `app.notification.routes.*`와 provider별 `*.enabled`를 읽음 | checked-in `application.yml`/env registry의 `app.notification.{slack,email}.provider` selector와 drift |
| production consumer | feature/application production consumer 0 | main source에는 application contract 선언, adapter 구현과 bootstrap composition만 존재 |
| evidence grade | local fake/contract baseline | real provider, durability, callback, security, load evidence 0; 모든 seam `R0 legacy` |
selector drift는 이 기준선의 일부다. Task 1에서는 고치지 않는다. canonical graph가 준비되고
cutover evidence가 생기기 전까지 기존 key를 새 의미로 재사용하거나 legacy class에 production
동작을 추가하지 않는다.
## 모듈 개요
@@ -32,3 +53,26 @@ client 는 포킹 프로젝트가 채우는 seam 이다.
`channel()`+`providerId()` 로 키잉된 `NotificationProvider` 빈으로 기여한다(예:
`GoogleEmailProvider`, `SlackWebhookProvider`). `GoogleEmailClient`/`SlackClient` 는 포크가
구현하는 seam 이며 실패는 데코레이터가 fail-open 처리한다.
## Wave G deletion inventory
다음 surface는 canonical-only cutover와 retained evidence 검증이 끝난 뒤 한 묶음으로 제거한다.
그 전에는 동작을 확장하지 않고 R0 회귀 기준선으로만 유지한다.
- application R0 contract:
`Channel`, `Notification`, raw `NotificationPort`
- router/decorator SPI:
`NotificationConfig`, `NotificationRoutesSettings`, `RoutingNotifier`,
`NotificationProvider`, `FailOpenNotificationProvider`
- fake-only Google email seam:
`GoogleEmailClient`, `GoogleEmailProvider`, `GoogleEmailNotificationAdapterConfig`
- fake-only Slack webhook seam:
`SlackClient`, `SlackWebhookProvider`, `SlackNotificationAdapterConfig`
- legacy configuration/tests:
`app.notification.routes.*`, `app.notification.google-email.enabled`,
`app.notification.slack-webhook.enabled`, drifted
`APP_NOTIFICATION_EMAIL_PROVIDER`/`APP_NOTIFICATION_SLACK_PROVIDER`,
`NotificationAdapterTest`, `RoutingNotifierTest`와 bootstrap legacy gating cases
accepted 또는 indeterminate work를 inventory하지 않은 상태에서 이 목록을 삭제하거나 canonical
provider로 자동 재전송하지 않는다.