Files
document-haness/docs/clean-architecture-backend-template/analysis/00-project-overview.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
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Project Overview
> **이 문서는 분석 시작 시점의 sizing 스냅샷이다.** 최종 결과는 `final/document.md`,
> 교차 스코프 종합은 `analysis/99-cross-scope.md`, 모듈별 확정 수치는 각 `analysis/NN-*.md`의
> 커버리지 원장이 정본이다. 아래 파일/LOC 표와 bounded scope 목록은 갱신하지 않는다 —
> 스냅샷으로서의 값이 그 정확성이기 때문이다.
## 분석 기준 revision
- repository: `/shared/codebase/clean-architecture-backend-template`
- 최초 기준선: `a24ece9cf797f7ea647e33bf846b115208ed1ba5` (모듈 01~19)
- **최종 기준**: `21234e38cdb9a926cbc92bb97a2aee2e4a7d2916` — 분석 도중 커밋
`21234e38`("feat: grpc 기능 deep 구현", 2026-08-31)이 gRPC 가족 18 leaf를 추가했다.
`git diff a24ece9c..HEAD` = 400 files / +40,217 / 4이고 변경 경로가 `src/grpc*` ·
`modules.json` · `src/build.gradle` · docs 15개뿐이어서 모듈 01~19는 영향받지 않는다
(`analysis/20-grpc-platform.md` §0, `analysis/99-cross-scope.md` §8).
- working tree: clean
## 최종 커버리지
| | |
|---|---|
| 등록 leaf (최종) | **62** (최초 스냅샷 시점 44) |
| COMPLETE | **61** |
| EXCLUDED | 1 — `sample-portfolio` (사용자 지시 2026-08-30) |
| PENDING | 0 |
| 모듈 문서 | `analysis/01``20` (20개). messaging 25 leaf는 `19`, gRPC 18 leaf는 `20`으로 통합 |
| 교차 스코프 | `analysis/99-cross-scope.md` |
| 최종 문서 | `final/document.md` |
**분석 단위와 문서 단위가 1:1이 아니다.** 아래 "분석할 bounded scopes" 표는 leaf마다 문서 하나를
계획했으나, `messaging:*` 25개와 `grpc:*`·`grpc-advanced:*` 18개는 leaf 경계를 넘는 계약
(capability 선언 → profile validator → 인증 증거 → 지원 문서)이 실제 설계 단위여서 각각 한 문서로
통합했다. `state.json``scopes`가 leaf 단위 정본이고, 각 항목의 `analysisFile`이 담당 문서를 가리킨다.
## Build and module map
- Gradle 9.0.0 wrapper 기반 멀티모듈 build이며 `src/settings.gradle`이 included build `build-logic``ca.architecture-registry` settings plugin을 적용한다.
- `src/config/architecture/modules.json`이 leaf 존재/경로/허용 project dependency/runtime membership의 SSOT다.
- registry leaf 수: 이 스냅샷 시점 **44**, 최종 **62**. 숫자는 측정치이며 문서 규칙의 별도 SSOT로 취급하지 않는다 — `modules.json`이 SSOT다.
| module | Gradle path | production files/LOC* | test files/LOC* | runtime membership | status |
|---|---|---:|---:|---|---|
| `domain-core` | `:domain-core` | 8 / 107 | 0 / 0 | app-bootstrap, sample-portfolio | COMPLETE |
| `shared-contract` | `:shared-contract` | 56 / 2839 | 23 / 2706 | app-bootstrap, sample-portfolio | PENDING |
| `application-core` | `:application-core` | 886 / 35798 | 136 / 19751 | app-bootstrap, sample-portfolio | PENDING |
| `adapter-outbound-support` | `:adapter:outbound:support` | 5 / 107 | 1 / 89 | app-bootstrap | PENDING |
| `adapter-outbound-persistence-jpa` | `:adapter:outbound:persistence-jpa` | 460 / 44594 | 141 / 12581 | app-bootstrap, sample-portfolio | PENDING |
| `adapter-outbound-persistence-mongo` | `:adapter:outbound:persistence-mongo` | 353 / 23401 | 139 / 15416 | app-bootstrap | PENDING |
| `adapter-outbound-identifier` | `:adapter:outbound:identifier` | 5 / 134 | 2 / 151 | app-bootstrap, sample-portfolio | PENDING |
| `adapter-outbound-fileserver` | `:adapter:outbound:fileserver` | 79 / 12723 | 37 / 12043 | app-bootstrap | PENDING |
| `adapter-outbound-objectstorage` | `:adapter:outbound:objectstorage` | 154 / 14966 | 49 / 6778 | sample-portfolio | PENDING |
| `adapter-outbound-cache-redis` | `:adapter:outbound:cache-redis` | 316 / 33690 | 70 / 15245 | app-bootstrap | PENDING |
| `adapter-outbound-httpclient` | `:adapter:outbound:httpclient` | 270 / 15887 | 97 / 8803 | app-bootstrap | PENDING |
| `adapter-outbound-messaging` | `:adapter:outbound:messaging` | 35 / 4350 | 19 / 3670 | app-bootstrap | PENDING |
| `adapter-outbound-notification` | `:adapter:outbound:notification` | 172 / 14749 | 61 / 10037 | app-bootstrap | PENDING |
| `adapter-inbound-web` | `:adapter:inbound:web` | 428 / 29488 | 202 / 23500 | app-bootstrap, sample-portfolio | PENDING |
| `adapter-inbound-grpc` | `:adapter:inbound:grpc` | 9 / 650 | 6 / 782 | none | PENDING |
| `adapter-inbound-graphql` | `:adapter:inbound:graphql` | 409 / 26477 | 117 / 14745 | app-bootstrap | PENDING |
| `adapter-inbound-websocket` | `:adapter:inbound:websocket` | 174 / 13426 | 74 / 10336 | none | PENDING |
| `app-bootstrap` | `:app-bootstrap` | 160 / 15096 | 282 / 32724 | app-bootstrap | PENDING |
| `sample-portfolio` | `:sample-portfolio` | 210 / 10429 | 63 / 7020 | sample-portfolio | PENDING |
| `messaging-core-api` | `:messaging:messaging-core-api` | 86 / 3952 | 8 / 934 | app-bootstrap | PENDING |
| `messaging-schema-api` | `:messaging:messaging-schema-api` | 11 / 635 | 3 / 263 | app-bootstrap | PENDING |
| `messaging-schema-json` | `:messaging:messaging-schema-json` | 2 / 234 | 3 / 373 | app-bootstrap | PENDING |
| `messaging-schema-avro` | `:messaging:messaging-schema-avro` | 3 / 356 | 4 / 463 | none | PENDING |
| `messaging-schema-protobuf` | `:messaging:messaging-schema-protobuf` | 3 / 209 | 2 / 278 | none | PENDING |
| `messaging-cloudevents` | `:messaging:messaging-cloudevents` | 4 / 243 | 1 / 162 | app-bootstrap | PENDING |
| `messaging-policy` | `:messaging:messaging-policy` | 27 / 1744 | 4 / 1236 | app-bootstrap | PENDING |
| `messaging-transport-spi` | `:messaging:messaging-transport-spi` | 14 / 783 | 4 / 586 | app-bootstrap | PENDING |
| `messaging-runtime-core` | `:messaging:messaging-runtime-core` | 7 / 804 | 4 / 866 | app-bootstrap | PENDING |
| `messaging-observability` | `:messaging:messaging-observability` | 10 / 847 | 6 / 827 | app-bootstrap | PENDING |
| `messaging-security` | `:messaging:messaging-security` | 13 / 959 | 3 / 475 | app-bootstrap | PENDING |
| `messaging-kafka` | `:messaging:messaging-kafka` | 36 / 3711 | 24 / 4087 | app-bootstrap | PENDING |
| `messaging-kafka-share-experimental` | `:messaging:messaging-kafka-share-experimental` | 5 / 200 | 1 / 112 | none | PENDING |
| `messaging-rabbit` | `:messaging:messaging-rabbit` | 22 / 2624 | 10 / 1727 | app-bootstrap | PENDING |
| `messaging-reliability-api` | `:messaging:messaging-reliability-api` | 14 / 822 | 0 / 0 | app-bootstrap | PENDING |
| `messaging-outbox-jdbc-postgresql` | `:messaging:messaging-outbox-jdbc-postgresql` | 19 / 2525 | 8 / 2140 | app-bootstrap | PENDING |
| `messaging-inbox-jdbc-postgresql` | `:messaging:messaging-inbox-jdbc-postgresql` | 8 / 576 | 4 / 607 | app-bootstrap | PENDING |
| `messaging-claim-check` | `:messaging:messaging-claim-check` | 7 / 424 | 3 / 333 | app-bootstrap | PENDING |
| `messaging-admin-api` | `:messaging:messaging-admin-api` | 26 / 1619 | 1 / 147 | app-bootstrap | PENDING |
| `messaging-admin-runtime` | `:messaging:messaging-admin-runtime` | 13 / 1263 | 6 / 1051 | app-bootstrap | PENDING |
| `messaging-pulsar-experimental` | `:messaging:messaging-pulsar-experimental` | 9 / 676 | 2 / 414 | none | PENDING |
| `messaging-nats-experimental` | `:messaging:messaging-nats-experimental` | 8 / 768 | 2 / 460 | none | PENDING |
| `messaging-spring-cloud-stream-bridge` | `:messaging:messaging-spring-cloud-stream-bridge` | 7 / 516 | 2 / 295 | none | PENDING |
| `messaging-spring-boot-starter` | `:messaging:messaging-spring-boot-starter` | 29 / 3590 | 12 / 2384 | app-bootstrap | PENDING |
| `messaging-testkit` | `:messaging:messaging-testkit` | 15 / 1246 | 6 / 828 | none | PENDING |
*파일/LOC 측정은 build output을 제외하고 Java/Kotlin/Groovy/proto/avsc/sql/yaml/properties/json 및 module build.gradle 계열을 대상으로 한 초기 scope sizing 값이다. 이후 각 bounded scope의 coverage denominator는 해당 분석에서 다시 확정한다.
## Dependency direction
- registry상 `domain-core`의 허용 project dependency는 0개다.
- `application-core``domain-core`, `shared-contract`를 허용하며, adapter/runtime leaf들은 registry에 명시된 방향으로만 project edge를 가질 수 있다.
- root `verifyCleanArchitectureDependencies`는 실제 `api/implementation/compileOnly/runtimeOnly` ProjectDependency 집합과 registry allowlist를 비교한다.
## Runtime entry points
- registry가 인식하는 runtime composition은 `app-bootstrap`, `sample-portfolio` 두 개다.
- `domain-core`는 두 composition 모두의 membership에 포함된다. 이는 런타임 closure 포함 계약이며, `domain-core` 자체가 framework entry point나 Spring bean을 가진다는 뜻은 아니다.
- 각 composition의 실제 wiring/conditional activation은 해당 bounded scope에서 추가 추적한다.
## Persistence / messaging / external systems
- 이 overview에서는 registry와 top-level structure만 확정했다. persistence, messaging, cache, object storage, HTTP client 등은 독립 leaf가 존재하며 세부 동작은 아직 분석하지 않았다.
## Test topology
- 각 leaf의 테스트 소스 수를 초기 계수했다. `domain-core` 자체에는 Java test가 없고 `.gitkeep`만 존재한다.
- 도메인 모델링 규칙은 `app-bootstrap``CleanArchitectureTest`가 production classes 전체를 대상으로 cross-module ArchUnit 검증한다.
## Configuration and operational surfaces
- top-level Docker Compose variants, `infra/`, runtime configuration이 존재하지만 아직 bounded analysis 전이다.
## 분석할 bounded scopes (계획 — 실제 문서 배치는 위 "최종 커버리지" 참조)
아래는 분석 시작 시점의 계획이며, 실제 산출물은 다음과 같이 통합됐다:
| 계획 | 실제 |
|---|---|
| leaf 1개 = 문서 1개 (44개 문서) | 20개 문서 |
| `analysis/19-sample-portfolio.md` | **EXCLUDED** (사용자 지시) |
| `analysis/20``44`: messaging leaf 25개 각각 | **`analysis/19-messaging-platform.md`** 하나로 통합 |
| (계획에 없음 — 분석 도중 추가된 가족) | **`analysis/20-grpc-platform.md`** — grpc 18 leaf |
계획 표 원본은 git 이력에 남아 있다.
## 아직 단정하지 않는 것 (분석 시작 시점의 목록)
아래는 이 개요를 쓴 시점의 미결 목록이다. 각 항목의 현재 상태를 병기한다.
- 각 adapter의 실제 runtime activation 조건과 external system behavior
**해소**. 각 모듈 문서 §조립/활성화 절 및 `99-cross-scope.md` §2.
- 각 messaging leaf의 production reachability와 experimental/stable 경계
**해소**. `19-messaging-platform.md` §1.1(출하 18 / build-only 7)·§10.1.
- persistence migration ownership과 provider-specific guarantees
**부분 해소**. `05`·`06`·`19` §7.2가 소유권과 미적용 스트림을 확정했으나,
컨테이너가 필요한 마이그레이션 IT는 실행하지 않았다(`99-cross-scope.md` §10.1).
- project 전체가 모든 문서상의 architectural claim을 만족한다는 결론
**부정**. 만족하지 않는 지점이 `99-cross-scope.md` §2~§5에 형태별로 정리돼 있다.