Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/257-messaging-msg015-two-kafka-stacks.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

67 lines
4.2 KiB
Plaintext

# evidence 257 — messaging-msg015-two-kafka-stacks
# revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
# cwd: /shared/codebase/clean-architecture-backend-template/src/messaging
# command: echo ---app-bootstrap seam---; sed -n "36,40p" ../app-bootstrap/src/main/java/dev/caskeleton/bootstrap/messaging/KafkaSenderConfig.java; sed -n "58,66p" ../app-bootstrap/src/main/java/dev/caskeleton/bootstrap/messaging/KafkaSenderConfig.java; echo; echo ---platform producer---; sed -n "124,136p" messaging-spring-boot-starter/src/main/java/dev/caskeleton/messaging/autoconfigure/KafkaMessagingAutoConfiguration.java; echo; echo ---root master switch---; grep -n "ConditionalOnProperty" messaging-spring-boot-starter/src/main/java/dev/caskeleton/messaging/autoconfigure/MessagingPlatformRootAutoConfiguration.java; echo; echo ---CLAUDE.md MSG-015---; grep -n -B2 -A10 "MSG-015" CLAUDE.md
# ---- raw output ----
---app-bootstrap seam---
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "app.messaging.broker", havingValue = "kafka")
public class KafkaSenderConfig {
* @param settings the broker addresses, already validated as non-empty when Kafka is selected
* @return the producer, closed on context shutdown
*/
@Bean(name = "kafkaSeamProducer", destroyMethod = "close")
@ConditionalOnMissingBean(name = "kafkaSeamProducer")
public Producer<String, String> kafkaSeamProducer(KafkaAdapterSettings settings) {
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, String.join(",", settings.brokers()));
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
---platform producer---
*
* @param environment the resolved environment
* @return the producer, closed with the context
*/
@Bean(destroyMethod = "close")
@ConditionalOnMissingBean
public org.apache.kafka.clients.producer.Producer<byte[], byte[]> messagingKafkaProducer(
org.springframework.core.env.Environment environment) {
String bootstrapServers = environment.getProperty("spring.kafka.bootstrap-servers", "").trim();
if (bootstrapServers.isEmpty()) {
throw new IllegalStateException(
"app.messaging.broker=kafka needs spring.kafka.bootstrap-servers; a producer with no "
+ "broker address defaults to localhost:9092 and fails at the first publish rather "
---root master switch---
4:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
28:@ConditionalOnProperty(prefix = MessagingSettings.PREFIX, name = "enabled", havingValue = "true")
---CLAUDE.md MSG-015---
68- 증명되었는가", membership은 "무엇이 실행되는가"다.
69-
70:## application이 이 family에 도달하는 경로 (MSG-015, 미해결)
71-
72-리뷰(`docs/reviews/2026-08-14-messaging-module-code-review.md` §6.1·§6.3)가 정한 흐름은
73-`application-owned port → platform anti-corruption bridge → platform`이고, §6.2는 그 bridge의 자리를
74-`adapter/outbound/messaging/platformbridge/`로 지정한다. §6.3 첫 규칙: **application은 신규
75-broker/runtime 타입을 직접 보지 않고 자기 port만 소유한다.**
76-
77-**현재 그 bridge가 없다.** `adapter/outbound/messaging`은 platform에 의존하지 않고(registry의
78-`allowed_dependencies` 참조), `app-bootstrap`이 starter를 직접 문다. 그래서 서로를 모르는 messaging
79-스택 두 개가 한 아티팩트에 있고, 그것이 `kafkaSeamProducer`/`messagingKafkaProducer` bean 이름 충돌로
80-드러났다.
81-
82:이것이 리뷰의 **MSG-015 (P0)** 이며 닫히지 않았다. 지금 안전한 이유는 설계가 아니라 기본값이다 —
83-`app.messaging.enabled=false`. 이 스위치를 켜는 배포는 port를 거치지 않는 경로로 platform을 쓰게 된다.
84-bridge 구현은 런타임 배선 변경이라 리뷰가 말한 대로 별도 change set이다.
85-
86-## Stable 승격에 필요한 증거
87-
88-adapter를 Experimental → Stable로 올릴 때 필요한 것 (`docs/messaging/support-matrix.md`가 현재
89-상태를 기록한다):
90-
91-1. 실제 broker container를 쓰는 fault 시나리오 lane이 존재하고, 그 lane이
92- `BrokerCertificationEvidence`를 산출한다.