--- title: Data Model Overview tags: [data-model, mongoose, overview] aliases: [Models Index, Schema Overview] --- # Data Model Overview This section documents every Mongoose model that backs the marketplace. The persistence layer lives in `backend/src/models/` and is exported through a single barrel file at `backend/src/models/index.ts`. All models target MongoDB via Mongoose, lean on `timestamps: true` for `createdAt` / `updatedAt`, and follow a consistent pattern: one schema per file, an exported `I` TypeScript interface, and named exports for the compiled model. > [!note] Scope > Sixteen models are documented here. The "File" concept exists only at the service layer (`backend/src/services/file/`) and is not persisted as its own Mongoose collection, so it is not listed below. > > [!warning] Implementation gap > As of the 2026-05-24 audit, the following documented models **do not yet have Mongoose schema files** in `backend/src/models/`: > - [[Dispute]] > - [[BlogPost]] > - [[Review]] > - [[PointTransaction]] > - [[LevelConfig]] > - [[ShopSettings]] > The following *are* implemented in code and are documented accurately: > - [[User]], [[PurchaseRequest]], [[SellerOffer]], [[Payment]], [[Chat]], [[Notification]], [[RequestTemplate]], [[Address]], [[Category]], [[TempVerification]] > Additionally, `FundsLedgerEntry.ts` and `TrezorAccount.ts` exist in `backend/src/models/` but are not yet documented in this vault. ## Index of Models - [[User]] — Core identity. Stores credentials, profile, preferences, referral data, points, and WebAuthn passkeys. Every other model that records "who did what" points back at a `User._id`. Buyers, sellers, and admins all live in this collection, differentiated by a `role` enum. - [[PurchaseRequest]] — The buyer-side document at the heart of the marketplace. Captures what a buyer wants, the budget, urgency, delivery preferences, and the full lifecycle status (`pending_payment` → `seller_paid`). Aggregates [[SellerOffer]] references and tracks delivery codes. - [[SellerOffer]] — A seller's bid against a [[PurchaseRequest]]. Holds price, delivery ETA, attachments, and a small status machine (`pending` / `accepted` / `rejected` / `withdrawn`). - [[Payment]] — Records every monetary movement: buyer pay-in, seller payout, refund. Integrates with the SHKeeper crypto gateway and tracks escrow state plus on-chain transaction metadata. - [[Chat]] — Conversation container with embedded messages, participants, unread counters, and reactions. Used for direct buyer-seller chats, group chats, and support tickets. Can be linked to a [[PurchaseRequest]] or [[SellerOffer]]. - [[Notification]] — Per-user notification with category, type, and 90-day TTL for automatic cleanup. References any related entity by stringified id. - [[RequestTemplate]] — A seller-authored, sharable template that pre-fills a [[PurchaseRequest]]. Carries a public shareable link, usage counter, and an optional default proposal. - [[Dispute]] — Buyer-raised complaint tied to a [[PurchaseRequest]]. Captures evidence uploads, a timeline of admin actions, deadlines, and a structured resolution. - [[BlogPost]] — Editorial content: title, slug, rich content, media, SEO metadata, view/like counters, and a draft/published/archived workflow. - [[Address]] — User shipping address book entry. Enforces a single primary address per user via a pre-save hook. - [[Category]] — Hierarchical product/service taxonomy referenced by [[PurchaseRequest]] and [[RequestTemplate]]. Supports parent/child via `parentId` and bilingual `name` / `nameEn`. - [[Review]] — Polymorphic 1-5 star review against either a seller or a [[RequestTemplate]] (`subjectType` discriminator). One review per reviewer per subject (compound unique index). - [[PointTransaction]] — Ledger of point grants and spends per user. Sources include purchase, referral, bonus, admin grant, and redemption. - [[LevelConfig]] — Static configuration of loyalty tiers (level number, point thresholds, benefits, icon, color). Driven by admins; consumed by the [[User]].points.level field. - [[ShopSettings]] — One-to-one storefront branding for a seller: name, description, avatar, cover image, review toggles, and social links. - [[TempVerification]] — Short-lived signup record that holds candidate user data and a verification code. Auto-purges via TTL when `emailVerificationCodeExpires` passes. ## Relationship Diagram ```mermaid erDiagram USER ||--o{ PURCHASE_REQUEST : "creates as buyer" USER ||--o{ SELLER_OFFER : "submits as seller" USER ||--o{ ADDRESS : "owns" USER ||--o{ NOTIFICATION : "receives" USER ||--o{ POINT_TRANSACTION : "earns/spends" USER ||--o{ REQUEST_TEMPLATE : "authors as seller" USER ||--o| SHOP_SETTINGS : "configures" USER ||--o{ BLOG_POST : "publishes" USER ||--o{ REVIEW : "writes as reviewer" USER ||--o{ DISPUTE : "raises as buyer" USER ||--o{ USER : "referred by" PURCHASE_REQUEST }o--|| CATEGORY : "belongs to" PURCHASE_REQUEST ||--o{ SELLER_OFFER : "receives" PURCHASE_REQUEST ||--o{ PAYMENT : "settled by" PURCHASE_REQUEST ||--o| CHAT : "discussed in" PURCHASE_REQUEST ||--o{ DISPUTE : "may trigger" PURCHASE_REQUEST ||--o| REVIEW : "rated by buyer" SELLER_OFFER ||--o| PAYMENT : "funds" SELLER_OFFER }o--|| PURCHASE_REQUEST : "responds to" PAYMENT }o--|| USER : "buyer" PAYMENT }o--|| USER : "seller" CHAT }o--o{ USER : "participants" CHAT ||--o{ DISPUTE : "support channel" REQUEST_TEMPLATE }o--|| CATEGORY : "belongs to" REQUEST_TEMPLATE ||--o{ REVIEW : "rated as subject" CATEGORY ||--o{ CATEGORY : "parent of" POINT_TRANSACTION }o--|| USER : "owner" LEVEL_CONFIG ||..|| USER : "level lookup" TEMP_VERIFICATION ||..|| USER : "promoted to" ``` ## Conventions Across All Models > [!note] Shared schema patterns > - **Timestamps**: every model declares `{ timestamps: true }`, so `createdAt` and `updatedAt` are always present. > - **ObjectId references**: foreign keys use `Schema.Types.ObjectId` with an explicit `ref` (e.g. `ref: 'User'`). The two exceptions are [[Notification]] and [[Payment]] which use string-typed or `Mixed` identifiers in places to support template-flow payments. > - **Soft delete**: deletion is modelled as a `status` flag (e.g. `User.status = 'deleted'`, `BlogPost.status = 'archived'`) rather than physical removal. > - **TTL indexes**: short-lived collections ([[Notification]], [[TempVerification]]) use `{ expireAfterSeconds: ... }` so MongoDB does the cleanup. > - **toJSON sanitisation**: [[User]] overrides `toJSON` to strip credentials, refresh tokens, and verification codes before serialisation. > [!warning] Index discipline > Several schemas leave a comment noting that `unique: true` already creates an index — adding `schema.index({ field: 1 })` on top would produce a duplicate-index warning at startup. When introducing new indexes, search for `unique: true` first. ## Lifecycle View The dominant happy-path flow exercises five collections in order: 1. A buyer (`User`) creates a `PurchaseRequest` with `status: 'pending'`. 2. Sellers (other `User`s) attach `SellerOffer` documents; the request transitions through `received_offers` → `in_negotiation` as the parties chat in a `Chat`. 3. The buyer accepts an offer; a `Payment` is opened against the SHKeeper provider with `escrowState: 'funded'`. 4. The seller marks the request `delivery` → `delivered`; the buyer confirms with the 6-digit `deliveryCode` and the request becomes `completed`. 5. The escrow `Payment` flips to `released` and a payout `Payment` (`direction: 'out'`) is issued. Optionally the buyer writes a `Review` and earns a `PointTransaction`. If anything goes sideways, the buyer can open a `Dispute` (planned but not yet implemented), which would freeze the flow until an admin resolves it (refund, replacement, compensation, or no-action). ## How to Navigate Each model has its own note in this folder. Cross-references use `[[wikilinks]]` so backlinks work in Obsidian's graph view. Schemas are documented at field-level granularity — every field is listed with its type, default, validation, and indexing decisions. Where a model carries a meaningful state machine, a Mermaid `stateDiagram-v2` accompanies the schema table. > [!note] Source of truth > The information below is mirrored from the TypeScript schema definitions. If a field listed here disagrees with the code, the code wins — please update the note. All source citations use the form `backend/src/models/.ts:`.