From c2981eec1e3a6466bd3048a7c80bf0225d4bb03e Mon Sep 17 00:00:00 2001 From: Dimo-2562 Date: Mon, 25 May 2026 15:51:08 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20user=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A1=9C=EB=93=9C=EB=A7=B5=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/ddd-test-refactoring-roadmap.md | 99 +++++++++++++++++++++++++--- docs/domain-strategy.md | 20 ++++++ 2 files changed, 111 insertions(+), 8 deletions(-) diff --git a/docs/ddd-test-refactoring-roadmap.md b/docs/ddd-test-refactoring-roadmap.md index 9962d39..6d4acf2 100644 --- a/docs/ddd-test-refactoring-roadmap.md +++ b/docs/ddd-test-refactoring-roadmap.md @@ -796,7 +796,82 @@ RssToPostProcessorTest --- -#### 4.8 전술 모델 2차 정리 (ID reference / 값 객체 / 엔티티 경계) +#### 4.8 `global` 패키지 소유권 회수 원칙 + +##### 왜 `global`을 먼저 정리하지 않는가 + +- `global`은 바운디드 컨텍스트가 아니라 shared infra, 웹/API 계약, 도메인 정책 잔여물이 섞여 있는 구현 bucket이다. +- 따라서 `global`을 먼저 청소하기 시작하면 “어디로 보내야 하는가” 기준이 약해져, 잘못된 공통화나 과한 패키지 이동이 생기기 쉽다. +- DDD 관점에서는 **소유 도메인을 먼저 정리하고**, 그 과정에서 해당 도메인이 실제로 소유하는 `global` 코드를 회수하는 편이 더 안전하다. + +##### 기본 원칙 + +```text +global-first cleanup 금지 +→ owner-context-first reclaim +``` + +즉: + +1. 먼저 바운디드 컨텍스트의 테스트/용어/aggregate 책임을 정리한다. +2. 그 다음 해당 컨텍스트가 실제로 소유하는 `global/*` 코드를 회수한다. +3. 끝까지 공통으로 남는 것만 shared/platform support로 유지한다. + +##### 무엇이 진짜 공통인가 + +대체로 다음은 독립 컨텍스트로 보지 않고 shared/platform support로 유지 가능하다. + +```text +global/common/BaseEntity, BaseTimeEntity +global/response/* +global/exception/* +global/config/* (단, 도메인 초기 데이터 제외) +global/lock/DistributedLock +``` + +##### 무엇이 도메인 소유인가 + +대표 예시는 다음과 같다. + +```text +global/security/* -> Auth / Security +global/elasticsearch/query/* -> Search / Recommendation +global/util/LinearTimeDecayStrategy -> Recommendation +global/config/InitialDataConfig -> Source / Ingestion +global/util/ContentCleaner -> Source / Post shared content support 후보 +``` + +##### 권장 회수 순서 + +현재 시점 기준 추천 순서는 다음과 같다. + +```text +1. User Account 4.3 + - 필요 시 global/security shared seam(UserAuthCacheService, UserPrincipal)만 함께 정리 + - 하지만 Auth / Security 전체 승격은 아직 하지 않는다 + +2. Personalization Profile 4.4 + - User Account와의 직접 호출 책임을 먼저 정리 + +3. Auth / Security + - global/security를 실제 컨텍스트 표면으로 승격/정리 + +4. Recommendation / Search + - VectorQueryBuilder, LinearTimeDecayStrategy, RRF/검색 정책 support를 owning context로 회수 + +5. Source / Post shared content support + - InitialDataConfig, ContentCleaner, 일부 converter/util을 owning context나 shared support로 재배치 +``` + +##### Activity / Post 1차 정리 이후의 해석 + +- Activity와 Post는 1차 정리가 끝났더라도, 지금 남아 있는 `global` 의존 중 상당수는 진짜 shared support이거나 다른 컨텍스트 소유다. +- 따라서 **Activity/Post 관련 `global` 정리를 먼저 별도 작업으로 시작하지 않는다.** +- 대신 이후 `Auth / Security`, `Recommendation / Search`, `Source`를 정리할 때 각각의 소유 코드를 회수한다. + +--- + +#### 4.9 전술 모델 2차 정리 (ID reference / 값 객체 / 엔티티 경계) 모든 컨텍스트를 **1차로 한 번씩 정리한 뒤**, 전역 기준에 맞춰 2차 정리를 수행한다. @@ -990,15 +1065,23 @@ TechnicalPostIndexed [완료] 6. Post aggregate / summary pipeline 안전망 확장 - PostTest, SummaryExtractionServiceTest - PostSummaryReaderDataJpaTest, PostSummaryWriterDataJpaTest -[다음] 6-1. Post embedding pipeline 테스트 작성 +[완료] 6-1. Post embedding pipeline 테스트 작성 - PostEmbeddingProcessorTest, PostEmbeddingWriterTest -[다음] 7. User aggregate 관심사 불변식 정리 -[다음] 8. Recommendation 생성 테스트 작성 - - MmrServiceTest, LlmRecommendationServiceTest -[다음] 9. SearchServiceImpl 테스트 작성 -[다음] 10. 컨텍스트 1차 정리 후 전술 모델 2차 정리 +[다음] 7. User Account 4.3 1차 정리 + - UserTest로 aggregate 규칙 보호 + - 관심사 불변식 aggregate 내부 이동 + - Personalization/Auth shared seam 정리 +[다음] 8. Personalization Profile 4.4 1차 정리 + - User Account와의 직접 호출 책임 분리 +[부분 진행] 9. Recommendation/Search 회귀 테스트 보강 + - LlmRecommendationServiceTest, SearchServiceImplTest 반영 + - MmrServiceTest 추가 필요 +[후속] 10. global 소유권 회수 + - global-first가 아니라 owner-context-first 방식으로 진행 + - Auth / Security -> Recommendation/Search -> Source/Post shared support 순으로 회수 +[다음] 11. 컨텍스트 1차 정리 후 전술 모델 2차 정리 - aggregate / value object / ID reference / 엔티티 경계 정교화 -[다음] 11. Phase 6 진입 조건 충족 후 이벤트/포트 분리 시작 +[다음] 12. Phase 6 진입 조건 충족 후 이벤트/포트 분리 시작 - hexagonal architecture(포트/어댑터) 적용 검토 ``` diff --git a/docs/domain-strategy.md b/docs/domain-strategy.md index 3f38e27..8879cf6 100644 --- a/docs/domain-strategy.md +++ b/docs/domain-strategy.md @@ -80,6 +80,23 @@ TechFork의 비즈니스 도메인은 다음으로 정의할 수 있다. | Webhook/운영 알림 전송 | `WebhookNotificationService` | 외부 메시지 전송 자체는 범용 integration이다. | | 이미지 URL 최적화 | `CloudflareThirdPartyThumbnailOptimizer` | 썸네일 전달 최적화는 범용 지원 기술이다. | +### `global` 패키지 해석 원칙 + +`src/main/java/com/techfork/global`은 **독립 바운디드 컨텍스트가 아니라 shared implementation bucket**으로 해석한다. + +따라서 DDD 리팩터링에서는 `global`을 먼저 정리하는 것이 아니라, **실제 소유 컨텍스트를 먼저 정리하고 그 과정에서 `global` 코드를 회수**하는 방식을 기본 원칙으로 둔다. + +대표 분류는 다음과 같다. + +| 현재 위치 | DDD 해석 | 기본 처리 원칙 | +|---|---|---| +| `global/security/*` | `Auth / Security`의 실제 구현 표면 | Auth / Security 리팩터링 시 컨텍스트로 승격/회수 | +| `global/llm/*` | 범용 provider adapter / AI integration | provider adapter는 shared로 유지, 프롬프트/정책은 owning context로 분리 | +| `global/elasticsearch/query/*` | Search / Recommendation 후보 탐색 정책 support | Search / Recommendation 리팩터링 시 owning context로 회수 | +| `global/util/LinearTimeDecayStrategy` | Recommendation 정책 | Recommendation 컨텍스트로 회수 | +| `global/config/InitialDataConfig` | Source 초기 데이터 bootstrap | Source / Ingestion 컨텍스트로 회수 | +| `global/common/*`, `global/response/*`, `global/exception/*`, 일부 `global/config/*`, `global/lock/*` | shared technical support | 진짜 공통이면 유지하거나 shared/platform support로 축소 | + #### 분류상 주의사항 - `User Account` 컨텍스트는 전체가 핵심은 아니다. @@ -93,6 +110,9 @@ TechFork의 비즈니스 도메인은 다음으로 정의할 수 있다. - 요약, 키워드 추출, 청크, 임베딩, 검색 문서화는 핵심 하위 도메인에 가깝다. - `Search` 컨텍스트에서 Elasticsearch 호출 자체는 일반 하위 도메인이지만, BM25/semantic/RRF/personal reranking 조합 정책은 핵심 하위 도메인이다. - `Recommendation` 컨텍스트는 거의 전체가 핵심 하위 도메인이다. 다만 executor, repository plumbing 같은 기술 요소는 일반/지원으로 볼 수 있다. +- `global`은 문서상 독립 컨텍스트로 다루지 않는다. + - `global/security`처럼 실제 컨텍스트 소유권이 분명한 코드는 해당 컨텍스트로 승격한다. + - `BaseEntity`, `BaseResponse`, `GlobalExceptionHandler`, infra config처럼 진짜 공통 지원 코드는 shared/platform support로 유지한다. ---