Implements the mongodb-superpowers-package design: Stable Tasks 1-50 and Advanced Tasks 1-15. The design assumes 19 Stable + 12 Advanced Gradle projects under modules/mongodb*. This repository's fail-closed registry declares exactly 19 leaf identities, so those modules become package boundaries inside the registered leaf :adapter:outbound:persistence-mongo, with the design's module dependency table enforced by ten ArchUnit rules. The mapping and every deviation are recorded in docs/mongodb/repository-adaptation.md. Contract highlights, all enforced by tests rather than convention: - Transaction body retry and commit retry are separate loops. A new session per body attempt; commit-only retry on an unknown commit. The body is never replayed after a commit ambiguity, so a failover cannot become a duplicate. - MongoExecutionOutcome keeps both ambiguous outcomes distinct from success and failure, and MongoFailureContext records only the design-permitted fields. - Failure classification reads server error labels before numeric codes. - BSON representations come from a pinned manifest, never a library default, and a golden type-signature gate fails on any drift. - Index and validator changes go through the manifest and the admin plane; metadata ownership gates every drop. - Every Advanced capability refuses construction unless its flag is enabled. Verified against real servers, not only unit tests. Running the lanes for the first time exposed four defects that a green `check` had hidden: - Four release lanes passed while executing zero tests; the gate now counts executed tests per lane and fails on zero. - The "single replica set" fixture was a standalone, because Testcontainers 2.x needs withReplicaSet(); its test only asserted a connection string. - The three-node fixture was three independent clusters, so no election could occur, and awaitNewPrimary() compared against the post-stop primary. - The migration lease checked modifiedCount, so a same-millisecond refresh read as a lost lease. scripts/verify-mongodb-platform.sh now reports: 9 lanes, 0 skipped, 0 failed, every evidence category produced. scripts/verify-mongodb-advanced.sh reports NOT PROMOTABLE: actual-topology evidence (real sharded cluster, real KMS, real target deployment) is unobtainable here, so it is named rather than assumed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
125 lines
9.3 KiB
Markdown
125 lines
9.3 KiB
Markdown
# MongoDB Document Persistence Platform — Repository Adaptation Contract
|
|
|
|
**Design source:** `mongodb-superpowers-package/docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md`
|
|
**Stable plan:** `mongodb-superpowers-package/docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md`
|
|
**Advanced plan:** `mongodb-superpowers-package/docs/superpowers/plans/2026-08-11-mongodb-advanced-capabilities-expansion-plan.md`
|
|
|
|
The design package declares its own module root (`modules/mongodb`) and root package
|
|
(`io.backend.skeleton.mongodb`) as *implementation assumptions*, not as contract. This file is the
|
|
single record of how that assumed layout was mapped onto this repository. Only paths, build DSL,
|
|
and composition-root ownership changed. Public contracts, policy order, and error semantics are
|
|
implemented exactly as specified.
|
|
|
|
## 1. Why the module layout differs
|
|
|
|
The design assumes 19 Stable Gradle projects under `modules/mongodb/` and 12 Advanced projects
|
|
under `modules/mongodb-advanced/`. This repository is a Clean Architecture template whose
|
|
**fail-closed registry** (`src/config/architecture/modules.json`, enforced by `src/settings.gradle`
|
|
and `verifyCleanArchitectureDependencies`) declares **exactly 19 leaf identities**. Creating 31 more
|
|
Gradle projects would violate HARD-STOP #5 in `AGENTS.md`.
|
|
|
|
Therefore the design's 31 modules become **package boundaries inside the registered leaf**
|
|
`:adapter:outbound:persistence-mongo`, following the precedent already set by
|
|
[docs/httpclient/repository-adaptation.md](../httpclient/repository-adaptation.md). The design's
|
|
module dependency table (§6.3) is reproduced as ten ArchUnit rules in `MongoModuleBoundaryTest`, so
|
|
a forbidden edge fails the build the same way a missing Gradle dependency would.
|
|
|
|
## 2. Package mapping
|
|
|
|
Root package: `io.backend.skeleton.mongodb` → `dev.caskeleton.adapter.outbound.mongo`.
|
|
|
|
### 2.1 Stable modules
|
|
|
|
| Design module | Repository package |
|
|
|---|---|
|
|
| `mongodb-core-api` | `…outbound.mongo.api` (+ `.capability`, `.consistency`, `.error`, `.mapping`, `.observation`, `.profile`, `.schema`) |
|
|
| `mongodb-spring-data` | `…outbound.mongo.mapping` (+ `.type`), `…outbound.mongo.failure` |
|
|
| `mongodb-imperative` | `…outbound.mongo.imperative` (+ `.atomic`, `.bulk`, `.revision`) |
|
|
| `mongodb-reactive` | `…outbound.mongo.reactive` (+ `.cursor`) |
|
|
| `mongodb-query` | `…outbound.mongo.query` (+ `.budget`, `.pagination`) |
|
|
| `mongodb-aggregation` | `…outbound.mongo.aggregation` |
|
|
| `mongodb-transaction` | `…outbound.mongo.transaction` (+ `.retry`, `.session`) |
|
|
| `mongodb-index-schema` | `…outbound.mongo.schema` (+ `.index`, `.manifest`, `.model`, `.ttl`, `.validation`) |
|
|
| `mongodb-change-stream` | `…outbound.mongo.changestream` (+ `.projector`, `.recovery`) |
|
|
| `mongodb-geospatial` | `…outbound.mongo.geo` |
|
|
| `mongodb-migration-core` | `…outbound.mongo.migration` |
|
|
| `mongodb-migration-flamingock` | `…outbound.mongo.migration.flamingock` |
|
|
| `mongodb-observability` | `…outbound.mongo.observation` |
|
|
| `mongodb-security` | `…outbound.mongo.security` (+ `.admin`), `…outbound.mongo.nativecap` |
|
|
| `mongodb-spring-boot-starter` | `…outbound.mongo.autoconfigure` |
|
|
| `mongodb-testkit-core` | `…outbound.mongo.testkit.mapping`, `.compat`, `.performance` (`testkit` source set) |
|
|
| `mongodb-testkit-replicaset` | `…outbound.mongo.testkit.rs` (`testkit` source set) |
|
|
| `mongodb-testkit-failover` | `…outbound.mongo.testkit.failover` (`testkit` source set) |
|
|
| `mongodb-testkit-migration` | `…outbound.mongo.testkit.migration` (`testkit` source set) |
|
|
|
|
`…outbound.mongo.architecture` has no design counterpart: it holds the `@MongoOperation` marker and
|
|
the reusable ArchUnit rule set a fork applies to its own document/repository code.
|
|
|
|
### 2.2 Advanced modules
|
|
|
|
| Design module | Repository package |
|
|
|---|---|
|
|
| `mongodb-sharding` | `…outbound.mongo.advanced.sharding` (+ `.admin` for the D4 shard plane) |
|
|
| `mongodb-timeseries` | `…outbound.mongo.advanced.timeseries` |
|
|
| `mongodb-csfle` | `…outbound.mongo.advanced.encryption.csfle` |
|
|
| `mongodb-queryable-encryption` | `…outbound.mongo.advanced.encryption.qe` |
|
|
| `mongodb-search` | `…outbound.mongo.advanced.search` |
|
|
| `mongodb-vector-search` | `…outbound.mongo.advanced.vector` |
|
|
| `mongodb-tenancy-shared` | `…outbound.mongo.advanced.tenancy.shared` |
|
|
| `mongodb-tenancy-database` | `…outbound.mongo.advanced.tenancy.database` |
|
|
| `mongodb-change-stream-messaging-bridge` | `…outbound.mongo.advanced.bridge` |
|
|
| `mongodb-gridfs-compat` | `…outbound.mongo.advanced.gridfs` |
|
|
| `mongodb-testkit-sharded` | `…outbound.mongo.testkit.sharded` (`testkit` source set) |
|
|
| `mongodb-testkit-atlas` | `…outbound.mongo.testkit.atlas` (`testkit` source set) |
|
|
|
|
The design's rule that a Stable module never depends on an Advanced one survives as an ArchUnit rule
|
|
(`stableNeverDependsOnAdvanced`) plus the opt-in flag: every Advanced entry point requires
|
|
`MongoAdvancedCapabilityFlags` to have the matching capability enabled and refuses construction
|
|
otherwise. Being on the classpath is not being enabled.
|
|
|
|
## 3. Other deliberate substitutions
|
|
|
|
| Design assumption | Repository reality | Adaptation |
|
|
|---|---|---|
|
|
| Gradle Kotlin DSL under `modules/mongodb*` | Groovy DSL, root `build.gradle` conventions, `LockMode.STRICT` locking | Dependencies declared in `src/adapter/outbound/persistence-mongo/build.gradle`; `gradle.lockfile` regenerated. |
|
|
| `mongodb-spring-boot-starter` is a separate module the app depends on | `modules.json` gives `adapter-outbound-persistence-mongo` `runtime_memberships: []` and does **not** list it among `app-bootstrap`'s allowed dependencies | The `autoconfigure` package stays inside the leaf and registers through the leaf's own `META-INF/spring/…AutoConfiguration.imports`. This differs from the httpclient precedent, where the starter moved to `:app-bootstrap`; here the registry forbids that edge. |
|
|
| Spring Boot 4.1 / Spring Data MongoDB 5.1 baseline | Repository baseline is Spring Boot 4.0.0 / Spring Data MongoDB 5.0.0 | The platform targets the Spring Data MongoDB **API surface** common to both; no 5.1-only type is referenced. The support matrix records the actual pinned versions. |
|
|
| `MongoRetryScope` lives in `mongodb-transaction` | The `mongodb-spring-data` failure translator must classify retry scope, and it cannot depend on `mongodb-transaction` | `MongoRetryScope` lives in `…api.error` (core-api), which both packages already depend on. Same values, same meaning, one legal position in the DAG. |
|
|
| `mongodb-migration-flamingock` depends on Flamingock | Adding an unvetted external dependency is out of scope for this task, and the design itself requires the public contract not to depend on Flamingock types | The adapter is provider-neutral: it consumes a platform-owned `FlamingockChangeUnitView`. Wiring an actual Flamingock distribution is a one-file change behind that view. |
|
|
| Testkit as its own Gradle module | The design forbids production modules depending on the testkit | A dedicated `testkit` source set whose output is on the test compile/runtime classpaths only. ArchUnit rule `productionNeverDependsOnTestkit` enforces the direction. |
|
|
| Per-task `git commit` | `AGENTS.md`: commit policy is `human-only` | Implementation is delivered unstaged; commits are the human's action. This is the only plan step intentionally not executed, and it is recorded here. |
|
|
| `docs/mongodb/**`, `scripts/verify-mongodb-*.sh` | Repository already owns `docs/` and `scripts/` | Created at the same repository-relative paths. |
|
|
|
|
## 4. What is unchanged from the design
|
|
|
|
- D1 / D2 / D3 / D4 exposure planes and the ordered D3 admission sequence (§5).
|
|
- Stable API V1 with `apiStrict=true` on the D1/D2 client generation; D3/D4 on separate generations.
|
|
- `MongoExecutionOutcome`, including both ambiguous outcomes (`WRITE_RESULT_UNKNOWN`,
|
|
`TRANSACTION_COMMIT_UNKNOWN`), and `MongoFailureContext`'s permitted-field list.
|
|
- The complete stable exception hierarchy and the label-before-code classification order.
|
|
- The BSON representation manifest (UUID `STANDARD`, `Decimal128`, UTC instants, alias type metadata)
|
|
and the document-size budget.
|
|
- Update-operator-first writes, and optimistic revision as the precondition for whole-document
|
|
replacement.
|
|
- Transaction body retry and commit retry as separate loops: a new session per body attempt, and
|
|
commit-only retry on unknown commit. The body is never replayed after a commit ambiguity.
|
|
- Registered operation names and manifests for query, aggregation and index; no free-form JSON query
|
|
and no unbounded pipeline.
|
|
- Keyset pagination with an authenticated cursor and a unique tie-breaker requirement.
|
|
- Change stream as an at-least-once projector with resume-token checkpointing and explicit
|
|
history-lost handling.
|
|
- TTL as physical cleanup only, never the sole basis for access denial or business scheduling.
|
|
- Manifest-owned index/validator state with an apply policy that never drops what it does not own.
|
|
- Low-cardinality observation tags, command redaction, and the credential reference indirection.
|
|
- The Stable release gate's evidence categories, and the Advanced promotion gate's requirement for
|
|
actual-topology evidence.
|
|
|
|
## 5. Verification
|
|
|
|
```bash
|
|
bash scripts/verify-mongodb-platform.sh # Stable gate
|
|
bash scripts/verify-mongodb-advanced.sh # Advanced gate (opt-in lanes)
|
|
```
|
|
|
|
Both scripts run from the repository root and delegate to `src/gradlew`.
|