Files
nick-doc/04 - Flows/Rating Flow.md
2026-05-23 20:35:34 +03:30

6.0 KiB
Raw Blame History

title, tags, related_models, related_apis
title tags related_models related_apis
Rating Flow
flow
rating
review
moderation
Review
ShopSettings
PurchaseRequest
POST /api/marketplace/reviews
GET /api/marketplace/reviews/:subjectType/:subjectId

Rating Flow

After an order is completed, the buyer rates the seller and (optionally) leaves a review; the seller can rate the buyer in the reciprocal flow. Reviews are scoped by subjectType (seller | template) and constrained by the seller's ShopSettings.

Actors

  • Buyer (typical reviewer).
  • Seller (subject; can also be a reviewer in the reciprocal direction).
  • System — enforces uniqueness and moderation rules.
  • Backendbackend/src/services/marketplace/reviewRoutes.ts.
  • MongoDBreviews collection (backend/src/models/Review.ts).

Preconditions

  • The associated PurchaseRequest is completed (or finalized).
  • The reviewer is the buyer of that request (for isVerifiedBuyer to be true).
  • The subject's ShopSettings.allowSellerReviews / allowTemplateReviews is not false (reviewRoutes.ts:15-31).

Step-by-step narrative

  1. From the request detail or seller profile, the buyer clicks "Leave review". The form captures rating (15) and comment (≤ 1000 chars).
  2. Frontend POSTs POST /api/marketplace/reviews with { subjectType: 'seller' | 'template', subjectId, rating, comment, purchaseRequestId? }.
  3. Backend route handler:
    • Validates payload.
    • Calls isReviewsAllowed(subjectType, subjectId) — checks the seller's ShopSettings (for sellers, look up the seller directly; for templates, look up the template's owning seller).
    • Sets isVerifiedBuyer = true if the user owns a completed purchase request from that seller.
    • Defaults status: 'published' (no moderation queue today).
  4. Inserts a Review document. Unique index { subjectType, subjectId, reviewerId } prevents a user from reviewing the same subject twice (Review.ts:34).
  5. Aggregated stats are recomputed on read via computeStats (reviewRoutes.ts:33-62) — count, average, per-star histogram. No denormalised counter on User today; everything is computed at read-time.

Visibility

  • Public — anyone hitting GET /api/marketplace/reviews/seller/:sellerId sees status: 'published' reviews paginated by 10.
  • If ShopSettings.allowSellerReviews === false (or allowTemplateReviews === false), reads return 403 Reviews are disabled by seller.
  • The seller can flag a review for moderation (planned — current statuses include pending and rejected, but no UI to flip them today; admin can update via direct DB).

Sequence diagram

sequenceDiagram
    autonumber
    actor B as Buyer
    participant FE as Frontend
    participant BE as Backend
    participant DB as MongoDB

    B->>FE: Open seller profile / request detail
    B->>FE: Click "Leave review", choose stars + comment
    FE->>BE: POST /api/marketplace/reviews
    BE->>DB: ShopSettings.findOne({sellerId}) → allowSellerReviews?
    alt allowed
        BE->>DB: PurchaseRequest.exists({buyer, seller, status:"completed"})?
        BE->>DB: Review.create({status:"published", isVerifiedBuyer})
        BE-->>FE: 201 { review }
    else disabled
        BE-->>FE: 403 Reviews disabled
    end

    Note over FE: Public visitor:
    FE->>BE: GET /api/marketplace/reviews/seller/{id}
    BE->>DB: Review.find / computeStats aggregate
    BE-->>FE: { items, pagination, stats:{count, avg, histogram} }

API calls

Method Endpoint Purpose
POST /api/marketplace/reviews Submit review
GET /api/marketplace/reviews/:subjectType/:subjectId List reviews + stats
PATCH /api/marketplace/reviews/:id Edit own review (within edit window)
DELETE /api/marketplace/reviews/:id Delete own review

Database writes

  • reviews — insert on submission; one document per (subjectType, subjectId, reviewerId).
  • shopsettings — read-only here; the seller controls allowSellerReviews / allowTemplateReviews in their shop settings.

Socket events emitted

  • None today. A new-review event broadcast to user-{sellerId} would be a useful enhancement so sellers see reviews appear live.

Side effects

  • Recompute aggregate on every list call — fine for small volumes; consider caching stats per subject when the review count grows.
  • Order rating field also stamps metadata.rating on the purchase request when the marketplace endpoint accepts ratings inline (see routes.ts references in backend/src/services/marketplace/routes.ts).

Error / edge cases

  • Duplicate review → MongoDB E11000 from the unique index; surface as 409 Already reviewed.
  • Subject disabled reviews403.
  • Reviewer not a verified buyer → review is still allowed but isVerifiedBuyer = false. Display this in the UI.
  • Rating out of 15 → Mongoose schema validator rejects.
  • Comment > 1000 chars → schema-level rejection.
  • Seller toggles allowSellerReviews=false after reviews exist → existing reviews remain stored but become unreadable via the public GET (reviewRoutes.ts:81-83).
  • Spam / abuse → no automatic moderation; admin can flip status to rejected to hide.

[!tip] Verified-buyer badge The isVerifiedBuyer flag is the most credible signal for prospective buyers. Always render a "Verified buyer" pill next to reviews where this is true.

Linked flows

Source files

  • Backend: backend/src/services/marketplace/reviewRoutes.ts
  • Backend: backend/src/models/Review.ts
  • Backend: backend/src/services/marketplace/shopSettingsController.ts (allow flags)
  • Backend: backend/src/services/marketplace/routes.ts (inline rating on order completion)
  • Frontend: review components under frontend/src/sections/account/ and seller profile views