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>
86 lines
4.3 KiB
Markdown
86 lines
4.3 KiB
Markdown
# Document Modeling Guide
|
|
|
|
Design §7–§9. The platform does not own your documents — D-01 is explicit that the domain owns
|
|
`@Document`, repositories, queries, index requirements and schema version. What the platform owns is
|
|
the set of modeling decisions that are expensive to reverse once a collection holds production data.
|
|
|
|
## 1. There is no `CommonMongoRepository`
|
|
|
|
A generic `CommonMongoRepository<T, ID>` is listed under explicitly unsupported (§3.4), and the
|
|
reason is not purity. A shared supertype forces every collection to share an id strategy, a
|
|
consistency profile and a query surface, and the first collection that needs a different one either
|
|
gets a cast or a leaky generic parameter. Declare a Spring Data repository per aggregate.
|
|
|
|
## 2. Embed or reference
|
|
|
|
`MongoDocumentModelManifest` records the decision per collection so it is reviewable, and
|
|
`MongoDocumentModelValidator` refuses the combinations that do not survive growth.
|
|
|
|
| Descriptor | Use when |
|
|
|---|---|
|
|
| `EmbeddedCollectionDescriptor` | The child is read with the parent, is bounded, and has no independent lifecycle. Declare `maxElements`; an unbounded array is the single most common way a document reaches the size limit. |
|
|
| `MongoReferenceDescriptor` | The child is queried independently, is unbounded, or outlives the parent. Declare `MongoReferenceLifecycle` so the deletion story is written down rather than discovered. |
|
|
|
|
The validator rejects an embedded collection without a bound, and a reference whose lifecycle says
|
|
the child is owned by the parent but which is also referenced from elsewhere.
|
|
|
|
## 3. Size budget
|
|
|
|
`MongoDocumentSizeBudget`:
|
|
|
|
| Constant | Bytes | Meaning |
|
|
|---|---|---|
|
|
| `MONGODB_HARD_LIMIT_BYTES` | 16 MiB | MongoDB's own limit. |
|
|
| `PLATFORM_CEILING_BYTES` | 4 MiB | The largest budget the platform will accept. |
|
|
| `DEFAULT_BYTES` | 2 MiB | `MongoDocumentSizeBudget.standard()`. |
|
|
|
|
A budget above the ceiling is refused at construction. Budgeting to 16 MiB means the failing write
|
|
is the first symptom, and by then the collection is already full of near-limit documents.
|
|
|
|
## 4. Identity
|
|
|
|
`DomainDocumentId` and `MongoIdRepresentation` fix how a domain identifier becomes `_id`. Pick the
|
|
representation once per collection and record it in the manifest:
|
|
|
|
- `OBJECT_ID` — server-generated, monotonic, 12 bytes. Good default when the domain has no natural id.
|
|
- `UUID_BINARY` — a domain UUID stored as `Binary` subtype 4 (`STANDARD`). Never store a UUID as a
|
|
string "because it is easier to read"; it doubles the index size and loses the type.
|
|
- `STRING` — a natural key that is genuinely a string (a slug, an external system's id).
|
|
|
|
An `_id` choice is effectively permanent: it is the shard key candidate, the resume-token join key
|
|
and the pagination tie-breaker.
|
|
|
|
## 5. Schema version
|
|
|
|
Every long-lived collection carries `DocumentSchemaVersion`. `MongoSchemaVersionPolicy` and
|
|
`MongoSchemaVersionRange` say which versions the running code can read; a document outside the range
|
|
raises `MongoDataSchemaUnsupportedException` rather than being silently mapped with missing fields.
|
|
|
|
Write the range down before the migration, not after: the range is what lets old and new instances
|
|
run at once during a rolling deploy.
|
|
|
|
## 6. Type metadata
|
|
|
|
`@LongLivedMongoDocument` marks a document whose stored type alias must not be a Java class name.
|
|
`MongoTypeMetadataRegistry` maps alias → class. Storing the FQCN means moving or renaming the class
|
|
becomes a data migration; storing an alias keeps it a refactor. See
|
|
[bson-mapping-guide.md](bson-mapping-guide.md) §4.
|
|
|
|
## 7. Collection profiles
|
|
|
|
`MongoCollectionProfileRegistry` binds a `CollectionProfileName` to its consistency profile, budget
|
|
and allowlist. A collection that is not registered cannot be reached through
|
|
`MongoImperativeExecutor` or `ReactiveMongoExecutor` — the allowlist is the mechanism that keeps an
|
|
unreviewed collection from appearing in production by accident.
|
|
|
|
## 8. What to write down before the first insert
|
|
|
|
1. Embed/reference decision per child collection, with bounds.
|
|
2. Size budget.
|
|
3. `_id` representation.
|
|
4. Schema version range.
|
|
5. Index manifest (see [schema-index-migration-guide.md](schema-index-migration-guide.md)).
|
|
6. Consistency profile (see [consistency-transaction-guide.md](consistency-transaction-guide.md)).
|
|
|
|
Each of these is cheap now and a migration later.
|