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>
3.7 KiB
ADR-MONGO-001 — MongoDB platform boundary
- Status: Accepted
- Date: 2026-08-13
- Design source:
mongodb-superpowers-package/.../2026-08-11-mongodb-document-persistence-platform-design.md§1, §2 (D-01, D-04, D-05), §5, §6
Context
Two failure modes are common when a team wraps MongoDB.
The first is flattening: a shared CommonMongoRepository<T, ID> and a generic CRUD facade, which
forces every collection to share an id strategy, a consistency profile and a query surface. MongoDB's
single-document atomicity, aggregation model and change streams stop being reachable, and the first
collection that needs something different gets a cast or a leaky generic.
The second is unrestricted exposure: the driver and runCommand available everywhere. Then any
service can drop a collection, run an unbounded pipeline, or issue an admin command from a request
thread, and no review catches it because there is nothing structural to catch.
Decision
The domain owns its documents; the platform owns the cross-cutting decisions. Four exposure planes:
| Plane | Contents | Client |
|---|---|---|
| D1 Standard document persistence | Spring Data repositories, typed queries, mapping manifest, atomic update primitives, optimistic revision | Stable API V1, apiStrict=true |
| D2 Advanced document operations | MongoTemplate, transactions/sessions, bulk, aggregation, keyset cursors, change streams |
Stable API V1, apiStrict=true |
| D3 Explicit Mongo capability | Native BSON, time series, search/vector, CSFLE/QE, shard-aware operations | Separate capability client |
| D4 Admin plane | Collection, validator, index, migration, shard, repair | Separate admin client and credential |
Specifically:
- No
CommonMongoRepository<T, ID>. Each aggregate declares its own repository. - D1/D2 run on Stable API V1 with
apiStrict=true, so a command outside the versioned API fails at development time instead of on the next server upgrade. - D3 is not a raw-client escape. Every call passes a fixed admission order: capability registered → database profile → collection allowlist → operation name → timeout → consistency profile → result limit → trace → redaction → command category → admin-command refusal → execute.
- D4 is a separate client with a separate credential. No application-plane path reaches it;
PolicyAwareMongoNativeGatewayrefuses admin-category commands regardless of capability. - Advanced and Experimental capabilities are opt-in modules, never transitive dependencies of the Stable surface.
Consequences
Positive. MongoDB's semantics stay reachable. Misuse is refused structurally rather than reviewed for. A server upgrade cannot silently change D1/D2 behaviour. Admin operations have their own audit trail and credential.
Negative. Every operation needs a registered name and profile, so a new query is a small amount of configuration rather than zero. A genuinely new capability requires a registration before it can be used. Both are deliberate: the cost is paid once per operation, at review time.
Rejected alternative — "expose the driver, rely on code review." Review does not scale to every
query in every service, and the operations that matter (unbounded pipeline, dropCollection,
unanchored regex on user input) look unremarkable in a diff.
Repository adaptation
The design assumes 19 Gradle modules under modules/mongodb/. This repository's fail-closed registry
declares exactly 19 leaf identities, so the modules became package boundaries inside
:adapter:outbound:persistence-mongo, enforced by ArchUnit. See
docs/mongodb/repository-adaptation.md.