Best Practices
Team-wide conventions for building fast, testable MongoDB services with mongo-ops.
๐๏ธ Layering
- One repository per collection. Encapsulate every query the domain needs behind repository methods; keep Mongo details (
$regex,$inc, projections) inside the repository. - Keep models thin.
BaseDocumentfor the shape; use PydanticFieldconstraints for validation; never put business rules in the model. - Use services for cross-repository logic. A
Servicecomposes multiple repositories (andTransactionManager) โ routes stay thin. - Expose
get_many(filter=..., skip=..., limit=..., sort=...)instead of rawfindfor list endpoints โ you get controlled pagination for free.
๐ Lifecycle
- Connect once, in the lifespan.
MongoConnectionManager.lifespan(...)(or explicitconnect/disconnect) โ never lazily per request. - Construct repositories after
connect(). Module-levelRepo()before connection raisesRuntimeError("Database not connected..."). Use dependencies or construct inside the lifespan/request. - Order the cache lifecycle strictly:
set_cache_backend(backend)โinitialize_cache()(after connect, before use) โshutdown_cache()on exit. - Register all models up front via
ModelRegistry.register(...)and letinitialize_all()create indexes once at startup (idempotent).
๐๏ธ Data & Performance
- Declare indexes for every hot query. Single-field, composite, and optioned (unique/TTL) specs all work via
ModelRegistry.registerโ see use case 13. - Cache only hot, low-write
_idreads. UseCachedBaseRepositoryfor lookups-by-id; invalidate (update/deletehandle it) and pick a sensibledefault_ttl. - Populate at the repository boundary.
PopulatingRepositoryresolves refs on read and depopulates on write; do not hand-roll joins in endpoints. - Respect the populate invariants: rules name the field that holds the reference and that is replaced;
patch()cannot touch FK fields โ useupdate().
๐ Transactions & Errors
- Use transactions for multi-document writes.
TransactionManager.start_session(inline) orexecute_transaction(list of ops) โ and passsession=to every collection call inside. - Handle the library's real exceptions at the edges:
DuplicateKeyErrorโ 409,InvalidIdโ 400,CircularReferenceErrorโ 409,ValueErrorguides โ 400/422 (see Error Handling).
๐งช Testing
- Default to mock-based unit tests. Patch
MongoConnectionManager.get_database, useAsyncMockcollections and cursor chains โ the whole suite runs without MongoDB (use case 14). - Mirror the library tests.
tests/test_{repository,populating_repository,cache,registry,transactions}.pyare canonical examples of every pattern above. - Use type hints end-to-end โ mypy-gated CI (see pyproject) catches drift early.
Related
- Overview ยท Core Components ยท Use Cases