refactor: 문서 개선 중
This commit is contained in:
+5
-5
@@ -47,7 +47,7 @@ mongo 플랫폼 자동설정에서 트랜잭션 타입과 인과 세션 타입
|
||||
|
||||
데이터 위험은 없다. 없는 것을 쓸 수는 없기 때문이다. 이 플래그가 무엇을 켜는지 적힌 곳이 없고, 시작 검증이 통과한 것과 실행체가 조립된 것을 구분해 주는 신호도 없다.
|
||||
|
||||
수정은 셋 중 하나다. 트랜잭션 실행체를 조건부 빈으로 조립하거나, 플래그가 무엇을 켜는지를 문서에 적거나, 같은 생성자의 required-secondaries 처럼 값을 예외로 거부하는 것이다.
|
||||
수정 선택지는 셋이다. 트랜잭션 실행체를 조건부 빈으로 조립할 수 있고, 플래그가 실제로 무엇을 켜는지 문서화할 수 있으며, 같은 생성자의 `required-secondaries`처럼 지원하지 않는 값을 예외로 거부할 수도 있다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -87,9 +87,9 @@ Spring Boot : 4.0.8
|
||||
|
||||
## 그 검사 자체가 조건부다
|
||||
|
||||
검증기를 돌리는 빈은 `MongoTopologyProbe` 에 조건되어 있다. 그리고 이 저장소는 프로브를 출하하지 않는다 — 그 자리 javadoc 이 직접 적는다. 프로브는 연결을 소유한 조립 루트가 살아 있는 데이터 평면 클라이언트로 만드는 것이고, 그것은 fork 의 결정이라는 것이다.
|
||||
검증기를 돌리는 빈은 `MongoTopologyProbe`에 조건되어 있다. 이 저장소가 프로브를 출하하지 않는다는 사실은 `MongoTopologyProbe`의 javadoc에 적혀 있다. 연결을 소유한 조립 루트가 실제 데이터 평면 클라이언트로 프로브를 만들며, javadoc은 그 선택을 fork의 책임으로 둔다.
|
||||
|
||||
프로브 없이 플랫폼 프로파일만 설정한 배포는 별도의 빈이 시작을 거부한다. 그 예외 문구가 이유를 적는다 — 검사가 하필 자기 부재를 보고해야 할 바로 그 빈에 조건되어 있어서, 프로브 없이 시작하면 토폴로지도 Stable API 수준도 자격의 실제 능력도 아무것도 검사하지 않은 채 조용히 지나간다는 것이다.
|
||||
프로브 없이 플랫폼 프로파일만 설정한 배포는 별도의 빈이 시작을 거부한다. 예외 문구는 검증 빈 자체가 프로브에 조건되어 있어, 프로브가 없으면 토폴로지·Stable API 수준·자격의 실제 능력을 검사할 경로가 열리지 않는다고 설명한다.
|
||||
|
||||
그래서 이 플래그가 시작 요구를 만드는 것은 fork 가 프로브와 보안 프로파일과 관리 자격 참조와 스키마 버전 범위를 모두 공급했을 때다. 셰이프 그대로의 이 저장소에서는 검사가 열리지 않는다.
|
||||
|
||||
@@ -99,7 +99,7 @@ Spring Boot : 4.0.8
|
||||
|
||||
`profiles` 의 널은 빈 맵으로 흡수한다. `change-streams` 는 무엇이 오든 거짓으로 덮어쓴다. `required-secondaries` 에 음수가 오면 예외를 던져 거부한다.
|
||||
|
||||
`transactions` 는 이 생성자에 아예 등장하지 않는다. 값이 그대로 보존되는 이유가 그것이다.
|
||||
`transactions`는 이 생성자에 아예 등장하지 않는다. 생성자가 이 값을 덮어쓰거나 거부하지 않으므로 입력값이 그대로 보존된다.
|
||||
|
||||
덮어쓰기 쪽만 참으로 설정해도 예외도 로그도 발생하지 않는다. 그 줄에 붙은 주석은 값을 무시하면 적용된 것처럼 보이게 되니 저장하지 않고 거부한다고 적는데, 예외로 거부하는 것은 `required-secondaries` 가 하는 일이고 여기서 일어나는 것은 조용한 덮어쓰기다.
|
||||
|
||||
@@ -113,7 +113,7 @@ Spring Boot : 4.0.8
|
||||
|
||||
## 두 스위치가 반대 방향으로 같은 곳에서 끊겼다
|
||||
|
||||
트랜잭션은 스위치가 살아서 요구를 만드는데 그 요구를 갚을 코드가 조립되지 않는다. change stream 은 코드가 조립되는데 스위치가 죽어 있다. 방향은 반대이고 끊긴 자리는 같다.
|
||||
트랜잭션은 스위치가 요구를 만들지만 그 요구를 수행할 코드가 조립되지 않는다. change stream은 실행 코드가 조립되는데 스위치가 생성자에서 꺼진다. 방향은 반대지만 둘 다 설정 플래그와 실제 조립 경로가 분리되어 있다.
|
||||
|
||||
## 남는 것은 데이터 위험이 아니다
|
||||
|
||||
|
||||
+6
-6
@@ -65,10 +65,10 @@ kafka-clients : 4.1.2
|
||||
## 재현 조건
|
||||
|
||||
1. KafkaProfileValidator 에서 운영 프로파일에 거는 규칙 둘을 읽는다.
|
||||
2. 그 검증기를 설정에서 컴파일된 프로파일에 돌리는 자리와, 그것이 기동의 어느 단계인지 확인한다.
|
||||
2. 설정에서 컴파일한 프로파일을 검증기에 넘기는 호출 경로와, 그 호출이 기동의 어느 단계에서 실행되는지 확인한다.
|
||||
3. 그 거부를 고정하는 테스트를 찾는다.
|
||||
4. 두 조립부를 켜는 프로퍼티를 전부 찾고 출하 기본값을 읽는다.
|
||||
5. 프로덕션에서 KafkaProducer 를 만드는 자리를 전부 세고, 각 설정 맵의 원문을 그대로 읽는다.
|
||||
5. 프로덕션에서 `KafkaProducer`를 생성하는 코드를 전부 찾고, 각 생성 코드가 넘기는 설정 맵을 그대로 읽는다.
|
||||
6. security.protocol 을 상수명과 리터럴 양쪽으로 저장소 전체에서 찾는다.
|
||||
7. KafkaSecurityConfigurer.configure 가 자격 종류마다 무엇을 넣고 어디서 던지는지 읽는다.
|
||||
8. 그 클래스의 빈 팩토리에 붙은 조건을 따라가고, 사슬 끝의 인터페이스를 구현하는 main 클래스를 센다.
|
||||
@@ -95,7 +95,7 @@ kafka-clients : 4.1.2
|
||||
|
||||
## 조립되는 KafkaProducer 두 곳에 security.protocol 이 없다
|
||||
|
||||
프로덕션에서 `KafkaProducer` 를 만드는 자리는 둘이다.
|
||||
프로덕션에서 `KafkaProducer`를 생성하는 코드는 둘이다.
|
||||
|
||||
`KafkaMessagingAutoConfiguration.messagingKafkaProducer` 가 `:153` 에서 만든다. `:139` 에서 빈 `HashMap` 을 열고 `:140`\~`:152` 에 넣는 것은 `BOOTSTRAP_SERVERS_CONFIG`, 직렬화기 둘, `ACKS_CONFIG`, `ENABLE_IDEMPOTENCE_CONFIG` 다섯이다.
|
||||
|
||||
@@ -122,7 +122,7 @@ kafka-clients : 4.1.2
|
||||
|
||||
이 클래스를 만드는 팩토리는 `KafkaMessagingAutoConfiguration:109` 에 있는데, 조건이 두 단이다. `:107` 이 `@ConditionalOnBean(CredentialRuntimeRegistry.class)` 이고, 그 레지스트리를 내놓는 `MessagingCoreAutoConfiguration:317` 은 `:315` 의 `@ConditionalOnBean(CredentialProvider.class)` 뒤에 있다. 그런데 `CredentialProvider` 를 구현하는 main 클래스가 0 건이다. 유일한 구현은 `CredentialRuntimeRegistryTest:21` 의 시험용 클래스다.
|
||||
|
||||
그래서 출하되는 애플리케이션에서 이 빈은 만들어지지도 않는다. 만들어졌다 해도 받을 곳이 없다 — 그것을 파라미터나 필드로 받는 프로덕션 코드가 0 건이고, `getBean` 과 `ObjectProvider` 와 빈 이름 문자열로 가져가는 자리도 0 건이다.
|
||||
그래서 확인한 출하 조립 경로에서는 이 빈이 만들어지지 않는다. searched direct reference 기준으로 파라미터나 필드로 받는 프로덕션 코드가 0건이고, `getBean`·`ObjectProvider`·빈 이름 문자열로 조회하는 코드도 찾지 못했다.
|
||||
|
||||
## 조립 테스트가 설정 맵의 키를 단언하지 않는다
|
||||
|
||||
@@ -130,9 +130,9 @@ kafka-clients : 4.1.2
|
||||
|
||||
실 브로커에 붙는 `MessagingLiveRoundTripQualificationTest:66` 은 `new KafkaContainer("apache/kafka:4.1.0")` 를 쓴다. 보안 설정이 하나도 없는 컨테이너다. 그래서 이 테스트가 확인한 왕복은 보안 설정이 없는 브로커와의 왕복이다.
|
||||
|
||||
## 원문과 갈리는 자리
|
||||
## 원문과 다른 생산자 수
|
||||
|
||||
원문 §17.1 은 조립되는 생산자를 `messagingKafkaProducer` 하나로 적었다. 프로덕션에서 `KafkaProducer` 를 만드는 자리는 둘이고, `KafkaSenderConfig.kafkaSeamProducer` 도 같은 프로퍼티 조건에서 조립되며 그쪽에도 보안 키가 없다.
|
||||
원문 §17.1은 조립되는 생산자를 `messagingKafkaProducer` 하나로 적었다. 실제 프로덕션 생성 코드는 둘이며, `KafkaSenderConfig.kafkaSeamProducer`도 같은 프로퍼티 조건에서 조립된다. 이 두 번째 생산자 설정에도 보안 키가 없다.
|
||||
|
||||
원문이 `KafkaSecurityConfigurer` 가 만드는 성분을 다섯으로 센 것도 자격 종류를 하나로 놓았을 때다. `configure` 는 자격 종류마다 다른 SASL 메커니즘을 넣고, 상호 TLS 에서는 `sasl.jaas.config` 없이 `NONE` 만 넣으며, OAuth2 와 Nkey 에서는 아무것도 넣지 않고 던진다.
|
||||
|
||||
|
||||
+3
-3
@@ -26,13 +26,13 @@ messaging 플랫폼의 outbox 사슬은 조건이 참이 될 수 없어 조립
|
||||
## 관계
|
||||
|
||||
- **@Bean이 있다는 것은 조립 증거가 아니다**
|
||||
두 스택 중 하나만 컨텍스트에 들어간다는 것이 이 규칙의 사례다.
|
||||
이 사건에서는 두 스택 중 하나만 컨텍스트에 들어갔고, 빈 선언만으로 실제 조립 여부를 판단할 수 없었다.
|
||||
- **@ConditionalOnBean은 조건이 만족될 수 있는지까지 확인해야 한다**
|
||||
조건이 참이 될 수 없다는 관측이 이 규칙으로 이어진다.
|
||||
- **조립 결함을 판정하려면 조립하는 쪽을 먼저 읽어야 한다**
|
||||
조립하는 쪽을 읽지 않으면 중복을 조건 결함으로 오진한다.
|
||||
- **high-water mark가 본 위치를 뜻해서 재전달된 변경이 영구히 사라졌다**
|
||||
같은 신뢰성 계열에서 조용한 실패가 나타난 다른 사례다.
|
||||
같은 신뢰성 계열에서 high-water mark 해석이 재전달 이벤트를 삼킨 별도 실패를 다룬다.
|
||||
|
||||
## 문제
|
||||
|
||||
@@ -66,7 +66,7 @@ messaging 스타터의 MessagingReliabilityAutoConfiguration 은 outbox 와 inbo
|
||||
구현 : JdbcOutboxRepository 2,276 LOC. 스프링 스테레오타입이 없다
|
||||
조립 : MessagingReliabilityAutoConfiguration 81행의 조건 뒤
|
||||
|
||||
측정으로 확정한 것은 이렇다. main 코드에서 JdbcOutboxRepository 나 JdbcInboxRepository 나 OutboxEnvelopeFactory 를 생성하는 곳이 0 이고, app-bootstrap 에서 관련 빈을 만드는 곳도 0 이다. OutboxEnvelopeFactory 를 @Bean 으로 만드는 곳은 스타터의 테스트 하나뿐이다.
|
||||
정적 검색에서는 main 코드가 `JdbcOutboxRepository`·`JdbcInboxRepository`·`OutboxEnvelopeFactory`를 직접 생성하지 않았고, app-bootstrap에서도 관련 빈 생성 코드를 찾지 못했다. `OutboxEnvelopeFactory`를 `@Bean`으로 만드는 코드는 스타터 테스트 하나에서만 확인했다.
|
||||
|
||||
이 차이가 중요한 이유는 수정 방향이 반대이기 때문이다. 조건이 만족되지 않는다고 읽으면 app-bootstrap 에 빈을 등록하는 수정이 된다. outbox 가 둘이라고 읽으면 어느 쪽이 정본인지 먼저 정해야 하는 문제가 된다.
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: the-guard-is-on-and-the-service-is-not
|
||||
title: 가드와 journal 과 durability 검증기는 켜지고, 부를 서비스가 없었다
|
||||
topic: assembly-ownership
|
||||
topicName: 조립 소유권 — 통제와 그 의존을 같은 곳이 소유하기
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: case:the-guard-is-on-and-the-service-is-not
|
||||
source:
|
||||
- final/document.md#a19
|
||||
- final/document.md#5-4
|
||||
- final/document.md#a19 §8.1
|
||||
- final/document.md#a19 §8.2
|
||||
---
|
||||
|
||||
# 가드와 journal 과 durability 검증기는 켜지고, 부를 서비스가 없었다
|
||||
|
||||
admin 경로의 보호 장치는 조립되는데 파괴적 작업을 수행할 서비스는 자동설정하지 않는 구조가 함께 존재한다. 현재 판정은 정적 조립 분석에 근거하며 실제 admin 활성 부팅은 재현하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **파괴적 admin 작업은 자동설정하지 않는다**
|
||||
이 부재를 프로젝트가 의도한 결정으로 설명한다.
|
||||
- **@Bean이 있다는 것은 조립 증거가 아니다**
|
||||
보호 장치와 실제 실행 서비스를 분리해서 확인하는 기준이다.
|
||||
|
||||
## 문제
|
||||
|
||||
가드·journal·durability 검증 같은 주변 통제는 자동설정 경로에 올라가지만, 실제 파괴 작업을 수행하는 admin 서비스는 같은 방식으로 제공되지 않는다.
|
||||
|
||||
정적 분석에서 서비스 직접 참조와 조립 경로를 찾지 못했고, SSOT는 이 부재를 의도된 안전 경계로 기록한다. 문제는 보호 장치가 존재한다는 사실만 보고 admin 기능 전체가 제공된다고 오해할 수 있다는 점이다.
|
||||
|
||||
## 결론
|
||||
|
||||
이 구조는 “가드가 켜졌으니 서비스도 켜졌다”는 신호가 아니다. 파괴적 실행 서비스는 애플리케이션이 명시적으로 제공해야 하며, 플랫폼 자동설정은 그 서비스를 만들어 주지 않는다.
|
||||
|
||||
현재 머신에서 source repository를 다시 대조하지 못했으므로, 실제 admin 활성 부팅까지 확인했다고 주장하지 않는다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
sourceRevision : 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
현재 source repository 재대조 : UNVERIFIABLE
|
||||
확인 방식 : SSOT에 기록된 조립 경로와 정적 참조 분석
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. SSOT의 admin 자동설정 절에서 생성되는 guard·journal·validator를 확인한다.
|
||||
2. 파괴적 admin service 타입의 main 직접 참조와 생성 경로를 확인한다.
|
||||
3. 자동설정이 실행 서비스를 직접 제공하는지 분리해서 본다.
|
||||
4. 실제 부팅 재현은 source repository가 있는 환경에서 별도로 수행한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 보호 장치와 실행 서비스는 같은 능력이 아니다
|
||||
|
||||
가드는 호출을 허용할지 판정하고 journal은 실행 이력을 남긴다. durability validator는 전제 조건을 검사한다. 이 셋이 조립돼도 실제 파괴 작업을 수행할 service가 자동으로 생기지는 않는다.
|
||||
|
||||
## 부재를 의도된 경계로 읽는다
|
||||
|
||||
SSOT는 destructive admin operation을 플랫폼이 자동설정하지 않는 방향을 별도 Decision 후보로 분리한다. 따라서 서비스 부재는 현재 분석에서 우연한 누락으로만 다루지 않는다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
실제로 admin 기능을 켠 애플리케이션을 부팅해 “호출 대상이 없다”는 런타임 결과를 재현하지 않았다. 현재 결론은 sourceRevision에 대한 기존 정적 분석 범위다.
|
||||
|
||||
<!-- body:end -->
|
||||
+12
-12
@@ -1,7 +1,7 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: thirteen-startup-rules-never-run
|
||||
title: 시작 검증기 13개 규칙이 유일한 조립 지점에서 호출되지 않는다
|
||||
title: 확인한 자동설정 경로가 시작 검증기 13개 규칙을 호출하지 않는다
|
||||
topic: assembly-ownership
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
@@ -17,14 +17,14 @@ source:
|
||||
- 원본 분석 절은 final/document.md#5-3 · final/document.md#a20 §3.1 이다.
|
||||
---
|
||||
|
||||
# 시작 검증기 13개 규칙이 유일한 조립 지점에서 호출되지 않는다
|
||||
# 확인한 자동설정 경로가 시작 검증기 13개 규칙을 호출하지 않는다
|
||||
|
||||
gRPC 플랫폼의 시작 검증기는 188줄에 13개 위반 규칙을 담고 있다. 이 검증기를 부르는 것은 자기 테스트뿐이고, 유일한 자동설정 지점은 부르지 않는다.
|
||||
gRPC 플랫폼의 시작 검증기는 188줄에 13개 위반 규칙을 담고 있다. searched direct reference 기준으로 호출은 자기 테스트에서 확인했고, `GrpcPlatformAutoConfiguration`은 이 검증기를 직접 호출하지 않는다. 이 결과만으로 다른 lifecycle·framework discovery 경로까지 없다고 단정하지 않는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다**
|
||||
이 사례에서 끌어낸 확인 절차다.
|
||||
- **시작 검증기는 실제 lifecycle과 assembly 경로에서 실행 여부를 확인한다**
|
||||
이 사례의 direct-call 결과를 runtime 전체 미실행으로 과장하지 않기 위해 만든 확인 절차다.
|
||||
- **@Bean이 있다는 것은 조립 증거가 아니다**
|
||||
검증기가 존재한다는 것과 그것이 도는 것은 별개다.
|
||||
|
||||
@@ -32,19 +32,19 @@ gRPC 플랫폼의 시작 검증기는 188줄에 13개 위반 규칙을 담고
|
||||
|
||||
GrpcPlatformStartupValidator 는 188줄이고 violations 목록에 13개 항목을 추가한다. TLS 요구, 실행기 풀 크기, 채널 프로파일, 자격증명 누출 등을 검사한다.
|
||||
|
||||
이 검증기를 호출하는 곳을 저장소 전체에서 찾으면 자기 테스트 GrpcPlatformStartupValidatorTest 하나뿐이다.
|
||||
searched direct reference에서는 `GrpcPlatformStartupValidatorTest`의 테스트 호출만 확인된다.
|
||||
|
||||
같은 패키지의 GrpcPlatformAutoConfiguration 은 106줄이고 @Bean 이 9개인데, 그중 어느 것도 이 검증기를 부르지 않는다.
|
||||
|
||||
## 결론
|
||||
|
||||
검증기는 정확하고 잘 테스트되어 있으며 돌지 않는다.
|
||||
검증기는 규칙과 단위 테스트를 갖고 있지만, 확인한 자동설정 경로에서는 호출되지 않는다.
|
||||
|
||||
이 판정은 gRPC 블록 전체가 어떤 런타임 컴포지션에도 속하지 않는다는 더 큰 사실 안에 있다. 그 상태는 저장소가 문서로 인정하고 있으며 결함이 아니다. 다만 이 검증기의 경우, 블록이 배포되기 시작하는 날에도 자동으로 돌기 시작하지는 않는다는 점이 남는다. 조립 지점이 그것을 부르지 않기 때문이다.
|
||||
이 판정은 gRPC 블록 전체가 현재 확인한 런타임 컴포지션에 속하지 않는다는 범위 안에 있다. 저장소 문서도 그 상태를 인정하므로 현재 미실행 자체를 결함으로 보지 않는다. 다만 블록을 배포하기 시작할 때는 검증기를 lifecycle에 명시적으로 연결해야 한다. 확인한 자동설정 경로는 이 검증기를 호출하지 않는다.
|
||||
|
||||
13개 규칙은 테스트로 고정되어 있으므로 회귀는 잡힌다. 잡히지 않는 것은 그 규칙이 실행 시점에 적용되는가다.
|
||||
|
||||
확인 절차로 일반화하면 이렇다. 시작 검증기가 실제로 도는지는 그 능력에 자동설정 루트가 있고 그 루트가 검증기를 부르는지와 일치한다. 검증기 파일의 존재나 그 테스트의 통과는 답이 아니다.
|
||||
확인 절차로 일반화하면 direct caller에서 멈추지 않는다. `@Bean`·component scan·auto-configuration, lifecycle callback, application event·post processor, framework discovery를 차례로 확인하고, 실제 부팅이 가능한 환경에서는 condition report와 시작 로그까지 본다. 검증기 파일과 테스트의 존재만으로 runtime 실행 여부를 판정하지 않는다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -73,13 +73,13 @@ validator가 5개 그룹 13개 규칙을 갖고(transport·security 4 / executor
|
||||
:::evidence key="thirteen-startup-rules-never-run" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
|
||||
:::
|
||||
|
||||
## 유일한 조립 지점이 부르지 않는다
|
||||
## 확인한 자동설정 경로는 validator를 부르지 않는다
|
||||
|
||||
자동설정은 `@Bean` 9개를 만들면서 이 validator를 부르지 않고, static 메서드라 빈이 될 수도 없다.
|
||||
자동설정은 `@Bean` 9개를 만들면서 이 validator를 직접 부르지 않는다. validator 자체는 static 메서드 기반이라 일반적인 component bean 등록 경로도 보이지 않는다. 다만 다른 lifecycle·framework discovery 경로까지 이번 정적 검색으로 배제하지 않는다.
|
||||
|
||||
## 두 개의 강제가 이 하나를 통해서만 성립한다
|
||||
|
||||
CLAUDE.md가 인용한 "streaming method가 Stable catalog에 등록되면 startup을 거부한다"와 §2.2의 runtime 강제 둘 다이므로, 둘 다 실행되지 않는다.
|
||||
CLAUDE.md가 인용한 "streaming method가 Stable catalog에 등록되면 startup을 거부한다"와 §2.2의 runtime 강제는 이 validator의 규칙에 의존한다. 확인한 자동설정 경로만 보면 이 규칙을 호출하지 않는다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+2
-2
@@ -28,9 +28,9 @@ source:
|
||||
## 관계
|
||||
|
||||
- **스캔에서 뺀 다섯 패키지의 컴포넌트 여섯을 두 자동설정 어느 쪽도 소유하지 않았다**
|
||||
경로가 바뀌는 지점에서 소유권이 끊긴 사례다.
|
||||
스캔에서 제외된 뒤 자동설정도 여섯 컴포넌트를 만들지 않아 실행 컨텍스트에 들어오지 않았다.
|
||||
- **넓은 스캔을 좁히자 여덟 컴포넌트에 아무것도 도달하지 않았다**
|
||||
같은 형태가 퍼시스턴스 리프에서 나타난 사례다.
|
||||
퍼시스턴스 리프에서도 스캔 범위를 좁힌 뒤 여덟 컴포넌트의 조립 경로가 사라졌다.
|
||||
- **@Bean이 있다는 것은 조립 증거가 아니다**
|
||||
이 개념을 확인 절차로 옮긴 규칙이다.
|
||||
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
---
|
||||
kind: PROJECT_DECISION
|
||||
slug: destructive-admin-operations-are-not-autoconfigured
|
||||
title: 파괴적 admin 작업은 자동설정하지 않는다
|
||||
topic: assembly-ownership
|
||||
topicName: 조립 소유권 — 통제와 그 의존을 같은 곳이 소유하기
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
decisionStatus: ADOPTED
|
||||
source:
|
||||
- final/document.md#a19
|
||||
- final/document.md#10-3
|
||||
- final/document.md#5-4
|
||||
- final/document.md#a19 §8.1
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
---
|
||||
|
||||
# 파괴적 admin 작업은 자동설정하지 않는다
|
||||
|
||||
플랫폼은 파괴적 admin 작업의 보호 장치와 계약을 제공할 수 있지만, 실제 실행 서비스까지 기본 빈으로 만들지는 않는다.
|
||||
|
||||
## 근거
|
||||
|
||||
- **가드와 journal 과 durability 검증기는 켜지고, 부를 서비스가 없었다**
|
||||
보호 장치와 실행 주체가 분리된 현재 조립 결과를 보여 준다.
|
||||
- **꺼짐은 조건의 반복이 아니라 구조여야 한다**
|
||||
능력 활성 여부를 조립 구조로 제한하는 기준이다.
|
||||
|
||||
## 결정문
|
||||
|
||||
파괴적 admin operation의 실제 실행 서비스는 자동설정하지 않는다. 애플리케이션이 사용 의도를 명시하고 필요한 의존성을 제공한 경우에만 별도 조립 경로에서 만든다.
|
||||
|
||||
## 판단 이유
|
||||
|
||||
파괴 작업은 잘못 노출됐을 때 복구 비용이 크다. 플랫폼이 클래스패스와 설정만 보고 실행 서비스를 자동 생성하면, 사용자가 기능을 선택하지 않았는데도 파괴 권한이 생길 수 있다.
|
||||
|
||||
가드와 journal을 자동설정하는 것은 실행 서비스 자동설정과 다르다. 보호 장치는 실행 서비스가 제공되는 경우 적용할 공통 규칙이고, 서비스 생성은 채택 애플리케이션의 책임으로 둔다.
|
||||
|
||||
## 영향
|
||||
|
||||
감수하는 것 : 기능을 쓰는 애플리케이션이 명시적인 wiring을 추가해야 한다.
|
||||
|
||||
얻는 것 : 라이브러리를 추가했다는 이유만으로 파괴적 operation이 실행 가능한 상태가 되지 않는다.
|
||||
|
||||
얻는 것 : 자동설정의 존재와 admin 권한의 존재를 구분할 수 있다.
|
||||
+1
-1
@@ -37,7 +37,7 @@ source:
|
||||
|
||||
그리고 자식을 컴포넌트 스캔에서 빼는 것이 이 구조의 나머지 절반이다. 스캔이 자식 설정을 독립적으로 발견하면 루트를 우회하기 때문이다.
|
||||
|
||||
임포트 필터는 권한을 잃고 도구로 남는다. 프레임워크 자신의 자동설정을 후보 집합에서 빼는 일은 어떤 프로젝트 조건보다 먼저 일어나야 하므로 그 자리가 필요하지만, 능력이 켜졌는지 판정하는 것은 그 필터의 일이 아니다.
|
||||
임포트 필터는 마스터 스위치를 판정하지 않고 프레임워크 자동설정을 후보 집합에서 제거하는 역할만 맡는다. 이 제거는 프로젝트 조건을 평가하기 전에 실행되어야 하지만, 능력 활성 여부는 루트 자동설정이 판정한다.
|
||||
|
||||
## 영향
|
||||
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
|
||||
## 규칙
|
||||
|
||||
1. 애너테이션은 후보를 만들 뿐이다
|
||||
1. 애너테이션은 빈 후보만 등록한다
|
||||
Component 나 Repository 나 Bean 은 이 클래스가 빈이 될 수 있다는 뜻이지 빈이라는 뜻이 아니다. 스캔 범위 밖이거나 조건이 거짓이거나 소유자가 없으면 후보로 끝난다.
|
||||
|
||||
2. 이름은 아무것도 보장하지 않는다
|
||||
@@ -32,8 +32,8 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
4. 확인은 도달 경로로 한다
|
||||
세 경로 중 어느 것이 이 클래스를 소유하는지 묻는다. 스캔이면 범위와 제외를, 자동설정이면 imports 파일과 조건을, 명시 조립이면 그 생성 지점을 확인한다.
|
||||
|
||||
5. main 참조 0 은 강한 신호다
|
||||
프로덕션 소스에서 그 타입을 참조하는 파일이 자기 자신뿐이면, 테스트만 그것을 쓴다는 뜻이다.
|
||||
5. searched direct reference 0부터 확인하되 거기서 멈추지 않는다
|
||||
프로덕션 소스의 직접 참조 검색에서 선언 자신 외의 사용처를 찾지 못했다는 뜻까지가 증거다. 이것만으로 runtime 미사용을 확정하지 않는다. component scan, auto-configuration imports와 `@Bean`, lifecycle callback, event/post processor, ServiceLoader·reflection·configuration/resource discovery처럼 정적 직접 참조에 잡히지 않는 경로도 확인한다.
|
||||
|
||||
## 적용 조건
|
||||
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
|
||||
# @ConditionalOnBean은 조건이 만족될 수 있는지까지 확인해야 한다
|
||||
|
||||
조건부 빈을 선언해 두고 그 조건을 만족시킬 수 있는 경로가 있는지 확인하지 않아, 능력 전체가 조용히 없는 상태를 막는다. Spring 은 조건 불만족을 정상 동작으로 보므로 로그에도 액추에이터에도 신호가 남지 않는다.
|
||||
조건부 빈을 선언해 두고 그 조건을 만족시킬 수 있는 경로가 있는지 확인하지 않아 능력 전체가 조립되지 않는 상태를 막는다. 조건 불만족이 application failure나 health failure로 자동 승격되지 않을 수는 있지만, condition evaluation evidence 자체가 사라지는 것은 아니다. Spring Boot의 `ConditionEvaluationReport`와, endpoint가 노출된 경우 Actuator `/actuator/conditions`에서 match 여부와 이유를 확인할 수 있다.
|
||||
|
||||
## 목적
|
||||
|
||||
@@ -29,8 +29,8 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
3. 플랫폼이 제공하지 않겠다고 선언한 경우 질문을 바꾼다
|
||||
애플리케이션이 제공해야 하는 계약이라면, 물을 것은 조건이 아니라 출하 애플리케이션이 그 계약을 이행하는가다.
|
||||
|
||||
4. 조건 불만족은 오류로 보고되지 않는다
|
||||
Spring 은 조건부 빈이 조건을 만족하지 못하는 것을 정상 동작으로 본다. 로그에도 액추에이터에도 신호가 없다.
|
||||
4. 조건 불만족과 관측 가능성을 구분한다
|
||||
조건이 맞지 않았다는 사실이 application failure나 health failure로 자동 승격되지 않을 수 있다. 그렇다고 condition evaluation evidence가 없어지는 것은 아니다. `ConditionEvaluationReport`와, endpoint가 노출된 경우 `/actuator/conditions`에서 positive/negative match와 이유를 확인한다.
|
||||
|
||||
5. 꺼진 것과 조립될 수 없는 것을 구별할 방법을 남긴다
|
||||
둘이 런타임에서 같아 보이면 운영자는 차이를 알 수 없다.
|
||||
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
---
|
||||
kind: REFERENCE
|
||||
slug: the-startup-validator-follows-the-autoconfiguration-root
|
||||
title: 시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다
|
||||
title: 시작 검증기는 실제 lifecycle과 assembly 경로에서 실행 여부를 확인한다
|
||||
topic: assembly-ownership
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
@@ -10,7 +10,7 @@ rootTreeNode: reference:the-startup-validator-follows-the-autoconfiguration-root
|
||||
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
|
||||
---
|
||||
|
||||
# 시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다
|
||||
# 시작 검증기는 실제 lifecycle과 assembly 경로에서 실행 여부를 확인한다
|
||||
|
||||
검증기 파일이 존재하고 그 테스트가 통과한다는 사실을 검증이 실행된다는 증거로 읽는 것을 막는다. 규칙이 많고 잘 테스트되어 있다는 것은 품질의 증거이지 실행의 증거가 아니다.
|
||||
|
||||
@@ -20,11 +20,11 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
|
||||
## 규칙
|
||||
|
||||
1. 검증기의 호출자를 센다
|
||||
프로덕션 호출자가 0 이고 테스트 호출자만 있으면 그 검증은 실행 시점에 적용되지 않는다.
|
||||
1. searched direct caller를 확인한다
|
||||
프로덕션 직접 호출을 찾지 못한 것은 강한 신호지만 그것만으로 runtime 미실행을 확정하지 않는다.
|
||||
|
||||
2. 자동설정 루트가 부르는지 확인한다
|
||||
능력의 조립 지점이 검증기를 호출하지 않으면, 그 능력이 배포되기 시작해도 검증은 자동으로 시작되지 않는다.
|
||||
2. assembly와 lifecycle 경로를 함께 확인한다
|
||||
`@Bean`·component scan·auto-configuration, lifecycle callback, application event, post processor, framework discovery를 차례로 확인한다. 실제 부팅이 가능하면 condition report와 시작 로그도 함께 본다.
|
||||
|
||||
3. 규칙 수와 실행 여부를 분리해서 본다
|
||||
규칙이 많고 잘 테스트되어 있다는 것은 품질의 증거이지 실행의 증거가 아니다.
|
||||
@@ -44,7 +44,7 @@ verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지
|
||||
|
||||
## 예시
|
||||
|
||||
gRPC 플랫폼의 시작 검증기는 188줄에 13개 위반 규칙을 담고 있고, 호출자는 자기 테스트뿐이다. 같은 패키지의 자동설정은 106줄에 Bean 이 9개인데 검증기를 부르지 않는다.
|
||||
gRPC 플랫폼의 시작 검증기는 188줄에 13개 위반 규칙을 담고 있다. 현재 분석에서는 searched direct caller가 테스트에 있고 같은 패키지의 자동설정이 검증기를 직접 부르지 않는다는 점을 확인했다. 이 사실을 runtime 미실행으로 확정하려면 다른 lifecycle·framework discovery 경로도 닫아야 한다.
|
||||
|
||||
## 관계
|
||||
|
||||
|
||||
Reference in New Issue
Block a user