# PRD — Mongo Retirement: Full Code Nuke **Status:** In Progress **Date:** 2026-06-06 **Scope:** Remove every Mongoose/MongoDB reference from the codebase and replace Mongo-style ObjectId fields with plain UUID strings throughout. --- ## Context The Mongo→Postgres migration scaffolding is complete: - 25 Drizzle schemas cover all 23 collections - 11 Drizzle repos, 11 Mongo repos, 9 DualWrite fan-out repos - Backfill scripts written (not yet run against production) - Factory defaults to `mongo`; reads not yet cut over - Chat uses a JSONB shim (acceptable for now — normalize later) This PRD covers the **code-level retirement**: delete all Mongoose artifacts, flip the factory to Postgres-only, remove MongoDB from docker-compose and package.json, and replace `Types.ObjectId` with `string` (UUID) across every interface and schema. Production data migration (backfill execution + read-cutover per domain) is a **separate, human-gated operation** and is NOT in scope here. --- ## Goals 1. Zero `mongoose` or `mongodb` imports anywhere in `backend/` 2. Zero `Schema.Types.ObjectId` / `Types.ObjectId` field types 3. Zero `_id: ObjectId` interface declarations (replaced with `id: string`) 4. Zero `ObjectId(...)` constructors or `.toString()` coercions on IDs 5. MongoDB service removed from all docker-compose files 6. `mongoose` removed from `package.json` 7. `MONGODB_URI` / `MONGO_*` env vars removed from config and docs 8. Factory defaults to `pg`; Mongo and DualWrite modes deleted 9. Seeds and scripts updated to target Postgres only 10. Frontend: legacy ObjectId validation path removed --- ## Out of Scope - Backfill execution against production data - Production env var changes (`REPO_*`, `MONGO_CONNECT_MODE`) - Chat normalization (JSONB → child tables) — tracked separately - Drizzle schema changes (schemas are already correct) - Any Drizzle repo logic changes --- ## Phased Work Plan ### Phase 1 — Factory Cutover & Repo Deletion (Day 1) **1.1 Factory: delete Mongo + DualWrite modes** - File: `src/db/repositories/factory.ts` - Change: remove all `mongo` and `dual` branches; every domain returns its Drizzle repo directly - Remove `REPO_*` env flag checks (no longer needed) - Remove all imports of Mongo repos and DualWrite repos **1.2 Delete Mongo repo files (11 files)** ``` src/db/repositories/mongo/MongoUserRepo.ts src/db/repositories/mongo/MongoPaymentRepo.ts src/db/repositories/mongo/MongoPointsRepo.ts src/db/repositories/mongo/MongoMarketplaceRepo.ts src/db/repositories/mongo/MongoBlogRepo.ts src/db/repositories/mongo/MongoNotificationRepo.ts src/db/repositories/mongo/MongoDisputeRepo.ts src/db/repositories/mongo/MongoTrezorAccountRepo.ts src/db/repositories/mongo/MongoDerivedDestinationRepo.ts src/db/repositories/mongo/MongoChatRepo.ts src/db/repositories/mongo/MongoReleaseHoldRepo.ts src/db/repositories/mongo/index.ts ``` **1.3 Delete DualWrite repo files (9 files)** ``` src/db/repositories/dual/DualWriteUserRepo.ts src/db/repositories/dual/DualWritePaymentRepo.ts src/db/repositories/dual/DualWritePointsRepo.ts src/db/repositories/dual/DualWriteMarketplaceRepo.ts src/db/repositories/dual/DualWriteBlogRepo.ts src/db/repositories/dual/DualWriteNotificationRepo.ts src/db/repositories/dual/DualWriteDisputeRepo.ts src/db/repositories/dual/DualWriteTrezorAccountRepo.ts src/db/repositories/dual/DualWriteDerivedDestinationRepo.ts src/db/repositories/dual/index.ts ``` --- ### Phase 2 — Mongoose Model Deletion & Interface Extraction (Day 1–2) **2.1 Extract pure TS interfaces from each model file** For each of the 24 model files in `src/models/`, the plan is: - Keep the `I` TypeScript interface (with `id: string` replacing `_id: Types.ObjectId`) - Delete the `new Schema(...)` definition, virtual fields, pre/post hooks - Delete the `mongoose.model>(...)` export - Delete the mongoose import The interface files move to `src/types/models/` (or inline into the Drizzle schema files as inferred types). **2.2 Convert all `_id` → `id: string` in interfaces** Key changes per domain: - `IUser._id: Types.ObjectId` → `id: string` - `ICategory._id` → `id: string`, `parentId: string | null` - `IPurchaseRequest._id` → `id: string`, `buyerId/sellerId: string` - `ISellerOffer._id` → `id: string`, `sellerId/purchaseRequestId: string` - `IPayment._id` → `id: string`, polymorphic fields → `string` - `IChat._id` → `id: string`, `senderId/userId: string` - `IDispute._id` → `id: string`, all ref fields → `string` - All others follow same pattern **2.3 Delete `src/models/` directory after interfaces extracted** --- ### Phase 3 — Mongoose Connection & Config Removal (Day 2) **3.1 `src/infrastructure/database/connection.ts`** - Remove `mongoose.connect()` call and all Mongo connection logic - Remove `MONGO_CONNECT_MODE` handling - Keep only the Postgres pool initialization **3.2 `src/shared/config/index.ts`** - Remove `mongoUri` field - Remove `MONGODB_URI` env var read **3.3 `src/services/auth/authStore.ts`** - Remove `AUTH_FALLBACK_MONGO`, `AUTH_MIRROR_MONGO`, `MONGO_CONNECT_MODE` branches - Auth reads only from Postgres **3.4 `src/services/health/healthCheckService.ts`** - Remove MongoDB health check - Remove `MONGO_CONNECT_MODE` reference **3.5 `src/app.ts`** - Remove `mongoose.connect()` / `connectMongo()` call on startup - Remove `p.userId.toString()` ObjectId coercions (already string) --- ### Phase 4 — Seeds & Scripts Cleanup (Day 2) **4.1 Seeds (`src/seeds/`)** - Remove `mongoose.connect()` from all 7 seed files - Seeds already have PG-aware paths; remove Mongo dual-path - `seedUsers.ts`, `seedLevels.ts`, `seedCategories.ts`, etc. **4.2 Scripts (`src/scripts/`)** - Remove Mongoose imports from 13 scripts - Scripts that only operated on Mongo (e.g. `clearChats.ts`, `updateRequestStatus.ts`) — convert to Postgres queries or delete if obsolete --- ### Phase 5 — Backfill Infrastructure (Day 3) **5.1 Archive backfill scripts** (don't delete — needed for production data migration) - Move `src/db/backfill/` → `src/db/backfill/_archive/` - OR keep as-is but add a `.nocompile` flag / remove from tsconfig paths - The backfill scripts import mongoose (to read from Mongo) — they're tools for ops, not app code **5.2 Remove verification layer Mongo references** - `src/db/verify/shadowRead.ts` — remove Mongo comparison path - `src/db/verify/rowCounts.ts` — remove Mongo row count queries - `src/db/verify/checksums.ts` — remove Mongo checksum queries - `src/db/verify/reconcile.ts` — keep (handles `pg_dualwrite_gaps` replay, still useful) **5.3 `_idMap.ts`** — keep as a utility for ops scripts only; remove from app imports --- ### Phase 6 — Docker & Dependencies (Day 3) **6.1 `docker-compose.dev.yml`** - Remove `mongodb` service block - Remove `depends_on: mongodb` from backend service - Remove all `*_STORE=mongo` env vars (Auth, Config, Address, etc.) - Remove `mongodb_data` volume **6.2 `docker-compose.production.yml`** - Same: remove mongodb service, depends_on, volume **6.3 `deployment/docker-compose.yml`** - Same: remove mongodb service block **6.4 `backend/package.json`** - Remove `mongoose` from dependencies - Remove `mongodb-memory-server` from devDependencies **6.5 `.env.example` / `.env.development` / `.env.local`** - Remove `MONGODB_URI`, `MONGO_CONNECT_MODE`, `AUTH_FALLBACK_MONGO`, `AUTH_MIRROR_MONGO` - Remove `MONGO_INITDB_*` vars --- ### Phase 7 — Frontend ID Validation Cleanup (Day 3) **7.1 Remove ObjectId validation branch** - `frontend/src/sections/request-template/view/seller-shop-view.tsx:78` - Remove the "legacy 24-hex Mongo ObjectId" validation path - Keep only UUID validation **7.2 Audit frontend for other ObjectId references** - `grep -r 'ObjectId\|[0-9a-f]{24}' frontend/src/` - Remove any other legacy ID format handling --- ### Phase 8 — TypeScript Compilation Check (Day 3) - `npm run tsc` — fix all remaining type errors from the migration - Common issues: `.toString()` on already-string IDs, `_id` vs `id` mismatches, missing UUID imports --- ## Files to Delete (Complete List) ### Mongoose Model Files (24) ``` backend/src/models/User.ts backend/src/models/Category.ts backend/src/models/PurchaseRequest.ts backend/src/models/SellerOffer.ts backend/src/models/Payment.ts backend/src/models/Chat.ts backend/src/models/Dispute.ts backend/src/models/Review.ts backend/src/models/Address.ts backend/src/models/Notification.ts backend/src/models/TelegramLink.ts backend/src/models/TelegramSession.ts backend/src/models/TempVerification.ts backend/src/models/TrezorAccount.ts backend/src/models/DerivedDestination.ts backend/src/models/FundsLedgerEntry.ts backend/src/models/PointTransaction.ts backend/src/models/RequestTemplate.ts backend/src/models/BlogPost.ts backend/src/models/ConfigSetting.ts backend/src/models/ConfigSettingHistory.ts backend/src/models/LevelConfig.ts backend/src/models/ShopSettings.ts backend/src/models/index.ts ``` ### Mongo Repos (11+index) ``` backend/src/db/repositories/mongo/ (entire directory) ``` ### DualWrite Repos (9+index) ``` backend/src/db/repositories/dual/ (entire directory) ``` --- ## ID Migration Pattern Every interface field that was `Types.ObjectId` or `Schema.Types.ObjectId` becomes `string` (UUID). **Before:** ```typescript interface ISellerOffer { _id: Types.ObjectId; sellerId: Types.ObjectId | string; purchaseRequestId: Types.ObjectId | string; } ``` **After:** ```typescript interface ISellerOffer { id: string; // UUID sellerId: string; purchaseRequestId: string; } ``` Any code doing `someDoc._id.toString()` → `someDoc.id` (already a string). Any code doing `new mongoose.Types.ObjectId(value)` → just use `value` as string. --- ## Risk Register | Risk | Mitigation | |---|---| | App breaks because Mongo isn't seeded in dev | Run `npm run seed:pg` before removing Mongo from docker-compose | | Type errors cascade from `_id` → `id` rename | Fix systematically: models first, then services/routes | | Backfill scripts break (they import mongoose) | Keep backfill dir outside tsconfig compilation scope | | Auth fallback to Mongo breaks login | Auth already has PG path; remove fallback gate | | Chat reads fail (JSONB shim) | JSONB shim already works; normalization is future work | --- ## Acceptance Criteria - [ ] `grep -r "mongoose" backend/src/ --include="*.ts"` returns zero hits (excluding backfill archive) - [ ] `grep -r "Types.ObjectId\|Schema.Types.ObjectId" backend/src/` returns zero hits - [ ] `grep -r "mongodb" backend/package.json` returns zero hits - [ ] `grep -r "MONGODB_URI\|MONGO_CONNECT_MODE" backend/src/` returns zero hits - [ ] `npm run tsc` exits 0 - [ ] Backend starts with `MONGO_CONNECT_MODE=never` (or removed) and `REPO_*=pg` (or removed) - [ ] Seed scripts populate Postgres successfully - [ ] All docker-compose files have no `mongodb` service