10 KiB
title, tags, aliases
| title | tags | aliases | |||||
|---|---|---|---|---|---|---|---|
| Data Model Overview |
|
|
Data Model Overview
This section documents every Mongoose model that backs the marketplace. On backend integrate-main-into-development@cab0719, these Mongoose models are still the live application persistence layer. The repo also contains a Drizzle/Postgres migration layer, but most services still call backend/src/models/* directly.
[!note] Scope Twenty-two models are present in
backend/src/models/. 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.[!note] Documentation freshness The 2026-05-24 audit note that marked
Dispute,BlogPost,Review,PointTransaction,LevelConfig, andShopSettingsas missing is now stale: schema files exist for those models. Newer operational models such as ConfigSetting, DerivedDestination, FundsLedgerEntry, and TrezorAccount should be expanded into dedicated model pages when the docs are next deepened.
[!warning] Mongo vs Postgres runtime status Postgres schemas and repositories exist for the money/relational core, but normal app traffic is not fully cut over. Payment quote rows are the only current conditional PG write in checkout, and even that requires
ORACLE_QUOTING_ENABLED=trueplus a resolvable PG payment row. See Postgres Runtime Cutover Status.
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 aroleenum. - 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 monetary movement intent and state: buyer pay-in, seller release, and refund. The current primary provider path is Request Network plus in-house checkout, derived destinations, funds ledger entries, and Transaction Safety Provider 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
parentIdand bilingualname/nameEn. - Review — Polymorphic 1-5 star review against either a seller or a RequestTemplate (
subjectTypediscriminator). 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
emailVerificationCodeExpirespasses. - TelegramLink — Permanent auditable association between a Telegram user ID and an Amanat User. Stores Telegram profile metadata, link source (
miniapp/bot/login_widget), status (active/blocked), and last-seen timestamp. One per Telegram user (unique on bothuserIdandtelegramUserId). - TelegramSession — Short-lived Telegram Mini App session token issued when
initDatais verified. Carries theinitDataFingerprintfor replay protection and auto-expires via a MongoDB TTL index onexpiresAt. - ConfigSetting — Runtime configuration persisted in MongoDB for operational knobs that need an admin surface rather than a deploy.
- DerivedDestination — Per-payment derived wallet destination records used to reduce address reuse and reconcile on-chain pay-ins.
- FundsLedgerEntry — Immutable accounting ledger rows for pay-in, hold, release, refund, fee, adjustment, and reversal events.
- TrezorAccount — Hardware-wallet/safekeeping account metadata for custody operations and staged signer hardening.
- ConfigSettingHistory — Immutable audit trail of numeric runtime-config changes. Currently used for per-chain confirmation threshold change events, keyed as
confirmation_threshold:<chainId>. Added in commit27fb15a.
Relationship Diagram
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"
USER ||--o{ TREZOR_ACCOUNT : "controls custody account"
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"
PAYMENT ||--o{ FUNDS_LEDGER_ENTRY : "accounted by"
PAYMENT ||--o| DERIVED_DESTINATION : "collects into"
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"
TELEGRAM_LINK }o--|| USER : "links identity"
TELEGRAM_SESSION }o--o| USER : "session for"
TELEGRAM_SESSION }o--|| TELEGRAM_LINK : "matches"
Conventions Across All Models
[!note] Shared schema patterns
- Timestamps: every model declares
{ timestamps: true }, socreatedAtandupdatedAtare always present.- ObjectId references: foreign keys use
Schema.Types.ObjectIdwith an explicitref(e.g.ref: 'User'). The two exceptions are Notification and Payment which use string-typed orMixedidentifiers in places to support template-flow payments.- Soft delete: deletion is modelled as a
statusflag (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
toJSONto strip credentials, refresh tokens, and verification codes before serialisation.
[!warning] Index discipline Several schemas leave a comment noting that
unique: truealready creates an index — addingschema.index({ field: 1 })on top would produce a duplicate-index warning at startup. When introducing new indexes, search forunique: truefirst.
Lifecycle View
The dominant happy-path flow exercises five collections in order:
- A buyer (
User) creates aPurchaseRequestwithstatus: 'pending'. - Sellers (other
Users) attachSellerOfferdocuments; the request transitions throughreceived_offers→in_negotiationas the parties chat in aChat. - The buyer accepts an offer; a
Paymentis opened against the Request Network provider and, once verified by webhook/reconciliation and safety checks, advances to a funded escrow state. - The seller marks the request
delivery→delivered; the buyer confirms with the 6-digitdeliveryCodeand the request becomescompleted. - The escrow
Paymentflips toreleasedafter a ledger-gated custody transfer instruction. Optionally the buyer writes aReviewand earns aPointTransaction.
If anything goes sideways, the buyer can open a Dispute, which freezes release 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/<File>.ts:<line>.