Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/099-experimental-structural-optin-gap.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

78 lines
4.0 KiB
Plaintext

revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
=== stable scan includes experimental ===
7- "dev.caskeleton.adapter.outbound.persistence.cache",
8- "dev.caskeleton.adapter.outbound.persistence.envers",
9: "dev.caskeleton.adapter.outbound.persistence.experimental",
10- "dev.caskeleton.adapter.outbound.persistence.failure",
11- "dev.caskeleton.adapter.outbound.persistence.hibernate",
--
30- "dev.caskeleton.adapter.outbound.persistence.cache",
31- "dev.caskeleton.adapter.outbound.persistence.envers",
32: "dev.caskeleton.adapter.outbound.persistence.experimental",
33- "dev.caskeleton.adapter.outbound.persistence.failure",
34- "dev.caskeleton.adapter.outbound.persistence.hibernate",
=== opt-in exemptions known by scan coverage test ===
/** Each opt-in capability: its package, its own configuration, and the switch that gates it. */
private record OptInCapability(String packageName, Class<?> configuration, String switchPrefix) {}
private static final List<OptInCapability> OPT_IN =
List.of(
new OptInCapability(
PersistenceJpaConfig.OPT_IN_NOTIFICATION_PACKAGE,
NotificationJpaPersistenceConfig.class,
"ca-skeleton.notification.platform"),
new OptInCapability(
PersistenceJpaConfig.OPT_IN_FILESERVER_PACKAGE,
FileserverJpaPersistenceConfig.class,
"app.fileserver-platform"));
/** Sub-packages that hold no JPA entity or repository and therefore need no scan. */
private static final Set<String> NOT_SCANNED =
Set.of(
// Vendor-selection configuration only.
"config",
"h2",
// The opt-in capabilities, each scanned by its own gated configuration instead.
"notification",
"fileserver");
=== current experimental JPA stereotypes ===
=== architecture rule checks type dependency, not scan strings ===
"the testkit carries ArchUnit and Testcontainers; a production edge would put both on"
+ " every deployment's runtime classpath")
.check(PRODUCTION_CLASSES);
}
@Test
@DisplayName("the Stable platform does not depend on experimental packages")
void stablePlatformDoesNotDependOnExperimentalPackages() {
noClasses()
.that()
.resideOutsideOfPackage(ROOT + ".experimental..")
.should()
.dependOnClassesThat()
.resideInAPackage(ROOT + ".experimental..")
.as("experimental capabilities are never reachable from Stable code (plan §3)")
.because(
"an experimental capability may change or be withdrawn; a Stable type referencing one"
+ " gives it a Stable contract nobody agreed to")
.check(PRODUCTION_CLASSES);
}
@Test
=== plan constraint ===
**Architecture:** Experimental module은 Stable `jpa-core-api` 계약만 소비하며 Stable starter에 자동 포함되지 않는다. 각 기능은 명시적 feature flag와 별도 compatibility/failure suite를 요구한다. 실험 결과가 Stable 의미론과 충돌하면 Core를 왜곡하지 않고 capability 또는 별도 profile로 유지한다.
**Tech Stack:** Stable 계획의 Java 21·Spring Boot 4.1·PostgreSQL Testcontainers 기반, PostgreSQL RLS, AbstractRoutingDataSource, tenant-specific DataSource registry, Jakarta Persistence 4.0 preview/final compatibility lane, Hibernate ORM 8 compatibility lane, PostgreSQL 19 compatibility lane.
## Global Constraints
- Stable 계획 Task 1~53이 완료되고 Release Gate가 통과한 뒤 시작한다.
- 모듈 루트는 `modules/jpa-experimental`이다.
- Experimental module은 `jpa-spring-boot-starter`의 기본 dependency가 아니다.
- 모든 기능은 `backend.jpa.experimental.*` feature flag를 요구한다.
- Tenant ID와 consistency token은 metric label에 기록하지 않는다.
- Tenant context 누락은 fail-closed다.
- `readOnly=true`만으로 replica routing하지 않는다.
- Lock query, write transaction, read-after-write pin은 primary를 사용한다.