review-service is a Quarkus service that owns customer reviews and ratings for products and stores in ShopSTAR3. It exposes a GraphQL subgraph via Apollo Federation for storefront read access, a REST API for review submission and helpfulness voting, and a REST admin API for moderation and settings. Storage is PostgreSQL with store_id as a discriminator column.
Data Model#
Reviews#
reviews — review_id UUID PK, store_id, target_type ENUM (PRODUCT / STORE), target_id UUID, customer_id, order_id (set for verified purchaser submissions), rating SMALLINT (1–5), title, body, status ENUM (PENDING / APPROVED / REJECTED), rejection_reason, moderated_by UUID (staff ID; null = auto-approved or pending), moderated_at, submitted_at, updated_at.
Media Attachments#
review_media — media_id UUID PK, review_id FK, store_id, media_type ENUM (IMAGE / VIDEO), url TEXT (object storage reference), position.
review-service stores references only. File upload and storage are handled by the content sandbox or the platform’s object storage layer.
Helpfulness Votes#
review_votes — vote_id UUID PK, review_id FK, store_id, customer_id, is_helpful BOOLEAN, voted_at. UNIQUE on (review_id, customer_id) — one vote per customer per review.
Aggregate Ratings#
rating_aggregates — aggregate_id UUID PK, store_id, target_type, target_id UUID, avg_rating NUMERIC(3,2), total_count, count_1 through count_5 INT (star distribution), updated_at. UNIQUE on (store_id, target_type, target_id).
Aggregates are recomputed atomically in the same transaction as any review status change that affects the APPROVED set (approval, rejection of an approved review, or GDPR deletion).
Review Settings (per store)#
review_settings — store_id UUID PK, eligibility ENUM (OPEN / VERIFIED_PURCHASER / REGISTERED_CUSTOMER), auto_approve BOOLEAN, moderation_ai_enabled BOOLEAN, min_display_count INT (minimum approved reviews before the aggregate is shown on storefront), show_unverified BOOLEAN, default_sort_order ENUM (NEWEST / HIGHEST_RATED / MOST_HELPFUL), max_media_per_review INT.
Submission Eligibility#
When a review is submitted, the configured eligibility rule is enforced before the record is inserted:
| Eligibility | Check |
|---|---|
OPEN | No check — any request with a valid store context is accepted |
REGISTERED_CUSTOMER | customer-service GetCustomer(customerId, storeId) must return an active customer |
VERIFIED_PURCHASER | Redis cache checked first (verified_purchase:{storeId}:{customerId}:{productId}); on miss, order-service VerifyPurchase is called via gRPC |
Verified purchaser eligibility is cached in Redis (TTL = 2 years) when order.fulfilled is consumed, making the hot path a Redis lookup rather than a cross-service call.
Moderation#
Reviews are inserted with status = PENDING unless auto_approve = true. When moderation_ai_enabled = true and auto_approve = false, an asynchronous AI screening call flags low-risk reviews for auto-approval and surfaces suspicious ones for manual review.
State machine:
PENDING → APPROVED— rating aggregate updatedPENDING → REJECTED— rejection reason storedAPPROVED → REJECTED— aggregate contribution reversed, reason stored
GraphQL Subgraph#
review-service joins the Apollo Federation graph and extends the Product and Store types:
extend type Product @key(fields: "id") {
id: ID! @external
reviews(first: Int, after: String, sort: ReviewSort): ReviewConnection!
ratingAggregate: RatingAggregate
}
type Review {
id: ID!
rating: Int!
title: String
body: String
customer: ReviewAuthor
isVerifiedPurchaser: Boolean!
helpfulCount: Int!
media: [ReviewMedia!]!
submittedAt: DateTime!
}
type RatingAggregate {
average: Float
totalCount: Int!
distribution: [RatingBucket!]!
}Only APPROVED reviews are returned by the subgraph. The min_display_count setting suppresses the aggregate until sufficient approved reviews exist.
REST Interfaces#
Storefront#
| Method | Endpoint | Purpose |
|---|---|---|
POST | /reviews | Submit a review (authenticated customer) |
POST | /reviews/{id}/votes | Submit a helpfulness vote |
Admin#
| Method | Endpoint | Purpose |
|---|---|---|
GET | /reviews?status=PENDING | Moderation queue |
PATCH | /reviews/{id} | Approve or reject with reason |
GET | /reviews | Full list with filters |
DELETE | /reviews/{id} | Hard delete (GDPR erasure) |
GET/PUT | /settings | Store-level review settings |
Kafka Topics#
Consumed#
| Topic | Action |
|---|---|
order.fulfilled | Cache verified purchaser eligibility in Redis per product |
customer.anonymized | Anonymize or delete reviews for the customer (GDPR) |
Published#
| Topic | Payload |
|---|---|
review.submitted | reviewId, storeId, targetType, targetId, rating |
review.approved | reviewId, storeId, targetType, targetId |
review.rejected | reviewId, storeId, reason |
review.approved is consumed by catalog-service to update its own product rating cache for storefront display.
Vault Configuration#
| Key path | Purpose |
|---|---|
ss3/kv/review-service/db.url | PostgreSQL JDBC URL |
ss3/kv/review-service/db.username | Database username |
ss3/kv/review-service/db.password | Database password |
ss3/kv/shared/ | Shared Kafka bootstrap, OTel config, Redis URL |