Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/declaration-and-document-drift/case/case-a05-f019-ssot.md
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

142 lines
7.9 KiB
Markdown

---
kind: CASE
slug: a05-f019-ssot
title: 정본이 어디인지 주석에 적어 두고 정의는 다시 타이핑한다
topic: declaration-and-document-drift
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: case:a05-f019-ssot
evidenceCapturedOn: 2026-09-02
assets:
- key: a05-f019-ssot
file: ../../../final/evidence/rendered/a05-f019-ssot.svg
evidence:
- ../../../final/evidence/raw/a05-f019-ssot.txt
source:
- 원본 분석 절은 `analysis/05-adapter-outbound-persistence-jpa.md` §51 이다. 두 목록의 이름과 차이가 `postgresql` 과 `h2` 라는 것이 §51.1 과 §51.2 에, 리프 목록이 바깥 소비자를 스캔하지 않는다는 것이 §51.3 에 있다. 같은 문서 §55 의 backlog 가 이것을 P2/P3 아키텍처 거버넌스 강화로 분류한다.
- 사본의 javadoc 이 정본이 어디인지 적어 두고도 그 값을 코드로 읽지 않는다는 것은 여기서 확인했다.
---
# 정본이 어디인지 주석에 적어 두고 정의는 다시 타이핑한다
리프가 내보내는 패키지 목록을 선언하고, 컴포지션 루트의 소비자 규칙이 같은 목록을 자기 안에 다시 적는다. 사본의 javadoc 은 정본이 리프 쪽이라고 이름으로 적지만, 그 이름을 코드로 읽는 곳은 없다. 두 목록은 이미 두 항목 다르다.
## 관계
- **문서의 수치는 세지 말고 파생하거나 게이트로 붙든다**
문서의 수치를 세는 대신 파생하거나 게이트로 붙들라는 규칙이다.
- **빠뜨림이 통과가 되는 게이트는 게이트가 아니다**
한쪽에 항목을 더해도 다른 쪽이 조용한 형태가 같다.
- **같은 개념의 두 어휘가 공존하면 하나를 죽은 것으로 표시한다**
같은 값을 두 곳이 각자 적고 있어 어느 쪽이 정본인지 정해야 한다.
## 문제
이 리프에는 jar 하나에 공개 구현 타입이 많이 들어 있다. 그래서 자바 접근 제어와 아키텍처 노출 목록을 각각 따로 둔다.
내보낼 패키지 목록을 두 곳이 각각 들고 있다.
## 결론
리프의 경계 시험 쪽에는 열 개가 선언돼 있다. springdata 와 querydsl 은 거기 없다.
컴포지션 루트의 아키텍처 시험이 같은 열 개를 자기 안에 다시 적고, 벤더 진입점 둘을 더 넣는다. postgresql 과 h2 다. 이유는 그 자리 주석에 적혀 있다. 벤더 설정이 코어 JPA 설정을 임포트하는 방향이라 컴포지션 루트가 대신 막을 단일 내부 진입점이 없고, 방향을 뒤집으면 패키지 순환이 생겼다는 것이다.
사본의 javadoc 에는 이 목록이 리프의 export 허용 목록을 옮겨 적은 것이고 정의는 리프의 경계 시험에 있다고 적혀 있다.
그 문장이 컴포지션 루트에서 그 클래스를 언급하는 유일한 줄이다. 선언 파일 밖에서 그 목록 상수를 참조하는 자바 코드는 저장소 전체에 0 이다. 어느 쪽이 정본인지는 산문이 말하고, 값은 사람이 옮겨 적는다.
리프 목록은 바깥 소비자를 검사하지도 않는다. 그 목록을 쓰는 시험 둘은 목록에 적힌 패키지가 실제로 있는지, 그 패키지가 카탈로그의 거버넌스 대상인지를 본다. 임포트 관계는 컴포지션 루트 쪽 규칙이 따로 본다.
그래서 두 시험 모두 통과한다. 통과는 각자의 규칙을 만족한다는 뜻이고, 두 목록이 같다는 뜻은 아니다. 지금 이미 두 항목 다르다.
권고는 등록부를 한 곳에 두고 두 검사가 같은 데이터를 보게 하라는 것이다.
## 검증 환경
OpenJDK : 21.0.12
확인 방식 : 두 목록의 항목 추출과 집합 대조, 정본 참조 계수, 리프 시험의 검사 범위 확인
소스 수정 : x
실행 : 없음. 정적 검색과 집합 연산이다.
## 재현 조건
1. 리프 경계 시험의 export 목록을 읽는다.
2. 컴포지션 루트의 소비자 규칙 안에 있는 같은 이름의 목록을 읽는다.
3. 두 집합을 뽑아 차집합을 구한다.
4. 사본의 javadoc 이 정본을 어떻게 지목하는지 읽는다.
5. 컴포지션 루트에서 그 정본 클래스를 언급하는 줄과, 선언 파일 밖에서 그 상수를 참조하는 코드를 각각 센다.
6. 리프 목록을 쓰는 시험 둘이 무엇을 확인하는지 읽는다.
## 본문
<!-- body:start -->
이 리프는 하나의 jar 안에 공개 구현 타입이 많다. 그래서 자바의 `public` 과 아키텍처가 내보내는 패키지를 따로 관리한다.
내보내는 패키지 목록이 두 곳에 있다.
## 두 목록과 그 차이
:::evidence key="a05-f019-ssot" alt="리프 경계 시험이 선언하는 export 패키지 목록, 컴포지션 루트의 소비자 규칙 안에 다시 적힌 같은 목록과 거기 더해진 벤더 진입점 둘과 그 이유 주석, 두 집합의 크기와 차집합, 사본의 javadoc 이 정본을 지목하는 줄과 그 정본 클래스를 언급하는 줄 수와 선언 파일 밖의 상수 참조 수, 그리고 리프 목록을 쓰는 두 시험이 무엇을 보는지를 출력한 터미널 기록." caption="리프 10개와 루트 12개, 차이는 postgresql 과 h2 · 사본 javadoc 이 정본을 어디라고 적는지 · 그 클래스 언급 1줄은 그 주석뿐 · 상수 참조 0 · 리프 시험 둘은 목록의 자기 정합만 확인 — 65줄 · exit 0" zoom="true"
:::
리프의 경계 시험이 열 개를 선언한다.
```java
private static final Set<String> EXPORTED_PACKAGES =
Set.of(
"api", "notification.configuration", "transaction", "security",
"observation", "migration", "hibernate", "fileserver", "failure", "config");
```
컴포지션 루트의 아키텍처 시험은 같은 열 개에 둘을 더 적는다. 그 자리의 주석이 이유를 적는다.
```text
The two vendor entry points. A vendor configuration imports the core JPA config rather
than the reverse, so there is no single internal entry the composition root could gate
instead — inverting the import to make one produced a package cycle.
```
집합으로 빼면 차이가 정확히 둘이다.
```text
리프 10개 / 루트 12개
루트에만 있는 것: ['h2', 'postgresql']
리프에만 있는 것: []
```
## 사본이 정본을 지목하는 방식
```text
The list is the leaf's export allowlist, mirrored here because this is the consumer side
of the same boundary. JpaModuleBoundaryTest owns the definition.
```
그 문장이 컴포지션 루트에서 리프 경계 시험을 언급하는 유일한 줄이다. 선언 파일 밖에서 `EXPORTED_PACKAGES` 를 참조하는 자바 코드는 저장소 전체에 0 이다.
정본을 지목하는 것은 산문이고, 값은 손으로 옮겨져 있다.
## 리프 목록은 바깥 소비자를 보지 않는다
그 목록을 쓰는 시험은 둘이다. 목록에 적힌 패키지가 소스 트리에 실제로 있는지, 그리고 그 패키지가 카탈로그의 거버넌스 대상인지를 본다.
누가 무엇을 임포트하는지는 컴포지션 루트의 규칙이 따로 본다. split 은 실수가 아니라 역할 분리의 결과이고, 그래서 어느 쪽도 상대를 검사할 이유가 없다.
## 두 시험이 각각 무엇을 보는가
두 시험의 입력이 겹치지 않는다. 리프 시험은 리프의 소스 트리와 자기 카탈로그만, 루트 시험은 루트의 임포트 그래프와 자기 목록만 읽는다.
한쪽 목록이 늘어도 다른 쪽 단언의 입력은 그대로다. 실패할 근거가 없다.
## 고칠 방향
내보내는 패키지 등록부를 한 곳으로 옮기고, 리프의 패키지 그래프 검사와 소비자 규칙이 같은 데이터를 읽게 한다. 분석 문서의 권고가 그것이다.
## 확인하지 못한 것
한쪽 목록에 항목을 더해 다른 쪽이 조용한지 실행으로 확인하지 않았다. 두 선언을 대조하고 참조를 센 것까지가 확인 범위다.
<!-- body:end -->