Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/015-adapter-outbound-support-inventory.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

131 lines
6.3 KiB
Plaintext

Command: python3 - <<'PY'
import json
from pathlib import Path
root=Path('src/adapter/outbound/support')
for p in sorted(x for x in root.rglob('*') if x.is_file() and 'build/' not in str(x)):
print(p)
print('--- counts ---')
for label,base,pattern in [('prod-java',root/'src/main/java','*.java'),('test-java',root/'src/test/java','*.java')]:
files=list(base.rglob(pattern)) if base.exists() else []
print(label,'files=',len(files),'loc=',sum(len(p.read_text(errors='ignore').splitlines()) for p in files))
r=json.load(open('src/config/architecture/modules.json'))['modules']
print('--- registry ---')
print(json.dumps(next(m for m in r if m['id']=='adapter-outbound-support'),indent=2))
PY
printf '%s\n' '--- build.gradle ---'; cat src/adapter/outbound/support/build.gradle
printf '%s\n' '--- README.md ---'; cat src/adapter/outbound/support/README.md
printf '%s\n' '--- CLAUDE.md ---'; cat src/adapter/outbound/support/CLAUDE.md
Working directory: /shared/codebase/clean-architecture-backend-template
Executed at: 2026-08-29T07:19:26Z
Source revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
Observation boundary: Enumerates the support leaf source/build/document files and registry entry at the recorded revision; downstream consumers are not measured here.
--- stdout/stderr ---
src/adapter/outbound/support/CLAUDE.md
src/adapter/outbound/support/README.md
src/adapter/outbound/support/build.gradle
src/adapter/outbound/support/gradle.lockfile
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/package-info.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/FailOpenDependencyLogger.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/OutboundCorrelation.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/OutboundSupportConfig.java
src/adapter/outbound/support/src/test/java/dev/caskeleton/adapter/outbound/support/FailOpenDependencyLoggerTest.java
--- counts ---
prod-java files= 4 loc= 97
test-java files= 1 loc= 89
--- registry ---
{
"id": "adapter-outbound-support",
"gradle_path": ":adapter:outbound:support",
"source_path": "src/adapter/outbound/support",
"allowed_dependencies": [
"domain-core",
"application-core",
"shared-contract"
],
"runtime_memberships": [
"app-bootstrap"
]
}
--- build.gradle ---
// Shared base for outbound integration adapters: correlation, fail-open dependency
// logging, and the @Configuration seam. Depended on by messaging/cache/notification/httpclient.
dependencies {
implementation 'org.springframework.boot:spring-boot-autoconfigure'
implementation 'org.slf4j:slf4j-api'
}
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}
--- README.md ---
# adapter:outbound:support — 설계 결정 참조
아웃바운드 기술 어댑터(`:adapter:outbound:messaging`, `:adapter:outbound:cache-redis`,
`:adapter:outbound:notification`, `:adapter:outbound:httpclient`)가 공유하는 베이스 모듈. 패키지
루트: `dev.caskeleton.adapter.outbound.support`.
허용/금지 의존 정책은 `src/build.gradle` 의
`allowedProjectDependencies['adapter:outbound:support']` 항목이 SSOT 다(이 모듈은 아직 별도
CLAUDE.md 를 두지 않았다). 이 문서는 코드 주석에서 덜어낸 **설계 결정의 근거**를 모아둔 참조용
기록이다 — 코드를 읽다 "왜 이렇게 했나"가 궁금할 때 본다.
## 왜 별도 모듈인가
옛 `adapter-outbound` 모듈을 기술별(`messaging`/`cache-redis`/`notification`/`httpclient`)로
쪼개면서, 네 모듈이 공통으로 필요로 하는 조각 — correlation 조회, fail-open 의존성 로깅,
공유 `@Configuration` 등록 지점 — 을 이 모듈로 뽑아냈다. 네 기술 모듈 모두 `implementation
project(':adapter:outbound:support')` 로 이 모듈에 의존한다.
## FailOpenDependencyLogger — WARN, ERROR 아님
실패를 ERROR 가 아니라 WARN 으로 로깅한다: cache/messaging/notification 선택형 어댑터는
fail-open 이라 의존성 실패가 나도 use case 는 성공한 것이다 — 관측은 하되 escalate 하지 않는다.
이는 호출자에게 직접 노출되는 hard failure 를 ERROR 로 올리는 `httpclient` 모듈의
`OutboundHttpDependencyLogger` 와 명확히 구분된다. 메서드 시그니처가 본문·수신자·페이로드를
받지 않아 PII 가 로그에 닿지 않는다.
## OutboundCorrelation — `"unknown"` 센티넬
MDC 에 correlation id 가 없으면 `null` 이 아니라 `"unknown"` 문자열을 반환해 로그 라인에 필드가
비지 않게 한다. MDC 키는 SSOT(`mdc-keys.yaml`)를 따른다.
## OutboundSupportConfig — 공유 `@Configuration` seam
`FailOpenDependencyLogger` 빈을 `@ConditionalOnMissingBean` 으로 무조건 등록하는 단일
`@Configuration` 이다. 네 기술 모듈이 각자 로거 빈을 중복 등록하지 않고 이 모듈에만 의존하면
와이어링되며, 포크는 같은 타입의 빈을 직접 등록해 오버라이드할 수 있다. 이 모듈 자체는 어떤
`@ConditionalOnProperty` 게이팅도 갖지 않는다 — 게이팅은 각 기술 모듈이 자신의 연동 client
단위로 소유한다.
--- CLAUDE.md ---
# adapter:outbound:support — shared outbound support
## Registered identity
- Module ID: `adapter-outbound-support`
- Gradle path: `:adapter:outbound:support`
- Focused test (derived from Gradle path): `./gradlew :adapter:outbound:support:test --console=plain`
- Runtime baseline: Java 21; repository framework baseline: Spring Boot 4.0.0.
- Registry SSOT: `src/config/architecture/modules.json`.
Package roots: `dev.caskeleton.adapter.outbound` and
`dev.caskeleton.adapter.outbound.support`.
## Responsibility
- Own reusable outbound correlation, fail-open dependency logging, and auto-configuration seams.
- Provide technical support used by registered messaging/cache/notification/httpclient leaves.
- Keep feature-specific clients and routing policies in their owning leaf.
## Boundaries
- Allowed project dependencies are registry-owned: application, domain, and shared contracts.
- No inbound transport, persistence, bootstrap, or sample dependencies.
- No business rules or external-system-specific protocol implementation.
## Tests
Use focused unit tests for support behavior. Do not start a Spring application context unless the
contract being tested is configuration wiring itself.
Exit code: 0