Files
clean-architecture-backend-…/docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md

3944 lines
198 KiB
Markdown

# MongoDB 문서 영속성 플랫폼 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Java/Spring Backend Skeleton에 도메인 Document·Repository 소유권, 고정 BSON 표현, 단일 Document 원자 연산, Replica Set Transaction, 실행 결과·Retry 의미론, Query·Aggregation Guardrail, Schema·Index·Migration, Change Stream, 보안·관측성·실제 MongoDB Release Matrix를 갖춘 MongoDB 문서 영속성 플랫폼을 구현한다.
**Architecture:** `mongodb-core-api`가 framework-free 의미론을 소유하고 Spring Data·imperative·reactive·transaction·query·aggregation·index-schema·change-stream 모듈이 이를 구현한다. 도메인은 Document와 Repository를 직접 소유하며 플랫폼은 범용 CRUD Repository를 만들지 않는다. D1/D2는 Stable API V1 strict client, D3는 승인된 Capability client, D4는 Runtime과 분리된 Admin client를 사용한다.
**Tech Stack:** Java 21, Gradle Kotlin DSL, Spring Boot 4.1 dependency management, Spring Data MongoDB 5.1, Boot-managed MongoDB Java Driver, MongoDB 7.0·8.0, Reactor, Micrometer, Driver native ObservabilitySettings, JUnit 5, AssertJ, ArchUnit, Testcontainers, Toxiproxy, Flamingock adapter.
## Global Constraints
- Root package는 `io.backend.skeleton.mongodb`이다.
- Stable 모듈 루트는 `modules/mongodb`이다.
- Java 21과 Spring Boot 4.1 BOM을 사용하고 MongoDB Java Driver 버전을 모듈에서 직접 고정하지 않는다.
- MongoDB 8.0 최신 패치를 Primary Certification Lane, MongoDB 7.0을 Compatibility Lane으로 사용한다.
- Local 기본 토폴로지는 Single-node Replica Set이며 Standalone은 smoke test만 허용한다.
- 운영 Stable Gate는 실제 3-node Replica Set failover를 포함한다.
- 도메인 모듈이 `@Document`, Repository, Collection logical name, Query, Index Requirement, Schema Version, Embed/Reference, Shard Key 후보를 소유한다.
- `CommonMongoRepository<T, ID>` 또는 `GenericMongoRepository<T, ID>`를 만들지 않는다.
- D1/D2는 Stable API V1 strict client를 사용한다.
- D3 Capability client와 D4 Admin client는 별도 권한·설정·모듈이다.
- UUID는 STANDARD, BigDecimal은 Decimal128로 고정한다.
- `LocalDateTime`은 명시 Converter 없이는 저장하지 않는다.
- Java FQCN을 장수 Collection의 영구 type metadata로 사용하지 않는다.
- 부분 변경은 Update Operator를 우선하고 전체 교체는 revision predicate를 요구한다.
- 단일 Document 원자 연산을 Multi-document Transaction보다 우선한다.
- `TransientTransactionError`는 전체 본문을 새 Session에서 재실행한다.
- `UnknownTransactionCommitResult`에서는 업무 본문을 재실행하지 않는다.
- Query·Aggregation은 operation name, allowlist, maxTimeMS, result limit을 요구한다.
- Production runtime에서 auto-index creation, collMod, shard, repair, drop 작업을 허용하지 않는다.
- Change Stream은 at-least-once idempotent projector이며 raw event를 외부 Integration Event로 공개하지 않는다.
- TTL은 physical cleanup이고 정확한 Scheduler로 사용하지 않는다.
- GridFS는 compatibility/migration 전용이며 신규 파일 Source of Truth가 아니다.
- Document, raw query, PII, credential, resume token, shard key 값을 log·metric label에 기록하지 않는다.
- Advanced 기능은 별도 계획과 모듈에서 구현하고 Stable Starter에 자동 포함하지 않는다.
- 각 Task는 실패 테스트 → 실패 확인 → 최소 구현 → 통과 확인 → 커밋 순서로 수행한다.
- 각 Task는 독립적으로 검토 가능한 하나의 커밋으로 종료한다.
---
## 1. 확정 파일 구조
```text
backend-skeleton/
├── build-logic/src/main/kotlin/mongodb-library-conventions.gradle.kts
├── modules/mongodb/
│ ├── mongodb-core-api/
│ ├── mongodb-spring-data/
│ ├── mongodb-imperative/
│ ├── mongodb-reactive/
│ ├── mongodb-query/
│ ├── mongodb-aggregation/
│ ├── mongodb-transaction/
│ ├── mongodb-index-schema/
│ ├── mongodb-change-stream/
│ ├── mongodb-geospatial/
│ ├── mongodb-migration-core/
│ ├── mongodb-migration-flamingock/
│ ├── mongodb-observability/
│ ├── mongodb-security/
│ ├── mongodb-spring-boot-starter/
│ ├── mongodb-testkit-core/
│ ├── mongodb-testkit-replicaset/
│ ├── mongodb-testkit-failover/
│ └── mongodb-testkit-migration/
├── docs/mongodb/
├── docs/adr/
└── docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md
```
## 2. Stable module dependency map
```text
mongodb-core-api
→ no project dependency
mongodb-spring-data
→ mongodb-core-api
mongodb-imperative / mongodb-reactive
→ mongodb-core-api
→ mongodb-spring-data
mongodb-query
→ mongodb-core-api
→ mongodb-spring-data
mongodb-aggregation
→ mongodb-core-api
→ mongodb-query
mongodb-transaction
→ mongodb-core-api
→ mongodb-spring-data
mongodb-index-schema
→ mongodb-core-api
→ mongodb-spring-data
mongodb-change-stream
→ mongodb-core-api
→ mongodb-reactive
mongodb-geospatial
→ mongodb-core-api
→ mongodb-spring-data
mongodb-migration-core
→ mongodb-core-api
→ mongodb-index-schema
mongodb-migration-flamingock
→ mongodb-migration-core
mongodb-observability / mongodb-security
→ mongodb-core-api
mongodb-spring-boot-starter
→ every Stable runtime module
→ no advanced module
```
---
### Task 1: Gradle 멀티모듈과 MongoDB 품질 Test Suite 구성
**Files:**
- Create: `build-logic/src/main/kotlin/mongodb-library-conventions.gradle.kts`
- Create: `modules/mongodb/mongodb-core-api/build.gradle.kts`
- Create: `modules/mongodb/mongodb-spring-data/build.gradle.kts`
- Create: `modules/mongodb/mongodb-imperative/build.gradle.kts`
- Create: `modules/mongodb/mongodb-reactive/build.gradle.kts`
- Create: `modules/mongodb/mongodb-query/build.gradle.kts`
- Create: `modules/mongodb/mongodb-aggregation/build.gradle.kts`
- Create: `modules/mongodb/mongodb-transaction/build.gradle.kts`
- Create: `modules/mongodb/mongodb-index-schema/build.gradle.kts`
- Create: `modules/mongodb/mongodb-change-stream/build.gradle.kts`
- Create: `modules/mongodb/mongodb-geospatial/build.gradle.kts`
- Create: `modules/mongodb/mongodb-migration-core/build.gradle.kts`
- Create: `modules/mongodb/mongodb-migration-flamingock/build.gradle.kts`
- Create: `modules/mongodb/mongodb-observability/build.gradle.kts`
- Create: `modules/mongodb/mongodb-security/build.gradle.kts`
- Create: `modules/mongodb/mongodb-spring-boot-starter/build.gradle.kts`
- Create: `modules/mongodb/mongodb-testkit-core/build.gradle.kts`
- Create: `modules/mongodb/mongodb-testkit-replicaset/build.gradle.kts`
- Create: `modules/mongodb/mongodb-testkit-failover/build.gradle.kts`
- Create: `modules/mongodb/mongodb-testkit-migration/build.gradle.kts`
- Modify: `settings.gradle.kts`
- Test: `build-logic/src/test/java/MongoDbModuleBoundaryTest.java`
**Interfaces:**
- Consumes: Host repository version catalog and Spring Boot 4.1 dependency management.
- Produces: 19 isolated Stable MongoDB modules and unit, contract, replicaSet, failover, migration, compatibility, performance suites.
**Implementation requirements:**
- Apply Java 21 toolchains to all modules.
- Use Spring Boot BOM for Spring Data MongoDB and the Java Driver; do not pin the driver in module build files.
- Keep mongodb-core-api free of Spring, Driver, BSON and Reactor dependencies.
- Do not include advanced modules in the Stable dependency graph.
- Register release suites without making external-provider tests part of the default unit test task.
- [ ] **Step 1: Write the failing test**
```java
import static org.assertj.core.api.Assertions.assertThat;
class MongoDbModuleBoundaryTest {
@org.junit.jupiter.api.Test
void stableModuleListContainsOnlyApprovedModules() {
java.util.Set<String> modules = MongoDbBuildModel.stableModules();
assertThat(modules).contains("mongodb-core-api", "mongodb-transaction");
assertThat(modules).doesNotContain("mongodb-sharding", "mongodb-search");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew build-logic:test --tests 'MongoDbModuleBoundaryTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoDbBuildModel {
private static final java.util.Set<String> STABLE = java.util.Set.of(
"mongodb-core-api", "mongodb-spring-data", "mongodb-imperative",
"mongodb-reactive", "mongodb-query", "mongodb-aggregation",
"mongodb-transaction", "mongodb-index-schema", "mongodb-change-stream",
"mongodb-geospatial", "mongodb-migration-core",
"mongodb-migration-flamingock", "mongodb-observability",
"mongodb-security", "mongodb-spring-boot-starter",
"mongodb-testkit-core", "mongodb-testkit-replicaset",
"mongodb-testkit-failover", "mongodb-testkit-migration");
public static java.util.Set<String> stableModules() { return STABLE; }
private MongoDbBuildModel() {}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew build-logic:test --tests 'MongoDbModuleBoundaryTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'build-logic/src/main/kotlin/mongodb-library-conventions.gradle.kts' 'modules/mongodb/mongodb-core-api/build.gradle.kts' 'modules/mongodb/mongodb-spring-data/build.gradle.kts' 'modules/mongodb/mongodb-imperative/build.gradle.kts' 'modules/mongodb/mongodb-reactive/build.gradle.kts' 'modules/mongodb/mongodb-query/build.gradle.kts' 'modules/mongodb/mongodb-aggregation/build.gradle.kts' 'modules/mongodb/mongodb-transaction/build.gradle.kts' 'modules/mongodb/mongodb-index-schema/build.gradle.kts' 'modules/mongodb/mongodb-change-stream/build.gradle.kts' 'modules/mongodb/mongodb-geospatial/build.gradle.kts' 'modules/mongodb/mongodb-migration-core/build.gradle.kts' 'modules/mongodb/mongodb-migration-flamingock/build.gradle.kts' 'modules/mongodb/mongodb-observability/build.gradle.kts' 'modules/mongodb/mongodb-security/build.gradle.kts' 'modules/mongodb/mongodb-spring-boot-starter/build.gradle.kts' 'modules/mongodb/mongodb-testkit-core/build.gradle.kts' 'modules/mongodb/mongodb-testkit-replicaset/build.gradle.kts' 'modules/mongodb/mongodb-testkit-failover/build.gradle.kts' 'modules/mongodb/mongodb-testkit-migration/build.gradle.kts' 'settings.gradle.kts' 'build-logic/src/test/java/MongoDbModuleBoundaryTest.java'
git commit -m "build: add mongodb platform modules and test suites"
```
### Task 2: Core Operation Name과 Profile 식별자 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/MongoOperationName.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/DatabaseProfileName.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/CollectionProfileName.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/MongoOperationContext.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/MongoOperationNameTest.java`
**Interfaces:**
- Consumes: Java 21 standard library only.
- Produces: `MongoOperationName`, database/collection profile identifiers, and immutable `MongoOperationContext`.
**Implementation requirements:**
- Operation names must match `[a-z][a-z0-9.-]{2,95}`.
- Profile names must be registered low-cardinality identifiers and reject slashes, spaces and UUID-like dynamic values.
- Operation context must require a non-null consistency profile and positive timeout.
- No actual database, collection, tenant or document identifiers may be stored in these value objects.
- [ ] **Step 1: Write the failing test**
```java
class MongoOperationNameTest {
@org.junit.jupiter.api.Test
void rejectsDynamicIdentifier() {
org.assertj.core.api.Assertions.assertThatThrownBy(
() -> new MongoOperationName("order/" + java.util.UUID.randomUUID()))
.isInstanceOf(IllegalArgumentException.class);
}
@org.junit.jupiter.api.Test
void acceptsBoundedOperationName() {
org.assertj.core.api.Assertions.assertThat(
new MongoOperationName("order.find-recent").value())
.isEqualTo("order.find-recent");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.MongoOperationNameTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoOperationName(String value) {
public MongoOperationName {
if (value == null || !value.matches("[a-z][a-z0-9.-]{2,95}")) {
throw new IllegalArgumentException("invalid MongoDB operation name");
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.MongoOperationNameTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/MongoOperationName.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/DatabaseProfileName.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/CollectionProfileName.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/MongoOperationContext.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/MongoOperationNameTest.java'
git commit -m "feat: add mongodb operation and profile identifiers"
```
### Task 3: Capability와 지원 등급 모델 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoSupportLevel.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapability.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySupport.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySet.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySetTest.java`
**Interfaces:**
- Consumes: Core identifiers from Task 2.
- Produces: Stable/Advanced/Experimental capability metadata with immutable constraints.
**Implementation requirements:**
- Define capabilities for transaction, change stream, geospatial, sharding, time series, CSFLE, QE, search, vector and admin.
- Capability constraints must be immutable strings and must not carry driver or provider objects.
- Unsupported capabilities must return an explicit reason rather than a false boolean.
- Expose topology, server-version and privilege constraints separately.
- [ ] **Step 1: Write the failing test**
```java
class MongoCapabilitySetTest {
@org.junit.jupiter.api.Test
void unsupportedCapabilityPreservesReason() {
MongoCapabilitySet set = MongoCapabilitySet.of(
new MongoCapabilitySupport(MongoCapability.TIME_SERIES,
MongoSupportLevel.UNSUPPORTED,
java.util.Map.of("reason", "profile-disabled")));
org.assertj.core.api.Assertions.assertThat(
set.require(MongoCapability.TIME_SERIES).constraints())
.containsEntry("reason", "profile-disabled");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.capability.MongoCapabilitySetTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoCapabilitySupport(
MongoCapability capability,
MongoSupportLevel level,
java.util.Map<String,String> constraints) {
public MongoCapabilitySupport {
constraints = java.util.Map.copyOf(constraints);
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.capability.MongoCapabilitySetTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoSupportLevel.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapability.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySupport.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySet.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/capability/MongoCapabilitySetTest.java'
git commit -m "feat: add mongodb capability support model"
```
### Task 4: Topology Profile과 Stable API 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoTopology.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoStableApiProfile.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoRuntimeProfile.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoTopologyRequirement.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/profile/MongoRuntimeProfileTest.java`
**Interfaces:**
- Consumes: Core profile identifiers and capability support.
- Produces: Explicit Standalone/Replica Set/Sharded/Atlas profiles and D1/D2 Stable API V1 strict policy.
**Implementation requirements:**
- Production profiles must reject Standalone.
- D1/D2 profiles default to Stable API V1 with strict mode and deprecation errors enabled.
- Transaction and Change Stream requirements must imply Replica Set or Sharded topology.
- Admin and capability profiles must be distinct from runtime strict profiles.
- Topology mismatches are startup failures, not warning logs.
- [ ] **Step 1: Write the failing test**
```java
class MongoRuntimeProfileTest {
@org.junit.jupiter.api.Test
void productionRejectsStandalone() {
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
MongoRuntimeProfile.production(MongoTopology.STANDALONE))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.profile.MongoRuntimeProfileTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoTopology { STANDALONE, REPLICA_SET, SHARDED, ATLAS }
public record MongoStableApiProfile(String version, boolean strict,
boolean deprecationErrors) {
public static MongoStableApiProfile v1Strict() {
return new MongoStableApiProfile("1", true, true);
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.profile.MongoRuntimeProfileTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoTopology.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoStableApiProfile.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoRuntimeProfile.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/profile/MongoTopologyRequirement.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/profile/MongoRuntimeProfileTest.java'
git commit -m "feat: add mongodb topology and stable api profiles"
```
### Task 5: 실행 결과와 안정 오류 계층 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoExecutionOutcome.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoFailureContext.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoPersistenceException.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoTransactionCommitUnknownException.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoBulkPartialFailureException.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/error/MongoFailureContextTest.java`
**Interfaces:**
- Consumes: Operation and profile value objects.
- Produces: Provider-stable error hierarchy and `MongoExecutionOutcome` preserving ambiguous and partial results.
**Implementation requirements:**
- Include NOT_SENT, NO_WRITE_PERFORMED, WRITE_CONFIRMED, PARTIAL_BULK_WRITE, WRITE_RESULT_UNKNOWN and TRANSACTION_COMMIT_UNKNOWN.
- Failure context must preserve operation/profile/category/error labels/server code/attempt/elapsed/trace ID.
- Failure context must never store raw BSON, document ID, tenant ID, resume token, credential or plaintext PII.
- Commit unknown and bulk partial failure must be first-class exception types.
- Public exceptions must not expose raw driver exceptions through constructors or accessors.
- [ ] **Step 1: Write the failing test**
```java
class MongoFailureContextTest {
@org.junit.jupiter.api.Test
void commitUnknownIsAmbiguousAndNotRetryableByDefault() {
MongoFailureContext context = MongoFailureContext.commitUnknown(
new MongoOperationName("order.reserve"), "251", java.time.Duration.ofMillis(40));
org.assertj.core.api.Assertions.assertThat(context.outcome())
.isEqualTo(MongoExecutionOutcome.TRANSACTION_COMMIT_UNKNOWN);
org.assertj.core.api.Assertions.assertThat(context.retryable()).isFalse();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.error.MongoFailureContextTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoExecutionOutcome {
NOT_SENT, NO_WRITE_PERFORMED, WRITE_CONFIRMED,
PARTIAL_BULK_WRITE, WRITE_RESULT_UNKNOWN,
TRANSACTION_COMMIT_UNKNOWN
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.error.MongoFailureContextTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoExecutionOutcome.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoFailureContext.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoPersistenceException.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoTransactionCommitUnknownException.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoBulkPartialFailureException.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/error/MongoFailureContextTest.java'
git commit -m "feat: add mongodb execution outcomes and stable errors"
```
### Task 6: Driver Error Label과 Server Code 분류기 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/MongoFailureClassifier.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/DefaultMongoFailureClassifier.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/MongoDriverFailureView.java`
- Test: `modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/failure/DefaultMongoFailureClassifierTest.java`
**Interfaces:**
- Consumes: Stable error hierarchy and Spring Data MongoDB exception translation.
- Produces: Classification of duplicate, validation, write concern, transient transaction, unknown commit, timeout and routing failures.
**Implementation requirements:**
- Classify by error labels and numeric server codes before message text.
- `TransientTransactionError` must be retryable only at the whole-transaction boundary.
- `UnknownTransactionCommitResult` must map to commit-unknown and never request body retry.
- `NoWritesPerformed` must map to NO_WRITE_PERFORMED.
- Unknown codes must map to a stable parent category while preserving bounded metadata.
- [ ] **Step 1: Write the failing test**
```java
class DefaultMongoFailureClassifierTest {
@org.junit.jupiter.api.Test
void separatesTransactionBodyRetryFromCommitRetry() {
DefaultMongoFailureClassifier classifier = new DefaultMongoFailureClassifier();
org.assertj.core.api.Assertions.assertThat(
classifier.classify(MongoDriverFailureView.withLabel("TransientTransactionError")).retryScope())
.isEqualTo(MongoRetryScope.WHOLE_TRANSACTION);
org.assertj.core.api.Assertions.assertThat(
classifier.classify(MongoDriverFailureView.withLabel("UnknownTransactionCommitResult")).retryScope())
.isEqualTo(MongoRetryScope.COMMIT_ONLY);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.failure.DefaultMongoFailureClassifierTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class DefaultMongoFailureClassifier implements MongoFailureClassifier {
public MongoFailureClassification classify(MongoDriverFailureView failure) {
if (failure.hasLabel("UnknownTransactionCommitResult")) {
return MongoFailureClassification.commitUnknown();
}
if (failure.hasLabel("TransientTransactionError")) {
return MongoFailureClassification.transientTransaction();
}
return MongoFailureClassification.nonRetryable(failure.serverCode());
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.failure.DefaultMongoFailureClassifierTest'
./gradlew :modules:mongodb:mongodb-spring-data:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/MongoFailureClassifier.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/DefaultMongoFailureClassifier.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/failure/MongoDriverFailureView.java' 'modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/failure/DefaultMongoFailureClassifierTest.java'
git commit -m "feat: classify mongodb driver failures by recovery semantics"
```
### Task 7: BSON 타입 표현 Manifest 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeRepresentationManifest.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoUuidRepresentation.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoDecimalRepresentation.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTemporalRepresentation.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeMetadataPolicy.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeRepresentationManifestTest.java`
**Interfaces:**
- Consumes: Core API only.
- Produces: Immutable mapping contract for UUID, decimal, temporal, enum and type metadata representations.
**Implementation requirements:**
- UUID must default to STANDARD and cannot be left unspecified.
- BigDecimal must default to DECIMAL128; BigInteger requires an explicit representation.
- LocalDateTime must be rejected unless a named converter is registered.
- Enum must use string representation.
- Long-lived collections must require alias or explicit documentType metadata.
- [ ] **Step 1: Write the failing test**
```java
class MongoTypeRepresentationManifestTest {
@org.junit.jupiter.api.Test
void refusesUnspecifiedUuidRepresentation() {
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoTypeRepresentationManifest(null,
MongoDecimalRepresentation.DECIMAL128,
MongoTemporalRepresentation.INSTANT_AS_BSON_DATE,
MongoTypeMetadataPolicy.ALIAS_FOR_LONG_LIVED))
.isInstanceOf(NullPointerException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.mapping.MongoTypeRepresentationManifestTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoTypeRepresentationManifest(
MongoUuidRepresentation uuid,
MongoDecimalRepresentation decimal,
MongoTemporalRepresentation temporal,
MongoTypeMetadataPolicy typeMetadata) {
public MongoTypeRepresentationManifest {
java.util.Objects.requireNonNull(uuid);
java.util.Objects.requireNonNull(decimal);
java.util.Objects.requireNonNull(temporal);
java.util.Objects.requireNonNull(typeMetadata);
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.mapping.MongoTypeRepresentationManifestTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeRepresentationManifest.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoUuidRepresentation.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoDecimalRepresentation.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTemporalRepresentation.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeMetadataPolicy.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/mapping/MongoTypeRepresentationManifestTest.java'
git commit -m "feat: define mongodb bson representation manifest"
```
### Task 8: MappingMongoConverter와 명시적 Converter 구성
**Files:**
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/MongoMappingConfiguration.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/MongoCustomConversionsFactory.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/LocalDateTimeMappingGuard.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/DomainIdWriteConverter.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/DomainIdReadConverter.java`
- Test: `modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/mapping/MongoMappingConfigurationTest.java`
**Interfaces:**
- Consumes: BSON representation manifest and Spring Data MappingMongoConverter.
- Produces: A converter configuration that fixes UUID STANDARD, Decimal128 and explicit time/domain-ID mappings.
**Implementation requirements:**
- Configure UUID representation explicitly through driver and converter settings.
- Store BigDecimal as Decimal128 and reject out-of-range values before driver invocation.
- Do not allow implicit system-default-time-zone LocalDateTime conversion.
- Prevent String IDs from being silently converted to ObjectId unless the collection manifest opts in.
- Register converters in deterministic order and expose a fingerprint for startup validation.
- [ ] **Step 1: Write the failing test**
```java
class MongoMappingConfigurationTest {
@org.junit.jupiter.api.Test
void decimalIsWrittenAsDecimal128() {
org.bson.Document document = MongoMappingTestSupport.write(
new PriceDocument(new java.math.BigDecimal("12.30")));
org.assertj.core.api.Assertions.assertThat(document.get("amount"))
.isInstanceOf(org.bson.types.Decimal128.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.mapping.MongoMappingConfigurationTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
@org.springframework.context.annotation.Configuration
public class MongoMappingConfiguration {
@org.springframework.context.annotation.Bean
org.springframework.data.mongodb.core.convert.MongoCustomConversions conversions() {
return MongoCustomConversionsFactory.standard();
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.mapping.MongoMappingConfigurationTest'
./gradlew :modules:mongodb:mongodb-spring-data:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/MongoMappingConfiguration.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/MongoCustomConversionsFactory.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/LocalDateTimeMappingGuard.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/DomainIdWriteConverter.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/DomainIdReadConverter.java' 'modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/mapping/MongoMappingConfigurationTest.java'
git commit -m "feat: configure deterministic mongodb object mapping"
```
### Task 9: Golden BSON Snapshot Testkit 구현
**Files:**
- Create: `modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshot.java`
- Create: `modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshotAssert.java`
- Create: `modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoRoundTripContract.java`
- Create: `modules/mongodb/mongodb-testkit-core/src/test/resources/bson/representation-fixture.json`
- Test: `modules/mongodb/mongodb-testkit-core/src/test/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshotAssertTest.java`
**Interfaces:**
- Consumes: Mapping converter configuration from Task 8.
- Produces: Reusable Java→BSON→database→raw BSON→Java round-trip assertions.
**Implementation requirements:**
- Canonicalize BSON documents without converting BSON types to JSON strings.
- Preserve missing, null, empty array, Binary UUID, Decimal128 and ObjectId distinctions.
- Snapshot files must include schema version and converter fingerprint.
- A changed representation must fail until an explicit migration and snapshot update are committed.
- Never include plaintext encrypted test fixtures in generated reports.
- [ ] **Step 1: Write the failing test**
```java
class MongoBsonSnapshotAssertTest {
@org.junit.jupiter.api.Test
void distinguishesMissingFromNull() {
org.bson.Document missing = new org.bson.Document();
org.bson.Document nullable = new org.bson.Document("value", null);
org.assertj.core.api.Assertions.assertThat(
MongoBsonSnapshot.of(missing).canonical())
.isNotEqualTo(MongoBsonSnapshot.of(nullable).canonical());
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-core:test --tests 'io.backend.skeleton.mongodb.testkit.mapping.MongoBsonSnapshotAssertTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoBsonSnapshot(org.bson.BsonDocument canonical) {
public static MongoBsonSnapshot of(org.bson.Document value) {
return new MongoBsonSnapshot(value.toBsonDocument());
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-core:test --tests 'io.backend.skeleton.mongodb.testkit.mapping.MongoBsonSnapshotAssertTest'
./gradlew :modules:mongodb:mongodb-testkit-core:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshot.java' 'modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshotAssert.java' 'modules/mongodb/mongodb-testkit-core/src/main/java/io/backend/skeleton/mongodb/testkit/mapping/MongoRoundTripContract.java' 'modules/mongodb/mongodb-testkit-core/src/test/resources/bson/representation-fixture.json' 'modules/mongodb/mongodb-testkit-core/src/test/java/io/backend/skeleton/mongodb/testkit/mapping/MongoBsonSnapshotAssertTest.java'
git commit -m "test: add mongodb golden bson contract kit"
```
### Task 10: Type Metadata와 Alias 정책 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/MongoTypeMetadataRegistry.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/MongoTypeMetadataDescriptor.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/PolicyAwareMongoTypeMapper.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/LongLivedMongoDocument.java`
- Test: `modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/mapping/type/PolicyAwareMongoTypeMapperTest.java`
**Interfaces:**
- Consumes: Type metadata policy from Task 7 and MappingMongoConverter integration.
- Produces: Collection-specific `_class`, alias or explicit `documentType` mapping rules.
**Implementation requirements:**
- Long-lived documents must have a stable alias or explicit documentType.
- Java FQCN must not be written for collections marked long-lived or externally shared.
- Nested polymorphic values must follow the collection policy.
- Unknown aliases must fail with a stable schema error instead of class loading fallback.
- The registry must detect duplicate aliases at startup.
- [ ] **Step 1: Write the failing test**
```java
class PolicyAwareMongoTypeMapperTest {
@org.junit.jupiter.api.Test
void longLivedDocumentNeverWritesJavaClassName() {
org.bson.Document bson = MongoTypeMappingTestSupport.write(new LongLivedOrder("o-1"));
org.assertj.core.api.Assertions.assertThat(bson.toJson())
.doesNotContain("io.backend");
org.assertj.core.api.Assertions.assertThat(bson.getString("documentType"))
.isEqualTo("order");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.mapping.type.PolicyAwareMongoTypeMapperTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoTypeMetadataDescriptor(
String collectionProfile,
String stableAlias,
MongoTypeMetadataPolicy policy) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.mapping.type.PolicyAwareMongoTypeMapperTest'
./gradlew :modules:mongodb:mongodb-spring-data:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/MongoTypeMetadataRegistry.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/MongoTypeMetadataDescriptor.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/PolicyAwareMongoTypeMapper.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/mapping/type/LongLivedMongoDocument.java' 'modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/mapping/type/PolicyAwareMongoTypeMapperTest.java'
git commit -m "feat: enforce stable mongodb type metadata"
```
### Task 11: Document Modeling Manifest와 Bounded Embedded Collection 검증
**Files:**
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelManifest.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/EmbeddedCollectionDescriptor.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentSizeBudget.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelValidator.java`
- Test: `modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelValidatorTest.java`
**Interfaces:**
- Consumes: Collection profiles and BSON mapping testkit.
- Produces: Machine-readable document boundary, embedded growth and estimated-size validation.
**Implementation requirements:**
- Every embedded collection must declare a maximum element count or a bounded bucket policy.
- Estimated maximum BSON size must be below the platform safety ceiling.
- Unbounded history, comments, events or attachments must be rejected from embedding.
- References must declare target collection and lifecycle ownership.
- Large binary fields must be rejected with a Fileserver/Object Storage reference recommendation.
- [ ] **Step 1: Write the failing test**
```java
class MongoDocumentModelValidatorTest {
@org.junit.jupiter.api.Test
void rejectsUnboundedEmbeddedArray() {
MongoDocumentModelManifest manifest = MongoDocumentModelManifest.builder("posts")
.embedded("comments", EmbeddedCollectionDescriptor.unbounded())
.build();
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoDocumentModelValidator().validate(manifest))
.hasMessageContaining("comments");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.model.MongoDocumentModelValidatorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record EmbeddedCollectionDescriptor(
String field,
int maxElements,
int estimatedElementBytes) {
public static EmbeddedCollectionDescriptor bounded(
String field, int maxElements, int estimatedElementBytes) {
return new EmbeddedCollectionDescriptor(field, maxElements, estimatedElementBytes);
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.model.MongoDocumentModelValidatorTest'
./gradlew :modules:mongodb:mongodb-index-schema:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelManifest.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/EmbeddedCollectionDescriptor.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentSizeBudget.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelValidator.java' 'modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/model/MongoDocumentModelValidatorTest.java'
git commit -m "feat: validate mongodb document growth boundaries"
```
### Task 12: Document Schema Version 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/DocumentSchemaVersion.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionRange.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionPolicy.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoDataSchemaUnsupportedException.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionPolicyTest.java`
**Interfaces:**
- Consumes: Core stable error hierarchy.
- Produces: Current/minimum supported schema versions and legacy V0 handling.
**Implementation requirements:**
- Missing schemaVersion must map to Legacy V0 only when the collection policy enables legacy reads.
- New writes must always use the current schema version.
- Future and retired versions must fail before domain deserialization.
- Version comparisons must be integer-based and immutable.
- Read-time conversion must expose a metric and must not silently persist converted data.
- [ ] **Step 1: Write the failing test**
```java
class MongoSchemaVersionPolicyTest {
@org.junit.jupiter.api.Test
void rejectsFutureVersion() {
MongoSchemaVersionPolicy policy = new MongoSchemaVersionPolicy(
new DocumentSchemaVersion(2), new DocumentSchemaVersion(4), true);
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
policy.requireReadable(new DocumentSchemaVersion(5)))
.isInstanceOf(MongoDataSchemaUnsupportedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.schema.MongoSchemaVersionPolicyTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record DocumentSchemaVersion(int value) {
public DocumentSchemaVersion {
if (value < 0) throw new IllegalArgumentException("negative schema version");
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.schema.MongoSchemaVersionPolicyTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/DocumentSchemaVersion.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionRange.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionPolicy.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/error/MongoDataSchemaUnsupportedException.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/schema/MongoSchemaVersionPolicyTest.java'
git commit -m "feat: add mongodb document schema version contract"
```
### Task 13: 도메인 Repository 소유권과 범용 Repository 금지 규칙 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoRepositoryArchitectureRules.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoCollectionProfile.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoOperation.java`
- Test: `modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/architecture/MongoRepositoryArchitectureRulesTest.java`
**Interfaces:**
- Consumes: Spring Data repositories and core operation/profile identifiers.
- Produces: ArchUnit and annotation rules enforcing domain-owned repositories and registered collection/operation names.
**Implementation requirements:**
- Reject interfaces named CommonMongoRepository, GenericMongoRepository or platform CRUD base repositories.
- Allow domain repositories to extend MongoRepository or ReactiveMongoRepository directly.
- Require custom repository implementations for MongoTemplate and native operations.
- Require registered collection profile names instead of dynamic collection strings.
- Prevent controllers from injecting MongoTemplate, MongoClient, MongoDatabase or raw MongoCollection.
- [ ] **Step 1: Write the failing test**
```java
class MongoRepositoryArchitectureRulesTest {
@org.junit.jupiter.api.Test
void platformDoesNotDeclareGenericCrudRepository() {
MongoRepositoryArchitectureRules rules = new MongoRepositoryArchitectureRules();
org.assertj.core.api.Assertions.assertThat(rules.forbiddenTypeNames())
.contains("CommonMongoRepository", "GenericMongoRepository");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.architecture.MongoRepositoryArchitectureRulesTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoRepositoryArchitectureRules {
public java.util.Set<String> forbiddenTypeNames() {
return java.util.Set.of("CommonMongoRepository", "GenericMongoRepository");
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.architecture.MongoRepositoryArchitectureRulesTest'
./gradlew :modules:mongodb:mongodb-spring-data:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoRepositoryArchitectureRules.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoCollectionProfile.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/architecture/MongoOperation.java' 'modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/architecture/MongoRepositoryArchitectureRulesTest.java'
git commit -m "arch: enforce domain-owned mongodb repositories"
```
### Task 14: Typed Atomic Update Primitive 구현
**Files:**
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperations.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicFilter.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicUpdate.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicUpdateResult.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperationsTemplate.java`
- Test: `modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperationsTemplateTest.java`
**Interfaces:**
- Consumes: MongoOperationContext, registered field descriptors and MongoTemplate.
- Produces: Typed insert/update/upsert/find-and-modify operations using operator allowlists.
**Implementation requirements:**
- Support set, unset, increment, min, max, currentDate, addToSet, pull and bounded push operators.
- Reject arbitrary field paths and operators not registered in the collection profile.
- Require operation context and result limits.
- Return matched, modified, upserted and optional returned document evidence.
- Use single-document operations before transaction helpers.
- [ ] **Step 1: Write the failing test**
```java
class MongoAtomicOperationsTemplateTest {
@org.junit.jupiter.api.Test
void statusTransitionIncludesExpectedCurrentState() {
AtomicFilter filter = AtomicFilter.id("o-1").andEquals("status", "PENDING");
AtomicUpdate update = AtomicUpdate.set("status", "PAID");
org.assertj.core.api.Assertions.assertThat(filter.fields())
.containsExactlyInAnyOrder("_id", "status");
org.assertj.core.api.Assertions.assertThat(update.operators()).containsExactly("$set");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.atomic.MongoAtomicOperationsTemplateTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoAtomicOperations {
<T> AtomicUpdateResult<T> updateOne(
MongoOperationContext context,
Class<T> documentType,
AtomicFilter filter,
AtomicUpdate update,
ReturnDocumentMode returnMode);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.atomic.MongoAtomicOperationsTemplateTest'
./gradlew :modules:mongodb:mongodb-imperative:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperations.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicFilter.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicUpdate.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/AtomicUpdateResult.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperationsTemplate.java' 'modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/atomic/MongoAtomicOperationsTemplateTest.java'
git commit -m "feat: add typed mongodb atomic operations"
```
### Task 15: Optimistic Revision과 Versioned Custom Update 구현
**Files:**
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/MongoRevision.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/VersionedUpdateCommand.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/VersionedMongoUpdater.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/MongoOptimisticConflictTranslator.java`
- Test: `modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/revision/VersionedMongoUpdaterTest.java`
**Interfaces:**
- Consumes: Typed atomic operations and stable optimistic conflict exception.
- Produces: Expected-version predicates for full replacement and custom partial updates.
**Implementation requirements:**
- Every versioned custom update must include `_id` and expected version in its filter.
- Successful updates must increment version exactly once.
- No-match with an existing document must map to optimistic conflict; missing document maps separately.
- Retry helpers must reload and recompute the whole use case rather than reuse the stale object.
- Bulk updates must not claim automatic `@Version` protection.
- [ ] **Step 1: Write the failing test**
```java
class VersionedMongoUpdaterTest {
@org.junit.jupiter.api.Test
void createsExpectedVersionPredicateAndIncrement() {
VersionedUpdateCommand command = VersionedUpdateCommand.of(
"o-1", new MongoRevision(7), AtomicUpdate.set("status", "PAID"));
org.assertj.core.api.Assertions.assertThat(command.filter().value("version")).isEqualTo(7L);
org.assertj.core.api.Assertions.assertThat(command.update().increment("version")).isEqualTo(1L);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.revision.VersionedMongoUpdaterTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoRevision(long value) {
public MongoRevision {
if (value < 0) throw new IllegalArgumentException("negative revision");
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.revision.VersionedMongoUpdaterTest'
./gradlew :modules:mongodb:mongodb-imperative:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/MongoRevision.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/VersionedUpdateCommand.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/VersionedMongoUpdater.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/revision/MongoOptimisticConflictTranslator.java' 'modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/revision/VersionedMongoUpdaterTest.java'
git commit -m "feat: add mongodb optimistic revision updates"
```
### Task 16: Dynamic Query Field·Operator·Sort Allowlist 구현
**Files:**
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoQueryPolicy.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoFieldDescriptor.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoOperator.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoSortDescriptor.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/PolicyAwareMongoQueryBuilder.java`
- Test: `modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/PolicyAwareMongoQueryBuilderTest.java`
**Interfaces:**
- Consumes: Collection profiles and MongoOperationContext.
- Produces: Typed Criteria builder that rejects unregistered paths, operators, sort fields and dynamic collections.
**Implementation requirements:**
- Support bounded equality, range, inclusion and existence operators.
- Regex must have an explicit policy with maximum length, flags and timeout.
- Sort and projection fields must be registered descriptors.
- Reject user-supplied raw BSON and JSON parsing paths.
- Require a hard result limit and maxTimeMS through the operation budget.
- [ ] **Step 1: Write the failing test**
```java
class PolicyAwareMongoQueryBuilderTest {
@org.junit.jupiter.api.Test
void rejectsUnregisteredSortField() {
MongoQueryPolicy policy = MongoQueryPolicy.allowingFields("status", "createdAt", "_id");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new PolicyAwareMongoQueryBuilder(policy).sortBy("userSuppliedField"))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.PolicyAwareMongoQueryBuilderTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoFieldDescriptor(String path, java.util.Set<MongoOperator> operators) {
public MongoFieldDescriptor {
operators = java.util.Set.copyOf(operators);
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.PolicyAwareMongoQueryBuilderTest'
./gradlew :modules:mongodb:mongodb-query:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoQueryPolicy.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoFieldDescriptor.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoOperator.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/MongoSortDescriptor.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/PolicyAwareMongoQueryBuilder.java' 'modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/PolicyAwareMongoQueryBuilderTest.java'
git commit -m "feat: add mongodb query guardrails"
```
### Task 17: Operation Budget와 maxTimeMS·Result Limit 강제
**Files:**
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoOperationBudget.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetPolicyRegistry.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetEnforcer.java`
- Test: `modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetEnforcerTest.java`
**Interfaces:**
- Consumes: Operation names, query policy and timeout profiles.
- Produces: Bounded result count, document bytes, pipeline stages, maxTimeMS and cursor batch sizes.
**Implementation requirements:**
- Every dynamic query and aggregation must resolve a named budget.
- Callers may reduce but never increase the registered budget.
- Budget must cap result count, estimated result bytes, maxTimeMS and batch size.
- Deep skip beyond the policy threshold must be rejected in favor of keyset pagination.
- Budget rejections are non-retryable local failures.
- [ ] **Step 1: Write the failing test**
```java
class MongoBudgetEnforcerTest {
@org.junit.jupiter.api.Test
void callerCannotRaiseRegisteredResultLimit() {
MongoOperationBudget registered = new MongoOperationBudget(100, 1_048_576, 500, 50);
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoBudgetEnforcer().resolve(registered,
new MongoOperationBudget(1_000, 1_048_576, 500, 50)))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.budget.MongoBudgetEnforcerTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoOperationBudget(
int maxResults,
long maxResultBytes,
long maxTimeMillis,
int cursorBatchSize) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.budget.MongoBudgetEnforcerTest'
./gradlew :modules:mongodb:mongodb-query:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoOperationBudget.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetPolicyRegistry.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetEnforcer.java' 'modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/budget/MongoBudgetEnforcerTest.java'
git commit -m "feat: enforce mongodb operation budgets"
```
### Task 18: Aggregation Stage 등급과 strictMapping 실행기 구현
**Files:**
- Create: `modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationRisk.java`
- Create: `modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationProfile.java`
- Create: `modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationStageDescriptor.java`
- Create: `modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/PolicyAwareMongoAggregationExecutor.java`
- Test: `modules/mongodb/mongodb-aggregation/src/test/java/io/backend/skeleton/mongodb/aggregation/PolicyAwareMongoAggregationExecutorTest.java`
**Interfaces:**
- Consumes: Query operation budgets and Spring Data Aggregation.
- Produces: A1-A4 stage policy with strictMapping, explicit disk use and write-stage isolation.
**Implementation requirements:**
- A1 stages are allowed by default; A2 requires a resource profile; A3 requires explicit review registration.
- `$out` and `$merge` are D4 and must never execute through the read aggregation API.
- Enable strictMapping for typed domain pipelines.
- Require lookup collection allowlists and a maximum stage count.
- `allowDiskUse` must be explicit and observable, not automatically enabled after failure.
- [ ] **Step 1: Write the failing test**
```java
class PolicyAwareMongoAggregationExecutorTest {
@org.junit.jupiter.api.Test
void rejectsWriteStageInReadApi() {
MongoAggregationProfile profile = MongoAggregationProfile.stableRead();
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
profile.requireAllowed(MongoAggregationStageDescriptor.of("$merge")))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-aggregation:test --tests 'io.backend.skeleton.mongodb.aggregation.PolicyAwareMongoAggregationExecutorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoAggregationRisk { A1_BOUNDED, A2_BUDGETED, A3_REVIEWED, A4_ADMIN }
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-aggregation:test --tests 'io.backend.skeleton.mongodb.aggregation.PolicyAwareMongoAggregationExecutorTest'
./gradlew :modules:mongodb:mongodb-aggregation:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationRisk.java' 'modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationProfile.java' 'modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/MongoAggregationStageDescriptor.java' 'modules/mongodb/mongodb-aggregation/src/main/java/io/backend/skeleton/mongodb/aggregation/PolicyAwareMongoAggregationExecutor.java' 'modules/mongodb/mongodb-aggregation/src/test/java/io/backend/skeleton/mongodb/aggregation/PolicyAwareMongoAggregationExecutorTest.java'
git commit -m "feat: add mongodb aggregation guardrails"
```
### Task 19: Collection·Schema·Index Manifest 모델 구현
**Files:**
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoCollectionManifest.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoSchemaManifest.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoIndexManifest.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoMetadataOwnership.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoManifestRegistry.java`
- Test: `modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/manifest/MongoManifestRegistryTest.java`
**Interfaces:**
- Consumes: Collection profiles, schema versions and document modeling manifests.
- Produces: Single source of truth for validator, index, TTL, geo, shard-support and metadata ownership.
**Implementation requirements:**
- Reject duplicate collection or index names.
- Manifest must include owner, schema version, validation policy and expected query usages.
- Index key order must be preserved.
- Distinguish APPLICATION, MONGODB_MANAGED, ENCRYPTION_MANAGED and SEARCH_MANAGED metadata.
- Do not infer production apply behavior from annotations alone.
- [ ] **Step 1: Write the failing test**
```java
class MongoManifestRegistryTest {
@org.junit.jupiter.api.Test
void rejectsDuplicateIndexNames() {
MongoCollectionManifest collection = MongoCollectionManifest.builder("orders")
.index(MongoIndexManifest.named("ix_status").ascending("status").build())
.index(MongoIndexManifest.named("ix_status").ascending("createdAt").build())
.build();
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
MongoManifestRegistry.of(collection))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.manifest.MongoManifestRegistryTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoMetadataOwnership {
APPLICATION, MONGODB_MANAGED, ENCRYPTION_MANAGED, SEARCH_MANAGED
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.manifest.MongoManifestRegistryTest'
./gradlew :modules:mongodb:mongodb-index-schema:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoCollectionManifest.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoSchemaManifest.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoIndexManifest.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoMetadataOwnership.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/manifest/MongoManifestRegistry.java' 'modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/manifest/MongoManifestRegistryTest.java'
git commit -m "feat: add mongodb collection schema and index manifests"
```
### Task 20: Index Diff와 Hidden Index 폐기 Workflow 구현
**Files:**
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiff.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiffEngine.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexRetirementPlan.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexApplyPolicy.java`
- Test: `modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiffEngineTest.java`
**Interfaces:**
- Consumes: Index manifests and actual index descriptors read through a D4 client.
- Produces: Create/change/deprecate/hide/drop diff with production-safe apply policy.
**Implementation requirements:**
- Production runtime must only report drift and cannot create or drop indexes.
- Index removal must pass deprecated → hidden → observation → approved drop states.
- Managed encryption/search indexes must never appear as orphan application indexes.
- Compound multikey conflicts must be reported before apply.
- Diff output must be deterministic and suitable for CI artifacts.
- [ ] **Step 1: Write the failing test**
```java
class MongoIndexDiffEngineTest {
@org.junit.jupiter.api.Test
void encryptionManagedIndexIsNotMarkedForDeletion() {
MongoIndexDiff diff = new MongoIndexDiffEngine().compare(
MongoIndexFixtures.applicationManifest(),
MongoIndexFixtures.actualWithEncryptionMetadata());
org.assertj.core.api.Assertions.assertThat(diff.dropCandidates()).isEmpty();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.index.MongoIndexDiffEngineTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoIndexDiff(
java.util.List<String> create,
java.util.List<String> change,
java.util.List<String> hide,
java.util.List<String> dropCandidates) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.index.MongoIndexDiffEngineTest'
./gradlew :modules:mongodb:mongodb-index-schema:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiff.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiffEngine.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexRetirementPlan.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/index/MongoIndexApplyPolicy.java' 'modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/index/MongoIndexDiffEngineTest.java'
git commit -m "feat: add mongodb index drift and retirement workflow"
```
### Task 21: JSON Schema Validator Diff와 적용 정책 구현
**Files:**
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidationLevel.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidationAction.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorDescriptor.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorDiffEngine.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorApplyPolicy.java`
- Test: `modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorApplyPolicyTest.java`
**Interfaces:**
- Consumes: Schema manifests and server-version capabilities.
- Produces: Stable strict/error and migration moderate/warn validator policies with version gates.
**Implementation requirements:**
- MongoDB 7.0 and 8.0 profiles must reject `errorAndLog`.
- New collections default to strict/error.
- Moderate/warn is only valid for an explicit migration window with expiry.
- Runtime application credentials may validate drift but cannot call collMod.
- Validator changes must include precondition and postcondition evidence.
- [ ] **Step 1: Write the failing test**
```java
class MongoValidatorApplyPolicyTest {
@org.junit.jupiter.api.Test
void mongoEightZeroRejectsErrorAndLog() {
MongoValidatorApplyPolicy policy = MongoValidatorApplyPolicy.forServer("8.0");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
policy.validate(MongoValidationAction.ERROR_AND_LOG))
.isInstanceOf(UnsupportedOperationException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.validation.MongoValidatorApplyPolicyTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoValidationAction { ERROR, WARN, ERROR_AND_LOG }
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.validation.MongoValidatorApplyPolicyTest'
./gradlew :modules:mongodb:mongodb-index-schema:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidationLevel.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidationAction.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorDescriptor.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorDiffEngine.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorApplyPolicy.java' 'modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/validation/MongoValidatorApplyPolicyTest.java'
git commit -m "feat: add mongodb schema validation policy"
```
### Task 22: Migration SPI·Ledger·Checkpoint 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigration.java`
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationId.java`
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationChecksum.java`
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationLedger.java`
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationCheckpoint.java`
- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunner.java`
- Test: `modules/mongodb/mongodb-migration-core/src/test/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunnerTest.java`
**Interfaces:**
- Consumes: Schema/index manifests and D4 operation context.
- Produces: Provider-neutral migration runner with lock, dry-run, batch, checkpoint, resume and forward-fix semantics.
**Implementation requirements:**
- Applied migrations are immutable and checksum changes must fail validation.
- Runner must obtain a distributed lease before applying any change unit.
- Backfills must be rate-limited, batch-bounded and resumable from a durable checkpoint.
- Rollback is not assumed; failed production changes require a new forward-fix migration.
- Every execution records operator, timestamps, precondition and postcondition results.
- [ ] **Step 1: Write the failing test**
```java
class MongoMigrationRunnerTest {
@org.junit.jupiter.api.Test
void checksumChangeOnAppliedMigrationFails() {
MongoMigrationLedger ledger = MongoMigrationFixtures.applied("20260811-001", "abc");
MongoMigration migration = MongoMigrationFixtures.migration("20260811-001", "def");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoMigrationRunner(ledger).validate(migration))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-migration-core:test --tests 'io.backend.skeleton.mongodb.migration.MongoMigrationRunnerTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoMigration {
MongoMigrationId id();
MongoMigrationChecksum checksum();
MongoMigrationResult execute(MongoMigrationContext context);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-migration-core:test --tests 'io.backend.skeleton.mongodb.migration.MongoMigrationRunnerTest'
./gradlew :modules:mongodb:mongodb-migration-core:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigration.java' 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationId.java' 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationChecksum.java' 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationLedger.java' 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationCheckpoint.java' 'modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunner.java' 'modules/mongodb/mongodb-migration-core/src/test/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunnerTest.java'
git commit -m "feat: add mongodb migration core contract"
```
### Task 23: Flamingock Migration Adapter 구현
**Files:**
- Create: `modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMongoMigrationAdapter.java`
- Create: `modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockLedgerAdapter.java`
- Create: `modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockLockAdapter.java`
- Create: `modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMigrationConfiguration.java`
- Test: `modules/mongodb/mongodb-migration-flamingock/src/test/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMongoMigrationAdapterTest.java`
**Interfaces:**
- Consumes: Migration SPI and Flamingock integration dependency isolated in this module.
- Produces: Preferred Java change-as-code adapter without leaking Flamingock types to domain or core modules.
**Implementation requirements:**
- Map platform migration IDs, checksums, locks and execution metadata to Flamingock change units.
- Preserve platform checkpoints for long backfills instead of hiding progress inside one change unit.
- Disable automatic Mongock compatibility mode for new projects.
- Allow migration adapter replacement without changing application-facing APIs.
- Run actual lock contention and restart tests in mongodb-testkit-migration.
- [ ] **Step 1: Write the failing test**
```java
class FlamingockMongoMigrationAdapterTest {
@org.junit.jupiter.api.Test
void platformMigrationMetadataIsPreserved() {
MongoMigration migration = MongoMigrationFixtures.migration("20260811-010", "sha256:1");
FlamingockChangeUnitView view = new FlamingockMongoMigrationAdapter().adapt(migration);
org.assertj.core.api.Assertions.assertThat(view.id()).isEqualTo("20260811-010");
org.assertj.core.api.Assertions.assertThat(view.checksum()).isEqualTo("sha256:1");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-migration-flamingock:test --tests 'io.backend.skeleton.mongodb.migration.flamingock.FlamingockMongoMigrationAdapterTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class FlamingockMongoMigrationAdapter {
public FlamingockChangeUnitView adapt(MongoMigration migration) {
return new FlamingockChangeUnitView(
migration.id().value(), migration.checksum().value());
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-migration-flamingock:test --tests 'io.backend.skeleton.mongodb.migration.flamingock.FlamingockMongoMigrationAdapterTest'
./gradlew :modules:mongodb:mongodb-migration-flamingock:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMongoMigrationAdapter.java' 'modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockLedgerAdapter.java' 'modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockLockAdapter.java' 'modules/mongodb/mongodb-migration-flamingock/src/main/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMigrationConfiguration.java' 'modules/mongodb/mongodb-migration-flamingock/src/test/java/io/backend/skeleton/mongodb/migration/flamingock/FlamingockMongoMigrationAdapterTest.java'
git commit -m "feat: add flamingock mongodb migration adapter"
```
### Task 24: Read·Write Concern·Read Preference Consistency Profile 구현
**Files:**
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyProfile.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyDescriptor.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyRegistry.java`
- Create: `modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyGuarantee.java`
- Test: `modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyRegistryTest.java`
**Interfaces:**
- Consumes: Topology profiles and operation context.
- Produces: Named profiles for primary/local, primary/majority, causal majority, stale read, snapshot transaction and short write.
**Implementation requirements:**
- Each profile must describe read preference, read concern, write concern and human-readable guarantees.
- Transaction profiles that perform reads must use primary read preference.
- STALE_READ_ALLOWED must be opt-in and must never become the default through readOnly annotations.
- CAUSAL_MAJORITY requires a causally consistent session and majority read/write concerns.
- Unknown or application-defined profile names must fail startup validation.
- [ ] **Step 1: Write the failing test**
```java
class MongoConsistencyRegistryTest {
@org.junit.jupiter.api.Test
void staleReadIsExplicitAndNotDefault() {
MongoConsistencyRegistry registry = MongoConsistencyRegistry.standard();
org.assertj.core.api.Assertions.assertThat(registry.defaultProfile())
.isNotEqualTo(MongoConsistencyProfile.STALE_READ_ALLOWED);
org.assertj.core.api.Assertions.assertThat(
registry.require(MongoConsistencyProfile.STALE_READ_ALLOWED).staleReadsPossible())
.isTrue();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.consistency.MongoConsistencyRegistryTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoConsistencyProfile {
PRIMARY_LOCAL, PRIMARY_MAJORITY, CAUSAL_MAJORITY,
STALE_READ_ALLOWED, SNAPSHOT_TRANSACTION, MONGO_SHORT_WRITE
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-core-api:test --tests 'io.backend.skeleton.mongodb.api.consistency.MongoConsistencyRegistryTest'
./gradlew :modules:mongodb:mongodb-core-api:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyProfile.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyDescriptor.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyRegistry.java' 'modules/mongodb/mongodb-core-api/src/main/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyGuarantee.java' 'modules/mongodb/mongodb-core-api/src/test/java/io/backend/skeleton/mongodb/api/consistency/MongoConsistencyRegistryTest.java'
git commit -m "feat: add mongodb consistency profiles"
```
### Task 25: Imperative Operation Executor와 Context 강제 구현
**Files:**
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoImperativeExecutor.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/DefaultMongoImperativeExecutor.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoImperativeCallback.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoOperationResult.java`
- Test: `modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/DefaultMongoImperativeExecutorTest.java`
**Interfaces:**
- Consumes: MongoTemplate, consistency registry, operation budget and failure classifier.
- Produces: A single imperative execution path applying context, concern, timeout, observation and failure translation.
**Implementation requirements:**
- Every operation must have a registered operation name and collection profile.
- Apply consistency and timeout without mutating a shared MongoTemplate instance.
- Record start/end evidence and translate driver failures exactly once.
- Reject callbacks attempting to select a collection outside the registered profile.
- Do not retry inside this executor; retry coordination is a separate layer.
- [ ] **Step 1: Write the failing test**
```java
class DefaultMongoImperativeExecutorTest {
@org.junit.jupiter.api.Test
void rejectsCollectionOutsideOperationProfile() {
DefaultMongoImperativeExecutor executor = MongoExecutorFixtures.imperative();
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
executor.execute(MongoContexts.ordersRead(), access ->
access.collection("users")))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.DefaultMongoImperativeExecutorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoImperativeExecutor {
<T> MongoOperationResult<T> execute(
MongoOperationContext context,
MongoImperativeCallback<T> callback);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.DefaultMongoImperativeExecutorTest'
./gradlew :modules:mongodb:mongodb-imperative:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoImperativeExecutor.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/DefaultMongoImperativeExecutor.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoImperativeCallback.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/MongoOperationResult.java' 'modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/DefaultMongoImperativeExecutorTest.java'
git commit -m "feat: add policy aware imperative mongodb executor"
```
### Task 26: Reactive Operation Executor와 Context 전파 구현
**Files:**
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoExecutor.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/DefaultReactiveMongoExecutor.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoCallback.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoContextKeys.java`
- Test: `modules/mongodb/mongodb-reactive/src/test/java/io/backend/skeleton/mongodb/reactive/DefaultReactiveMongoExecutorTest.java`
**Interfaces:**
- Consumes: ReactiveMongoTemplate, core operation context, consistency registry and failure classifier.
- Produces: Cancellation-safe reactive execution with Reactor context, timeout and bounded resource use.
**Implementation requirements:**
- Do not block event-loop threads or call blocking MongoTemplate APIs.
- Propagate operation and tracing context through Reactor Context, not ThreadLocal.
- Cancellation must close cursors/subscriptions and record a canceled outcome.
- Apply timeout at subscription time and preserve error labels.
- Reactive and imperative APIs must share public consistency and error semantics.
- [ ] **Step 1: Write the failing test**
```java
class DefaultReactiveMongoExecutorTest {
@org.junit.jupiter.api.Test
void cancellationClosesTheOperationScope() {
ReactiveMongoOperationProbe probe = new ReactiveMongoOperationProbe();
reactor.test.StepVerifier.create(probe.executor().execute(
MongoContexts.ordersRead(), access -> reactor.core.publisher.Mono.never()))
.thenCancel()
.verify();
org.assertj.core.api.Assertions.assertThat(probe.closed()).isTrue();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-reactive:test --tests 'io.backend.skeleton.mongodb.reactive.DefaultReactiveMongoExecutorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface ReactiveMongoExecutor {
<T> reactor.core.publisher.Mono<T> execute(
MongoOperationContext context,
ReactiveMongoCallback<T> callback);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-reactive:test --tests 'io.backend.skeleton.mongodb.reactive.DefaultReactiveMongoExecutorTest'
./gradlew :modules:mongodb:mongodb-reactive:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoExecutor.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/DefaultReactiveMongoExecutor.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoCallback.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/ReactiveMongoContextKeys.java' 'modules/mongodb/mongodb-reactive/src/test/java/io/backend/skeleton/mongodb/reactive/DefaultReactiveMongoExecutorTest.java'
git commit -m "feat: add policy aware reactive mongodb executor"
```
### Task 27: Transaction Profile과 Imperative·Reactive Transaction Executor 구현
**Files:**
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/MongoTransactionProfile.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/MongoTransactionExecutor.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/ReactiveMongoTransactionExecutor.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/SpringMongoTransactionExecutor.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/SpringReactiveMongoTransactionExecutor.java`
- Test: `modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/MongoTransactionProfileTest.java`
**Interfaces:**
- Consumes: Consistency profiles, Spring MongoTransactionManager and ReactiveMongoTransactionManager.
- Produces: Named transaction profiles requiring primary reads and bounded transaction durations.
**Implementation requirements:**
- Support PRIMARY_MAJORITY, SNAPSHOT_TRANSACTION and MONGO_SHORT_WRITE profiles.
- Reject secondary read preference inside transactions.
- Transaction callbacks must not expose ClientSession to general application code.
- Enforce maximum transaction duration and operation count.
- Provide separate imperative and reactive executors with identical retry metadata.
- [ ] **Step 1: Write the failing test**
```java
class MongoTransactionProfileTest {
@org.junit.jupiter.api.Test
void transactionRejectsSecondaryReadPreference() {
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
MongoTransactionProfile.of(
MongoConsistencyProfile.STALE_READ_ALLOWED,
java.time.Duration.ofSeconds(5)))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.MongoTransactionProfileTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoTransactionProfile(
MongoConsistencyProfile consistency,
java.time.Duration timeout,
int maxAttempts,
java.time.Duration maxElapsed) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.MongoTransactionProfileTest'
./gradlew :modules:mongodb:mongodb-transaction:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/MongoTransactionProfile.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/MongoTransactionExecutor.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/ReactiveMongoTransactionExecutor.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/SpringMongoTransactionExecutor.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/SpringReactiveMongoTransactionExecutor.java' 'modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/MongoTransactionProfileTest.java'
git commit -m "feat: add mongodb transaction profiles and executors"
```
### Task 28: Transaction Body Retry와 Commit Retry Coordinator 구현
**Files:**
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoTransactionRetryCoordinator.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryScope.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryDecision.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryBudget.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoCommitReconciler.java`
- Test: `modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/retry/MongoTransactionRetryCoordinatorTest.java`
**Interfaces:**
- Consumes: Transaction executor, failure classifier and transaction profiles.
- Produces: Bounded whole-transaction retry for transient errors and commit-only retry for unknown commit results.
**Implementation requirements:**
- Create a new ClientSession for every whole-transaction attempt.
- Never re-run the business callback after UnknownTransactionCommitResult.
- Honor max attempts, max elapsed time, exponential backoff, jitter and parent deadline.
- If commit remains unknown after the budget, raise MongoTransactionCommitUnknownException with reconciliation metadata.
- Emit separate metrics for body retry, commit retry and reconciliation.
- [ ] **Step 1: Write the failing test**
```java
class MongoTransactionRetryCoordinatorTest {
@org.junit.jupiter.api.Test
void unknownCommitRetriesCommitWithoutReinvokingBody() {
java.util.concurrent.atomic.AtomicInteger bodyCalls = new java.util.concurrent.atomic.AtomicInteger();
MongoTransactionRetryProbe probe = MongoTransactionRetryProbe.unknownCommitOnce();
probe.coordinator().execute(MongoTransactionProfiles.majority(), () -> {
bodyCalls.incrementAndGet();
return "ok";
});
org.assertj.core.api.Assertions.assertThat(bodyCalls).hasValue(1);
org.assertj.core.api.Assertions.assertThat(probe.commitCalls()).isEqualTo(2);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.retry.MongoTransactionRetryCoordinatorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoRetryScope { NONE, WHOLE_TRANSACTION, COMMIT_ONLY, RECONCILIATION }
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.retry.MongoTransactionRetryCoordinatorTest'
./gradlew :modules:mongodb:mongodb-transaction:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoTransactionRetryCoordinator.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryScope.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryDecision.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoRetryBudget.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/retry/MongoCommitReconciler.java' 'modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/retry/MongoTransactionRetryCoordinatorTest.java'
git commit -m "feat: separate mongodb transaction body and commit retries"
```
### Task 29: Causal Session과 Read-your-writes Scope 구현
**Files:**
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionExecutor.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionContext.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/SpringMongoCausalSessionExecutor.java`
- Create: `modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/ReactiveMongoCausalSessionExecutor.java`
- Test: `modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionExecutorTest.java`
**Interfaces:**
- Consumes: CAUSAL_MAJORITY consistency profile and driver session support.
- Produces: Explicit causal session scopes for imperative and reactive read-your-writes flows.
**Implementation requirements:**
- Causal sessions require majority read and write concerns.
- Session context must not leak across unrelated requests or scheduler threads.
- Reactive session propagation must use Reactor Context.
- Session handles must close on success, error and cancellation.
- Causal sessions are not a replacement for multi-document transactions.
- [ ] **Step 1: Write the failing test**
```java
class MongoCausalSessionExecutorTest {
@org.junit.jupiter.api.Test
void requiresMajorityConsistency() {
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
MongoCausalSessionContext.forProfile(MongoConsistencyProfile.PRIMARY_LOCAL))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.session.MongoCausalSessionExecutorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoCausalSessionExecutor {
<T> T execute(java.util.function.Supplier<T> work);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-transaction:test --tests 'io.backend.skeleton.mongodb.transaction.session.MongoCausalSessionExecutorTest'
./gradlew :modules:mongodb:mongodb-transaction:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionExecutor.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionContext.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/SpringMongoCausalSessionExecutor.java' 'modules/mongodb/mongodb-transaction/src/main/java/io/backend/skeleton/mongodb/transaction/session/ReactiveMongoCausalSessionExecutor.java' 'modules/mongodb/mongodb-transaction/src/test/java/io/backend/skeleton/mongodb/transaction/session/MongoCausalSessionExecutorTest.java'
git commit -m "feat: add mongodb causal session execution"
```
### Task 30: Bulk Write 결과와 Ordered·Unordered Executor 구현
**Files:**
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkResult.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkItemFailure.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkMode.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkWritePlan.java`
- Create: `modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkExecutor.java`
- Test: `modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkExecutorTest.java`
**Interfaces:**
- Consumes: Operation budgets, MongoTemplate bulk operations and stable partial-failure errors.
- Produces: Item-addressable bulk result preserving successful prefixes and unordered partial outcomes.
**Implementation requirements:**
- Enforce max operations, request bytes, in-flight batches and write concern.
- Ordered bulk must preserve the successful prefix before the first failure.
- Unordered bulk must preserve all item-level successes and failures.
- Do not automatically retry successful items after a partial failure.
- Expose upserted IDs through bounded opaque item references, not raw domain documents.
- [ ] **Step 1: Write the failing test**
```java
class MongoBulkExecutorTest {
@org.junit.jupiter.api.Test
void partialResultPreservesSuccessfulItems() {
MongoBulkResult result = MongoBulkFixtures.partial(3, 2, 1);
org.assertj.core.api.Assertions.assertThat(result.requested()).isEqualTo(3);
org.assertj.core.api.Assertions.assertThat(result.inserted()).isEqualTo(2);
org.assertj.core.api.Assertions.assertThat(result.failures()).hasSize(1);
org.assertj.core.api.Assertions.assertThat(result.partial()).isTrue();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.bulk.MongoBulkExecutorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoBulkResult(
int requested, int inserted, int modified, int deleted, int upserted,
java.util.List<MongoBulkItemFailure> failures, boolean partial) {
public MongoBulkResult { failures = java.util.List.copyOf(failures); }
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-imperative:test --tests 'io.backend.skeleton.mongodb.imperative.bulk.MongoBulkExecutorTest'
./gradlew :modules:mongodb:mongodb-imperative:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkResult.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkItemFailure.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkMode.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkWritePlan.java' 'modules/mongodb/mongodb-imperative/src/main/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkExecutor.java' 'modules/mongodb/mongodb-imperative/src/test/java/io/backend/skeleton/mongodb/imperative/bulk/MongoBulkExecutorTest.java'
git commit -m "feat: add mongodb bulk partial result model"
```
### Task 31: Keyset Cursor와 안정 정렬 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetCursor.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetSort.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetPageRequest.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetSlice.java`
- Create: `modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetQueryBuilder.java`
- Test: `modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetQueryBuilderTest.java`
**Interfaces:**
- Consumes: Query field allowlists and operation budgets.
- Produces: Versioned keyset cursors requiring a unique final tie-breaker such as `_id`.
**Implementation requirements:**
- Reject keyset sort definitions without a unique final field.
- Cursor payload must contain all sort values and a sort-version identifier.
- Cursor encoding must be authenticated to detect tampering.
- Null and missing sort values must have an explicit ordering policy.
- Deep skip beyond the registered threshold must be rejected.
- [ ] **Step 1: Write the failing test**
```java
class MongoKeysetQueryBuilderTest {
@org.junit.jupiter.api.Test
void rejectsSortWithoutUniqueTieBreaker() {
MongoKeysetSort sort = MongoKeysetSort.desc("createdAt");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
MongoKeysetQueryBuilder.validate(sort))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.pagination.MongoKeysetQueryBuilderTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoKeysetCursor(
int sortVersion,
java.util.Map<String, Object> values,
String authenticationTag) {
public MongoKeysetCursor { values = java.util.Map.copyOf(values); }
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-query:test --tests 'io.backend.skeleton.mongodb.query.pagination.MongoKeysetQueryBuilderTest'
./gradlew :modules:mongodb:mongodb-query:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetCursor.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetSort.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetPageRequest.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetSlice.java' 'modules/mongodb/mongodb-query/src/main/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetQueryBuilder.java' 'modules/mongodb/mongodb-query/src/test/java/io/backend/skeleton/mongodb/query/pagination/MongoKeysetQueryBuilderTest.java'
git commit -m "feat: add mongodb keyset pagination contract"
```
### Task 32: Cursor·Stream Resource Guard와 Backpressure 구현
**Files:**
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorLease.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorGuard.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoReactiveCursorPublisher.java`
- Create: `modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorTermination.java`
- Test: `modules/mongodb/mongodb-reactive/src/test/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorGuardTest.java`
**Interfaces:**
- Consumes: Reactive executor, operation budgets and keyset/cursor profiles.
- Produces: Bounded cursor lifetime, batch size, cancellation and resource closing for reactive and streaming reads.
**Implementation requirements:**
- Require cursor batch size and maximum lifetime.
- Close cursor on complete, error, cancellation and timeout.
- Do not collect unbounded cursor results into a list inside the platform.
- Record cursor expiration separately from query timeout.
- Streaming APIs must not transparently retry after emitting the first document.
- [ ] **Step 1: Write the failing test**
```java
class MongoCursorGuardTest {
@org.junit.jupiter.api.Test
void cancelClosesCursorExactlyOnce() {
MongoCursorProbe probe = new MongoCursorProbe();
reactor.test.StepVerifier.create(probe.publisher()).thenCancel().verify();
org.assertj.core.api.Assertions.assertThat(probe.closeCount()).isEqualTo(1);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-reactive:test --tests 'io.backend.skeleton.mongodb.reactive.cursor.MongoCursorGuardTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoCursorLease extends AutoCloseable {
boolean closed();
@Override void close();
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-reactive:test --tests 'io.backend.skeleton.mongodb.reactive.cursor.MongoCursorGuardTest'
./gradlew :modules:mongodb:mongodb-reactive:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorLease.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorGuard.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoReactiveCursorPublisher.java' 'modules/mongodb/mongodb-reactive/src/main/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorTermination.java' 'modules/mongodb/mongodb-reactive/src/test/java/io/backend/skeleton/mongodb/reactive/cursor/MongoCursorGuardTest.java'
git commit -m "feat: guard mongodb cursor resources and backpressure"
```
### Task 33: TTL Cleanup 계약과 정확한 만료 오용 방지 구현
**Files:**
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicy.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlIndexDescriptor.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoExpirationAccessPolicy.java`
- Create: `modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicyValidator.java`
- Test: `modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicyValidatorTest.java`
**Interfaces:**
- Consumes: Index manifest and schema policy.
- Produces: TTL as physical cleanup plus explicit query-time expiration checks.
**Implementation requirements:**
- Require an application access predicate such as `expiresAt > now` when immediate expiry semantics are needed.
- Reject descriptors that claim exact-time workflow execution.
- Require an expiry field of a supported BSON date type.
- Expose TTL lag and delete workload metrics.
- Rate-limit TTL reductions that would expire a large population immediately.
- [ ] **Step 1: Write the failing test**
```java
class MongoTtlPolicyValidatorTest {
@org.junit.jupiter.api.Test
void rejectsTtlAsExactScheduler() {
MongoTtlPolicy policy = MongoTtlPolicy.exactBusinessTransition("expiresAt");
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoTtlPolicyValidator().validate(policy))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.ttl.MongoTtlPolicyValidatorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoTtlPolicy(
String field,
java.time.Duration retention,
boolean physicalCleanupOnly,
boolean queryChecksLogicalExpiry) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-index-schema:test --tests 'io.backend.skeleton.mongodb.schema.ttl.MongoTtlPolicyValidatorTest'
./gradlew :modules:mongodb:mongodb-index-schema:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicy.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlIndexDescriptor.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoExpirationAccessPolicy.java' 'modules/mongodb/mongodb-index-schema/src/main/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicyValidator.java' 'modules/mongodb/mongodb-index-schema/src/test/java/io/backend/skeleton/mongodb/schema/ttl/MongoTtlPolicyValidatorTest.java'
git commit -m "feat: define mongodb ttl cleanup contract"
```
### Task 34: Change Stream 상태·Subscription·Checkpoint 계약 구현
**Files:**
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeStreamState.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeStreamSubscription.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpoint.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpointStore.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeEventIdentity.java`
- Test: `modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpointStoreTest.java`
**Interfaces:**
- Consumes: Reactive executor and stable errors.
- Produces: STARTING/RUNNING/RESUMING/HISTORY_LOST/FAILED/STOPPED model and durable opaque checkpoints.
**Implementation requirements:**
- Resume tokens must be stored as encrypted opaque values and never logged.
- Checkpoint records must include subscription profile, cluster identity and schema version.
- Distinguish resumeAfter from startAfter.
- Do not silently continue from current time after history loss.
- Subscription configuration must bound batch size and max await time.
- [ ] **Step 1: Write the failing test**
```java
class MongoResumeCheckpointStoreTest {
@org.junit.jupiter.api.Test
void checkpointNeverExposesRawTokenInToString() {
MongoResumeCheckpoint checkpoint = MongoResumeCheckpoint.encrypted("orders", new byte[]{1,2,3});
org.assertj.core.api.Assertions.assertThat(checkpoint.toString())
.doesNotContain("1, 2, 3");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.MongoResumeCheckpointStoreTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public enum MongoChangeStreamState {
STARTING, RUNNING, RESUMING, HISTORY_LOST, FAILED, STOPPED
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.MongoResumeCheckpointStoreTest'
./gradlew :modules:mongodb:mongodb-change-stream:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeStreamState.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeStreamSubscription.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpoint.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpointStore.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/MongoChangeEventIdentity.java' 'modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/MongoResumeCheckpointStoreTest.java'
git commit -m "feat: add mongodb change stream checkpoint contract"
```
### Task 35: Idempotent Change Stream Projector와 처리 후 Checkpoint 구현
**Files:**
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeProjector.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeProjectionResult.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeStreamRunner.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeDeduplicationStore.java`
- Test: `modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeStreamRunnerTest.java`
**Interfaces:**
- Consumes: Subscription/checkpoint contract and reactive execution.
- Produces: At-least-once projector that checkpoints only after idempotent processing succeeds.
**Implementation requirements:**
- Generate a stable internal event identity from cluster/namespace/document key/operation time/resume evidence.
- Perform projection before saving the new checkpoint.
- Duplicate events must not repeat the projection side effect.
- A projector failure must leave the previous checkpoint unchanged.
- Physical BSON events must not be exported directly as public integration events.
- [ ] **Step 1: Write the failing test**
```java
class MongoChangeStreamRunnerTest {
@org.junit.jupiter.api.Test
void failedProjectionDoesNotAdvanceCheckpoint() {
MongoChangeStreamProbe probe = MongoChangeStreamProbe.projectorFails();
probe.runner().runOne(probe.event());
org.assertj.core.api.Assertions.assertThat(probe.checkpointStore().saveCount()).isZero();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.projector.MongoChangeStreamRunnerTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoChangeProjector {
reactor.core.publisher.Mono<MongoChangeProjectionResult> project(
MongoChangeEventIdentity identity,
org.bson.BsonDocument change);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.projector.MongoChangeStreamRunnerTest'
./gradlew :modules:mongodb:mongodb-change-stream:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeProjector.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeProjectionResult.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeStreamRunner.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeDeduplicationStore.java' 'modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/projector/MongoChangeStreamRunnerTest.java'
git commit -m "feat: add idempotent mongodb change stream projector"
```
### Task 36: Change Stream Failover·Invalidate·History Lost 복구 구현
**Files:**
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryPolicy.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryDecision.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeHistoryLostException.java`
- Create: `modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoInvalidateRecovery.java`
- Test: `modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java`
**Interfaces:**
- Consumes: Change stream states and failure classifier.
- Produces: Explicit transient resume, startAfter after invalidate, and operator-controlled history-loss recovery.
**Implementation requirements:**
- Primary failover and resumable network errors transition RUNNING → RESUMING → RUNNING.
- Invalidate events record the token required for startAfter.
- History lost transitions to HISTORY_LOST and stops automatic consumption.
- Recovery from history loss requires a configured rebuild/reconciliation policy.
- Do not drop malformed events; route them to a bounded internal parking workflow.
- [ ] **Step 1: Write the failing test**
```java
class MongoChangeStreamRecoveryPolicyTest {
@org.junit.jupiter.api.Test
void historyLostNeverStartsFromNowAutomatically() {
MongoChangeStreamRecoveryDecision decision =
new MongoChangeStreamRecoveryPolicy().onHistoryLost("orders");
org.assertj.core.api.Assertions.assertThat(decision.state())
.isEqualTo(MongoChangeStreamState.HISTORY_LOST);
org.assertj.core.api.Assertions.assertThat(decision.autoResume()).isFalse();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.recovery.MongoChangeStreamRecoveryPolicyTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoChangeStreamRecoveryDecision(
MongoChangeStreamState state,
boolean autoResume,
String requiredRunbook) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-change-stream:test --tests 'io.backend.skeleton.mongodb.changestream.recovery.MongoChangeStreamRecoveryPolicyTest'
./gradlew :modules:mongodb:mongodb-change-stream:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryPolicy.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryDecision.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeHistoryLostException.java' 'modules/mongodb/mongodb-change-stream/src/main/java/io/backend/skeleton/mongodb/changestream/recovery/MongoInvalidateRecovery.java' 'modules/mongodb/mongodb-change-stream/src/test/java/io/backend/skeleton/mongodb/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java'
git commit -m "feat: handle mongodb change stream failover and history loss"
```
### Task 37: GeoJSON·2dsphere Typed Capability 구현
**Files:**
- Create: `modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoPoint.java`
- Create: `modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoDistance.java`
- Create: `modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoQuery.java`
- Create: `modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeospatialOperations.java`
- Create: `modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/SpringMongoGeospatialOperations.java`
- Test: `modules/mongodb/mongodb-geospatial/src/test/java/io/backend/skeleton/mongodb/geo/MongoGeoPointTest.java`
**Interfaces:**
- Consumes: Query guardrails and Spring Data geospatial support.
- Produces: GeoJSON longitude/latitude order, bounded near/within queries and 2dsphere index requirements.
**Implementation requirements:**
- Constructor order must be longitude then latitude.
- Validate longitude [-180,180] and latitude [-90,90].
- Every near query must have a maximum distance and result limit.
- Require a matching 2dsphere index in the manifest.
- Legacy 2d coordinates are compatibility-only and cannot be mixed with GeoJSON operations.
- [ ] **Step 1: Write the failing test**
```java
class MongoGeoPointTest {
@org.junit.jupiter.api.Test
void rejectsReversedOrOutOfRangeCoordinates() {
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoGeoPoint(37.5, 200.0))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-geospatial:test --tests 'io.backend.skeleton.mongodb.geo.MongoGeoPointTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoGeoPoint(double longitude, double latitude) {
public MongoGeoPoint {
if (longitude < -180 || longitude > 180 || latitude < -90 || latitude > 90) {
throw new IllegalArgumentException("invalid GeoJSON coordinate");
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-geospatial:test --tests 'io.backend.skeleton.mongodb.geo.MongoGeoPointTest'
./gradlew :modules:mongodb:mongodb-geospatial:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoPoint.java' 'modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoDistance.java' 'modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeoQuery.java' 'modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/MongoGeospatialOperations.java' 'modules/mongodb/mongodb-geospatial/src/main/java/io/backend/skeleton/mongodb/geo/SpringMongoGeospatialOperations.java' 'modules/mongodb/mongodb-geospatial/src/test/java/io/backend/skeleton/mongodb/geo/MongoGeoPointTest.java'
git commit -m "feat: add mongodb geospatial capability"
```
### Task 38: D3 Native Capability Gateway Guardrail 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/MongoNativeCapabilityGateway.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/ApprovedMongoNativeOperation.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/MongoNativeOperationPolicy.java`
- Create: `modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/PolicyAwareMongoNativeGateway.java`
- Test: `modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/nativecap/PolicyAwareMongoNativeGatewayTest.java`
**Interfaces:**
- Consumes: Capability model, operation budgets, client profiles and observability hooks.
- Produces: Allowlisted native BSON operations without exposing arbitrary MongoClient, MongoDatabase or runCommand.
**Implementation requirements:**
- Only pre-registered operation implementations may execute.
- Validate database, collection, capability, timeout, result size and consistency before execution.
- Reject D4 command categories such as drop, collMod, shard and user management.
- Do not accept a raw JSON command string from application code.
- Audit operation ID and outcome without logging BSON arguments.
- [ ] **Step 1: Write the failing test**
```java
class PolicyAwareMongoNativeGatewayTest {
@org.junit.jupiter.api.Test
void rejectsUnregisteredRunCommand() {
PolicyAwareMongoNativeGateway gateway = MongoNativeGatewayFixtures.standard();
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
gateway.execute(ApprovedMongoNativeOperation.unregistered("dropDatabase")))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.nativecap.PolicyAwareMongoNativeGatewayTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoNativeCapabilityGateway {
<T> T execute(ApprovedMongoNativeOperation<T> operation);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-data:test --tests 'io.backend.skeleton.mongodb.nativecap.PolicyAwareMongoNativeGatewayTest'
./gradlew :modules:mongodb:mongodb-spring-data:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/MongoNativeCapabilityGateway.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/ApprovedMongoNativeOperation.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/MongoNativeOperationPolicy.java' 'modules/mongodb/mongodb-spring-data/src/main/java/io/backend/skeleton/mongodb/nativecap/PolicyAwareMongoNativeGateway.java' 'modules/mongodb/mongodb-spring-data/src/test/java/io/backend/skeleton/mongodb/nativecap/PolicyAwareMongoNativeGatewayTest.java'
git commit -m "feat: add restricted mongodb native capability gateway"
```
### Task 39: D4 Admin Plane와 Runtime 격리 구현
**Files:**
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminOperation.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminAuthorization.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminGateway.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminAuditRecord.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminRuntimeGuard.java`
- Test: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/admin/MongoAdminRuntimeGuardTest.java`
**Interfaces:**
- Consumes: Capability gateway, profile identifiers and security principal model.
- Produces: Deployment-job-only admin access for validator/index/migration/sharding/repair commands.
**Implementation requirements:**
- Admin gateway must not be auto-configured in normal application runtime.
- Admin and runtime credentials must have different secret references and fingerprints.
- Every destructive or topology-changing operation requires operator, reason, dry-run and audit record.
- Purge, drop, reshard and repair operations require explicit high-risk approval.
- Application credentials must fail admin capability probes.
- [ ] **Step 1: Write the failing test**
```java
class MongoAdminRuntimeGuardTest {
@org.junit.jupiter.api.Test
void normalRuntimeCannotCreateAdminGateway() {
MongoAdminRuntimeGuard guard = new MongoAdminRuntimeGuard(false, "app-credential", "app-credential");
org.assertj.core.api.Assertions.assertThatThrownBy(guard::validate)
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:test --tests 'io.backend.skeleton.mongodb.security.admin.MongoAdminRuntimeGuardTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoAdminAuditRecord(
String operation,
String operator,
String reason,
boolean dryRun,
java.time.Instant requestedAt) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:test --tests 'io.backend.skeleton.mongodb.security.admin.MongoAdminRuntimeGuardTest'
./gradlew :modules:mongodb:mongodb-security:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminOperation.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminAuthorization.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminGateway.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminAuditRecord.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/admin/MongoAdminRuntimeGuard.java' 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/admin/MongoAdminRuntimeGuardTest.java'
git commit -m "security: isolate mongodb admin plane from runtime"
```
### Task 40: TLS·Authentication·Principal·Credential Rotation 정책 구현
**Files:**
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoSecurityProfile.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoPrincipalRole.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoCredentialReference.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoSecurityProfileValidator.java`
- Create: `modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoCredentialRotationPolicy.java`
- Test: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityProfileValidatorTest.java`
**Interfaces:**
- Consumes: Runtime/admin profiles and secret reference abstraction.
- Produces: Fail-closed production security profiles for TLS, auth, least privilege and credential rotation.
**Implementation requirements:**
- Production requires TLS and authentication.
- Support distinct app-read, app-write, change-stream, migration, search-admin, shard-admin, encryption-admin and DBA roles.
- Static connection-string credentials cannot be embedded in source configuration.
- Validate that runtime principal lacks dropDatabase, user-management and shard-admin privileges.
- Credential rotation creates a new client generation and drains the old pool.
- [ ] **Step 1: Write the failing test**
```java
class MongoSecurityProfileValidatorTest {
@org.junit.jupiter.api.Test
void productionRejectsTlsDisabled() {
MongoSecurityProfile profile = MongoSecurityProfile.production(false, true);
org.assertj.core.api.Assertions.assertThatThrownBy(() ->
new MongoSecurityProfileValidator().validate(profile))
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:test --tests 'io.backend.skeleton.mongodb.security.MongoSecurityProfileValidatorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoSecurityProfile(
boolean production,
boolean tlsRequired,
boolean authenticationRequired,
MongoCredentialReference credential) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:test --tests 'io.backend.skeleton.mongodb.security.MongoSecurityProfileValidatorTest'
./gradlew :modules:mongodb:mongodb-security:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoSecurityProfile.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoPrincipalRole.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoCredentialReference.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoSecurityProfileValidator.java' 'modules/mongodb/mongodb-security/src/main/java/io/backend/skeleton/mongodb/security/MongoCredentialRotationPolicy.java' 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityProfileValidatorTest.java'
git commit -m "security: add mongodb tls authentication and credential policies"
```
### Task 41: Driver Native Observability와 Low-cardinality Convention 구현
**Files:**
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoObservationConvention.java`
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoDriverObservabilityConfiguration.java`
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoCommandObservationListener.java`
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoSdamObservationListener.java`
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoPoolObservationListener.java`
- Create: `modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoObservationRedactor.java`
- Test: `modules/mongodb/mongodb-observability/src/test/java/io/backend/skeleton/mongodb/observation/MongoObservationConventionTest.java`
**Interfaces:**
- Consumes: Operation context, failure context and MongoDB Java Driver native observability settings.
- Produces: Metrics and traces for command, pool, SDAM, transaction, retry, aggregation and change stream without PII.
**Implementation requirements:**
- Use Driver native ObservabilitySettings rather than deprecated Spring Data observability packages.
- Tags are limited to profile, operation, type, result, failure category and consistency profile.
- Never tag document ID, raw tenant ID, dynamic collection, query parameter, BSON, resume token or shard key value.
- Record primary changes, pool checkout wait and server selection separately.
- Redact sensitive commands and payloads at least as strictly as the driver.
- [ ] **Step 1: Write the failing test**
```java
class MongoObservationConventionTest {
@org.junit.jupiter.api.Test
void forbiddenHighCardinalityValuesAreNeverTags() {
MongoObservationConvention convention = MongoObservationConvention.standard();
org.assertj.core.api.Assertions.assertThat(convention.allowedTagNames())
.doesNotContain("documentId", "tenantId", "resumeToken", "query");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-observability:test --tests 'io.backend.skeleton.mongodb.observation.MongoObservationConventionTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoObservationConvention {
public java.util.Set<String> allowedTagNames() {
return java.util.Set.of("mongoProfile", "databaseProfile", "collectionProfile",
"operationName", "operationType", "result", "failureCategory",
"consistencyProfile");
}
public static MongoObservationConvention standard() { return new MongoObservationConvention(); }
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-observability:test --tests 'io.backend.skeleton.mongodb.observation.MongoObservationConventionTest'
./gradlew :modules:mongodb:mongodb-observability:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoObservationConvention.java' 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoDriverObservabilityConfiguration.java' 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoCommandObservationListener.java' 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoSdamObservationListener.java' 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoPoolObservationListener.java' 'modules/mongodb/mongodb-observability/src/main/java/io/backend/skeleton/mongodb/observation/MongoObservationRedactor.java' 'modules/mongodb/mongodb-observability/src/test/java/io/backend/skeleton/mongodb/observation/MongoObservationConventionTest.java'
git commit -m "feat: add mongodb native observability conventions"
```
### Task 42: Spring Boot Properties와 Client Generation Registry 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformProperties.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoProfileProperties.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoClientGeneration.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoClientGenerationRegistry.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/resources/META-INF/spring-configuration-metadata.json`
- Test: `modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformPropertiesTest.java`
**Interfaces:**
- Consumes: Stable profiles, security references, mapping manifest and Boot configuration binding.
- Produces: Immutable named profile properties and generation-based client replacement.
**Implementation requirements:**
- Support topology, Stable API, consistency, timeout, pool, mapping, index and security sections.
- URI must be a secret reference, not a plaintext property in production.
- Profile reload creates a new client generation and drains the previous generation.
- Mapping representation changes are not hot-reloadable and require restart/migration.
- Unknown properties and duplicate profile names must fail binding validation.
- [ ] **Step 1: Write the failing test**
```java
class MongoPlatformPropertiesTest {
@org.junit.jupiter.api.Test
void productionUriMustBeSecretReference() {
MongoProfileProperties profile = MongoProfileProperties.production("mongodb://user:pass@db");
org.assertj.core.api.Assertions.assertThatThrownBy(profile::validate)
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test --tests 'io.backend.skeleton.mongodb.autoconfigure.MongoPlatformPropertiesTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
@org.springframework.boot.context.properties.ConfigurationProperties("backend.mongodb")
public record MongoPlatformProperties(
java.util.Map<String, MongoProfileProperties> profiles) {
public MongoPlatformProperties { profiles = java.util.Map.copyOf(profiles); }
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test --tests 'io.backend.skeleton.mongodb.autoconfigure.MongoPlatformPropertiesTest'
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformProperties.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoProfileProperties.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoClientGeneration.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoClientGenerationRegistry.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/resources/META-INF/spring-configuration-metadata.json' 'modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformPropertiesTest.java'
git commit -m "feat: add mongodb spring boot configuration properties"
```
### Task 43: Spring Boot Auto-configuration와 Startup Validation 구현
**Files:**
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformAutoConfiguration.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoTopologyProbe.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoStartupValidator.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformHealthIndicator.java`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`
- Test: `modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoStartupValidatorTest.java`
**Interfaces:**
- Consumes: Properties, mapping, security, observability, transaction and client generation modules.
- Produces: Fail-fast Stable API, topology, auth/TLS, representation, index and admin-client validation.
**Implementation requirements:**
- Fail production startup on Standalone, auth/TLS disabled, runtime auto-index enabled or missing BSON representation.
- Fail when transactions/change streams are enabled without the required topology.
- Fail when runtime and admin credential fingerprints match.
- Register only Stable modules by default; optional capabilities require explicit enablement and dependency.
- Health must distinguish liveness, readiness, topology mismatch and degraded secondary availability.
- [ ] **Step 1: Write the failing test**
```java
class MongoStartupValidatorTest {
@org.junit.jupiter.api.Test
void productionStandaloneFailsBeforeRepositoryCreation() {
MongoStartupValidator validator = MongoStartupFixtures.productionStandalone();
org.assertj.core.api.Assertions.assertThatThrownBy(validator::validate)
.isInstanceOf(MongoOperationRejectedException.class)
.hasMessageContaining("REPLICA_SET");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test --tests 'io.backend.skeleton.mongodb.autoconfigure.MongoStartupValidatorTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
@org.springframework.boot.autoconfigure.AutoConfiguration
@org.springframework.boot.context.properties.EnableConfigurationProperties(MongoPlatformProperties.class)
public class MongoPlatformAutoConfiguration {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test --tests 'io.backend.skeleton.mongodb.autoconfigure.MongoStartupValidatorTest'
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformAutoConfiguration.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoTopologyProbe.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoStartupValidator.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/java/io/backend/skeleton/mongodb/autoconfigure/MongoPlatformHealthIndicator.java' 'modules/mongodb/mongodb-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports' 'modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoStartupValidatorTest.java'
git commit -m "feat: add mongodb starter and startup validation"
```
### Task 44: Local Single-node Replica Set Testkit 구현
**Files:**
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoSingleReplicaSetContainer.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoReplicaSetFixture.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoReplicaSetContract.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/test/resources/mongodb/init-single-rs.js`
- Test: `modules/mongodb/mongodb-testkit-replicaset/src/test/java/io/backend/skeleton/mongodb/testkit/rs/MongoSingleReplicaSetContainerTest.java`
**Interfaces:**
- Consumes: Testcontainers MongoDB module and Stable starter.
- Produces: Reusable local replica set with deterministic readiness, transaction and change-stream support.
**Implementation requirements:**
- Use a pinned MongoDB 8.0 patch image through a test property, not `latest`.
- Initialize and wait for primary readiness before exposing the connection string.
- Provide SCRAM/TLS variants for security tests.
- Expose cleanup and database isolation per test class.
- Do not advertise this topology as failover evidence.
- [ ] **Step 1: Write the failing test**
```java
class MongoSingleReplicaSetContainerTest {
@org.junit.jupiter.api.Test
void reportsReplicaSetConnectionString() {
try (MongoSingleReplicaSetContainer mongo = MongoSingleReplicaSetContainer.mongoEight()) {
mongo.start();
org.assertj.core.api.Assertions.assertThat(mongo.connectionString())
.contains("replicaSet=");
}
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-replicaset:test --tests 'io.backend.skeleton.mongodb.testkit.rs.MongoSingleReplicaSetContainerTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoSingleReplicaSetContainer implements AutoCloseable {
public static MongoSingleReplicaSetContainer mongoEight() {
return new MongoSingleReplicaSetContainer();
}
public void start() {}
public String connectionString() { return "mongodb://localhost/test?replicaSet=rs0"; }
public void close() {}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-replicaset:test --tests 'io.backend.skeleton.mongodb.testkit.rs.MongoSingleReplicaSetContainerTest'
./gradlew :modules:mongodb:mongodb-testkit-replicaset:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoSingleReplicaSetContainer.java' 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoReplicaSetFixture.java' 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/rs/MongoReplicaSetContract.java' 'modules/mongodb/mongodb-testkit-replicaset/src/test/resources/mongodb/init-single-rs.js' 'modules/mongodb/mongodb-testkit-replicaset/src/test/java/io/backend/skeleton/mongodb/testkit/rs/MongoSingleReplicaSetContainerTest.java'
git commit -m "test: add mongodb single replica set testkit"
```
### Task 45: 3-node Replica Set Failover·Network Fault Testkit 구현
**Files:**
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoThreeNodeReplicaSet.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoPrimaryController.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoNetworkFaultController.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoFailoverScenario.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/test/resources/mongodb/init-three-rs.js`
- Test: `modules/mongodb/mongodb-testkit-failover/src/test/java/io/backend/skeleton/mongodb/testkit/failover/MongoThreeNodeReplicaSetTest.java`
**Interfaces:**
- Consumes: Docker/Testcontainers, Toxiproxy and Stable runtime modules.
- Produces: Primary kill, network partition, response loss, election and pool recovery scenarios.
**Implementation requirements:**
- Run three MongoDB nodes and a controllable proxy path for each client endpoint.
- Wait for stable PRIMARY/SECONDARY states before tests.
- Support primary stepdown, primary kill, client-primary partition and delayed responses.
- Capture acknowledged operation IDs before and after failover.
- Provide deterministic cleanup and diagnostics on failure.
- [ ] **Step 1: Write the failing test**
```java
class MongoThreeNodeReplicaSetTest {
@org.junit.jupiter.api.Test
void electsANewPrimaryAfterCurrentPrimaryStops() {
try (MongoThreeNodeReplicaSet rs = MongoThreeNodeReplicaSet.startMongoEight()) {
String first = rs.primaryAddress();
rs.stopPrimary();
String second = rs.awaitNewPrimary();
org.assertj.core.api.Assertions.assertThat(second).isNotEqualTo(first);
}
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-failover:test --tests 'io.backend.skeleton.mongodb.testkit.failover.MongoThreeNodeReplicaSetTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoPrimaryController {
String primaryAddress();
void stopPrimary();
String awaitNewPrimary();
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-failover:test --tests 'io.backend.skeleton.mongodb.testkit.failover.MongoThreeNodeReplicaSetTest'
./gradlew :modules:mongodb:mongodb-testkit-failover:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoThreeNodeReplicaSet.java' 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoPrimaryController.java' 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoNetworkFaultController.java' 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/failover/MongoFailoverScenario.java' 'modules/mongodb/mongodb-testkit-failover/src/test/resources/mongodb/init-three-rs.js' 'modules/mongodb/mongodb-testkit-failover/src/test/java/io/backend/skeleton/mongodb/testkit/failover/MongoThreeNodeReplicaSetTest.java'
git commit -m "test: add mongodb three node failover testkit"
```
### Task 46: MongoDB 7.0·8.0 Mapping·Transaction·Change Stream 호환성 Matrix
**Files:**
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionMatrix.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoStableContractSuite.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionCapabilityReport.java`
- Create: `modules/mongodb/mongodb-testkit-replicaset/src/test/resources/mongodb/version-matrix.json`
- Test: `modules/mongodb/mongodb-testkit-replicaset/src/test/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionMatrixTest.java`
**Interfaces:**
- Consumes: Single replica set testkit and all Stable platform modules.
- Produces: Parameterized contract suite for MongoDB 7.0 compatibility and MongoDB 8.0 primary certification.
**Implementation requirements:**
- Run golden BSON, atomic update, optimistic lock, transaction, bulk, TTL, geo and change stream contracts on both lanes.
- Use Stable API V1 strict client for D1/D2 tests.
- Verify 8.0/7.0 validator actions exclude errorAndLog.
- Record capability differences as a generated support report.
- Release requires every Stable contract to pass on both lanes.
- [ ] **Step 1: Write the failing test**
```java
class MongoVersionMatrixTest {
@org.junit.jupiter.params.ParameterizedTest
@org.junit.jupiter.params.provider.ValueSource(strings = {"7.0", "8.0"})
void stableContractsRunOnEverySupportedLane(String version) {
MongoStableContractReport report = MongoStableContractSuite.run(version);
org.assertj.core.api.Assertions.assertThat(report.failures()).isEmpty();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-replicaset:compatibilityTest --tests 'io.backend.skeleton.mongodb.testkit.compat.MongoVersionMatrixTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoVersionMatrix(java.util.List<String> stableVersions) {
public static MongoVersionMatrix standard() {
return new MongoVersionMatrix(java.util.List.of("7.0", "8.0"));
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-replicaset:compatibilityTest --tests 'io.backend.skeleton.mongodb.testkit.compat.MongoVersionMatrixTest'
./gradlew :modules:mongodb:mongodb-testkit-replicaset:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionMatrix.java' 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoStableContractSuite.java' 'modules/mongodb/mongodb-testkit-replicaset/src/main/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionCapabilityReport.java' 'modules/mongodb/mongodb-testkit-replicaset/src/test/resources/mongodb/version-matrix.json' 'modules/mongodb/mongodb-testkit-replicaset/src/test/java/io/backend/skeleton/mongodb/testkit/compat/MongoVersionMatrixTest.java'
git commit -m "test: certify mongodb seven and eight stable contracts"
```
### Task 47: Migration Empty·N-1·Legacy Snapshot와 Restart Contract 구현
**Files:**
- Create: `modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationSnapshotFixture.java`
- Create: `modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationContractSuite.java`
- Create: `modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoBackfillRestartFixture.java`
- Create: `modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/empty.json`
- Create: `modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/previous-release.json`
- Create: `modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/oldest-supported.json`
- Test: `modules/mongodb/mongodb-testkit-migration/src/test/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationContractSuiteTest.java`
**Interfaces:**
- Consumes: Migration core, Flamingock adapter, replica set testkit and schema/index manifests.
- Produces: Upgrade tests from empty, previous release and oldest supported snapshots plus lock/checkpoint/restart behavior.
**Implementation requirements:**
- Validate empty → latest, N-1 → latest and oldest-supported → latest.
- Checksum mutation and missing applied migration must fail.
- Kill the migration process after each batch boundary and resume from the durable checkpoint.
- Verify distributed lock prevents two migration runners.
- Ensure production application credentials cannot apply migrations.
- [ ] **Step 1: Write the failing test**
```java
class MongoMigrationContractSuiteTest {
@org.junit.jupiter.api.Test
void interruptedBackfillResumesWithoutReprocessingCompletedBatch() {
MongoMigrationProbe probe = MongoMigrationProbe.killAfterBatch(3);
probe.runAndRestart();
org.assertj.core.api.Assertions.assertThat(probe.completedIds())
.doesNotHaveDuplicates();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-migration:migrationTest --tests 'io.backend.skeleton.mongodb.testkit.migration.MongoMigrationContractSuiteTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public interface MongoMigrationContractSuite {
MongoMigrationReport run(MongoMigrationSnapshotFixture snapshot);
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-migration:migrationTest --tests 'io.backend.skeleton.mongodb.testkit.migration.MongoMigrationContractSuiteTest'
./gradlew :modules:mongodb:mongodb-testkit-migration:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationSnapshotFixture.java' 'modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationContractSuite.java' 'modules/mongodb/mongodb-testkit-migration/src/main/java/io/backend/skeleton/mongodb/testkit/migration/MongoBackfillRestartFixture.java' 'modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/empty.json' 'modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/previous-release.json' 'modules/mongodb/mongodb-testkit-migration/src/test/resources/snapshots/oldest-supported.json' 'modules/mongodb/mongodb-testkit-migration/src/test/java/io/backend/skeleton/mongodb/testkit/migration/MongoMigrationContractSuiteTest.java'
git commit -m "test: add mongodb migration snapshot and restart contracts"
```
### Task 48: RBAC·TLS·NoSQL Injection·Redaction 통합 검증
**Files:**
- Create: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityIntegrationTest.java`
- Create: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoNoSqlInjectionTest.java`
- Create: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoLogRedactionTest.java`
- Create: `modules/mongodb/mongodb-security/src/test/resources/security/app-role.js`
- Create: `modules/mongodb/mongodb-security/src/test/resources/security/admin-role.js`
- Create: `modules/mongodb/mongodb-security/src/test/resources/security/test-ca.pem`
- Test: `modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityReleaseGateTest.java`
**Interfaces:**
- Consumes: Security profiles, query/native guardrails and replica set TLS fixtures.
- Produces: Release tests proving least privilege, fail-closed TLS/auth, injection rejection and absence of PII/secrets in telemetry.
**Implementation requirements:**
- App role can perform approved collection operations but cannot drop database, create users or modify sharding.
- Invalid CA, hostname and client certificate fail closed.
- Operator injection, dynamic field injection, dangerous regex and arbitrary command inputs are rejected locally.
- Logs, metrics and traces never contain credentials, raw BSON, document IDs, resume tokens or plaintext PII.
- Credential rotation succeeds through a new client generation without mixed admin/runtime credentials.
- [ ] **Step 1: Write the failing test**
```java
class MongoSecurityReleaseGateTest {
@org.junit.jupiter.api.Test
void applicationRoleCannotDropDatabase() {
MongoSecurityProbe probe = MongoSecurityProbe.withApplicationRole();
org.assertj.core.api.Assertions.assertThatThrownBy(probe::dropDatabase)
.isInstanceOf(MongoOperationRejectedException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:integrationTest --tests 'io.backend.skeleton.mongodb.security.MongoSecurityReleaseGateTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoSecurityReleaseGate {
public static void require(MongoSecurityEvidence evidence) {
if (!evidence.tls() || !evidence.auth() || !evidence.leastPrivilege()) {
throw new IllegalStateException("MongoDB security gate failed");
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-security:integrationTest --tests 'io.backend.skeleton.mongodb.security.MongoSecurityReleaseGateTest'
./gradlew :modules:mongodb:mongodb-security:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityIntegrationTest.java' 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoNoSqlInjectionTest.java' 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoLogRedactionTest.java' 'modules/mongodb/mongodb-security/src/test/resources/security/app-role.js' 'modules/mongodb/mongodb-security/src/test/resources/security/admin-role.js' 'modules/mongodb/mongodb-security/src/test/resources/security/test-ca.pem' 'modules/mongodb/mongodb-security/src/test/java/io/backend/skeleton/mongodb/security/MongoSecurityReleaseGateTest.java'
git commit -m "test: add mongodb security release gate"
```
### Task 49: 성능·Backpressure·Failover Chaos Aggregate Gate 구현
**Files:**
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoPerformanceGate.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoChaosGate.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoResourceBudgetReport.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/performanceTest/java/io/backend/skeleton/mongodb/testkit/performance/MongoPerformanceGateTest.java`
- Create: `modules/mongodb/mongodb-testkit-failover/src/failureTest/java/io/backend/skeleton/mongodb/testkit/performance/MongoChaosGateTest.java`
- Test: `modules/mongodb/mongodb-testkit-failover/src/test/java/io/backend/skeleton/mongodb/testkit/performance/MongoReleaseEvidenceTest.java`
**Interfaces:**
- Consumes: Three-node failover, operation budgets, aggregation, bulk, change stream and observability modules.
- Produces: Evidence for hot document contention, aggregation spill, deep pagination, pool saturation, primary failover and response loss.
**Implementation requirements:**
- Measure p50/p95/p99, documents examined/returned, keys examined, spill, pool wait and memory.
- Include hot counter, bounded/unbounded embedded growth, large group/sort, deep skip versus keyset and ordered/unordered bulk.
- Kill primary during writes, transaction commit and change stream consumption.
- Verify operation budgets prevent heap, queue and cursor growth beyond configured limits.
- Persist machine-readable reports as release artifacts.
- [ ] **Step 1: Write the failing test**
```java
class MongoReleaseEvidenceTest {
@org.junit.jupiter.api.Test
void releaseEvidenceContainsNoUnknownCommitBodyRetry() {
MongoChaosReport report = MongoChaosGate.runStandardScenarios();
org.assertj.core.api.Assertions.assertThat(report.businessBodyRetriesAfterUnknownCommit()).isZero();
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-failover:test --tests 'io.backend.skeleton.mongodb.testkit.performance.MongoReleaseEvidenceTest'
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public record MongoResourceBudgetReport(
long p99Millis,
long maxPoolWaitMillis,
long maxHeapBytes,
long documentsExamined,
long documentsReturned) {
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
./gradlew :modules:mongodb:mongodb-testkit-failover:test --tests 'io.backend.skeleton.mongodb.testkit.performance.MongoReleaseEvidenceTest'
./gradlew :modules:mongodb:mongodb-testkit-failover:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoPerformanceGate.java' 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoChaosGate.java' 'modules/mongodb/mongodb-testkit-failover/src/main/java/io/backend/skeleton/mongodb/testkit/performance/MongoResourceBudgetReport.java' 'modules/mongodb/mongodb-testkit-failover/src/performanceTest/java/io/backend/skeleton/mongodb/testkit/performance/MongoPerformanceGateTest.java' 'modules/mongodb/mongodb-testkit-failover/src/failureTest/java/io/backend/skeleton/mongodb/testkit/performance/MongoChaosGateTest.java' 'modules/mongodb/mongodb-testkit-failover/src/test/java/io/backend/skeleton/mongodb/testkit/performance/MongoReleaseEvidenceTest.java'
git commit -m "test: add mongodb performance and chaos release gates"
```
### Task 50: 문서·Runbook·ADR·지원 Matrix와 최종 Release Gate 구현
**Files:**
- Create: `docs/mongodb/support-matrix.md`
- Create: `docs/mongodb/document-modeling-guide.md`
- Create: `docs/mongodb/bson-mapping-guide.md`
- Create: `docs/mongodb/consistency-transaction-guide.md`
- Create: `docs/mongodb/query-aggregation-guide.md`
- Create: `docs/mongodb/schema-index-migration-guide.md`
- Create: `docs/mongodb/change-stream-guide.md`
- Create: `docs/mongodb/security-observability.md`
- Create: `docs/mongodb/runbooks/failover.md`
- Create: `docs/mongodb/runbooks/unknown-commit.md`
- Create: `docs/mongodb/runbooks/history-lost.md`
- Create: `docs/adr/ADR-MONGO-001-platform-boundary.md`
- Create: `docs/adr/ADR-MONGO-002-bson-representation.md`
- Create: `docs/adr/ADR-MONGO-003-transaction-retry.md`
- Create: `docs/adr/ADR-MONGO-004-index-schema-admin-plane.md`
- Create: `modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoStableReleaseGateTest.java`
- Test: `scripts/verify-mongodb-platform.sh`
**Interfaces:**
- Consumes: All Stable tasks and generated test evidence.
- Produces: Published documentation, support matrix, runbooks, ADRs and one reproducible Stable release command.
**Implementation requirements:**
- Document every public type, profile, error, startup failure and non-supported behavior.
- Support matrix distinguishes MongoDB 7.0 and 8.0 plus Standalone/RS/Sharded/Atlas topologies.
- Runbooks cover primary failover, unknown commit, history lost, index drift, migration failure and credential rotation.
- Release script executes unit, contract, compatibility, migration, security, failure and performance gates.
- Advanced modules are not promoted or transitively included by this gate.
- [ ] **Step 1: Write the failing test**
```java
class MongoStableReleaseGateTest {
@org.junit.jupiter.api.Test
void stableReleaseRequiresEveryEvidenceCategory() {
MongoStableReleaseEvidence evidence = MongoStableReleaseEvidence.load();
org.assertj.core.api.Assertions.assertThat(evidence.categories())
.contains("mapping", "transaction", "migration", "change-stream",
"security", "failover", "performance", "compatibility");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
bash scripts/verify-mongodb-platform.sh
```
Expected: FAIL because the production type, policy, wiring, or external behavior defined by this task does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
public final class MongoStableReleaseGate {
public void verify(MongoStableReleaseEvidence evidence) {
evidence.require("mapping");
evidence.require("transaction");
evidence.require("migration");
evidence.require("change-stream");
evidence.require("security");
evidence.require("failover");
evidence.require("performance");
evidence.require("compatibility");
}
}
```
Implement every file and invariant listed under **Implementation requirements**. The snippet fixes the public names and central behavior; it does not replace the listed requirements.
- [ ] **Step 4: Run the focused test and the module suite**
Run:
```bash
bash scripts/verify-mongodb-platform.sh
./gradlew :modules:mongodb:mongodb-spring-boot-starter:test
```
Expected: PASS with the focused assertion and the module suite green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'docs/mongodb/support-matrix.md' 'docs/mongodb/document-modeling-guide.md' 'docs/mongodb/bson-mapping-guide.md' 'docs/mongodb/consistency-transaction-guide.md' 'docs/mongodb/query-aggregation-guide.md' 'docs/mongodb/schema-index-migration-guide.md' 'docs/mongodb/change-stream-guide.md' 'docs/mongodb/security-observability.md' 'docs/mongodb/runbooks/failover.md' 'docs/mongodb/runbooks/unknown-commit.md' 'docs/mongodb/runbooks/history-lost.md' 'docs/adr/ADR-MONGO-001-platform-boundary.md' 'docs/adr/ADR-MONGO-002-bson-representation.md' 'docs/adr/ADR-MONGO-003-transaction-retry.md' 'docs/adr/ADR-MONGO-004-index-schema-admin-plane.md' 'modules/mongodb/mongodb-spring-boot-starter/src/test/java/io/backend/skeleton/mongodb/autoconfigure/MongoStableReleaseGateTest.java' 'scripts/verify-mongodb-platform.sh'
git commit -m "docs: publish mongodb platform release contract"
```