Files
nick-doc/02 - Data Models/Data Model Overview.md
Siavash Sameni dceaf82934 audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:48:04 +04:00

9.9 KiB

title, tags, aliases
title tags aliases
Data Model Overview
data-model
mongoose
overview
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<Name> TypeScript interface, and named exports for the compiled model.

[!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, and ShopSettings as 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.

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_paymentseller_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 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.
  • 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 both userId and telegramUserId).
  • TelegramSession — Short-lived Telegram Mini App session token issued when initData is verified. Carries the initDataFingerprint for replay protection and auto-expires via a MongoDB TTL index on expiresAt.
  • 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 commit 27fb15a.

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 }, 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 Users) attach SellerOffer documents; the request transitions through received_offersin_negotiation as the parties chat in a Chat.
  3. The buyer accepts an offer; a Payment is opened against the Request Network provider and, once verified by webhook/reconciliation and safety checks, advances to a funded escrow state.
  4. The seller marks the request deliverydelivered; the buyer confirms with the 6-digit deliveryCode and the request becomes completed.
  5. The escrow Payment flips to released after a ledger-gated custody transfer instruction. Optionally the buyer writes a Review and earns a PointTransaction.

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>.