Happy House - Ecommerce Docs
Developer ResourcesCart

Cart Features and Flows

Complete feature list, actor journeys, state flows, business rules, edge cases, and diagrams for the Cart module.

Cart Features and Flows

Use this page for the cart domain: what it does for customers and admins, and how each flow behaves from start to finish.

1. Documentation Evidence

Source TypeFiles or DocsWhat Was Extracted
APIapps/api/src/modules/cart/customer/cart-customer.controller.ts, admin/cart-admin.controller.tsRoutes, status codes, idempotency scopes
Backendcart-write.service.ts, cart-bulk.service.ts, cart-query.service.ts, cart-response.builder.tsOne-statement UPDATE, line validation, pricing
Schemapackages/db/src/schema/cart/{cart,cart-item,enums}.tsPartial unique, caps, status enum
Inventory seamInventoryAvailabilityService.checkQuantitiesThe stock question goes here
Error registryapps/api/src/common/types/error-codes.ts (// CART)CART_* codes

2. Feature Summary

FieldValue
Modulecart
SubmoduleN/A
Primary user valueA customer assembles what they intend to buy, with live prices and stock, a safe debounced stepper, and a strict pre-checkout validation pass
ActorsCustomer (signed in), admin (read-only)
Main entry points/api/mobile/cart (8 routes), /api/admin/carts (2 routes)
Main outputsWhole-cart responses, badge counters, checkout validation reports
Related docsAPI, Backend

3. Actor Matrix

ActorCan DoCannot DoAuth RequirementNotes
CustomerRead cart, badge counters, checkout validation; add, set quantity, remove, clear, bulk-editAdd a product that is not published/unlisted (404), exceed 50 distinct products (409) or 99 per line (409), mutate a locked cart (409)JWTCUSTOMER_READ 60/min, CUSTOMER_CART_MUTATION 60/min — account-keyed
AdminList carts (always paginated), read one cart with linesMutate any cart, see the storefront product contract or the customer recordAdmin JWT + Cart_READRead-only by design; no admin mutation planned
Checkout (future)Lock the cart (checkout_locked), convert itForget the unlock on payment failure/cancellation/expiryInternalThe obligation in §0

4. Capability Matrix

CapabilitySurfaceActorRoute/TriggerState ReadState WrittenLinked API Section
Read cartCustomerCustomerGET /api/mobile/cartcart, items, live productsAPI §4
Badge countersCustomerCustomerGET /api/mobile/cart/summarycart, items— (30s cache)API
Checkout validationCustomerCustomerGET /api/mobile/cart/checkout-validationeverything, freshAPI
Add itemCustomerCustomerPOST /api/mobile/cart/itemscart, productcart_itemAPI
Bulk editCustomerCustomerPOST /api/mobile/cart/items/bulkcart, productsmany cart_item rowsAPI
Set quantityCustomerCustomerPUT /items/:variantPublicIdcart, productcart_itemAPI
Remove lineCustomerCustomerDELETE /items/:variantPublicIdcartcart_itemAPI
Empty cartCustomerCustomerDELETE /itemscartcart_item rowsAPI
Admin listAdminAdminGET /api/admin/cartscarts + aggregatesAPI
Admin detailAdminAdminGET /api/admin/carts/:cartIdcart + linesAPI

5. User-Facing Flows

5.1 The debounced quantity stepper

Summary

The backend cannot debounce; the client does. What the backend guarantees: a coalesced flush is safe, and a retried flush cannot double-apply. The stepper uses PUT with an absolute quantity; retrying sets the same number twice, which is the same number.

Branches and Edge Cases

BranchConditionBehaviorError/Result
RetrySame absolute quantityNo double-apply200
Product not in cartFirst setUpsert adds it200
quantity: 0Stepper at bottomLine removed — even if the product was archived200
Stale versionAnother device wrote409 CART_VERSION_CONFLICT + current cartRefetch and reapply
Version omittedProduct-card addLast-write-wins200
Resulting quantity > 99Accumulation409 CART_QUANTITY_LIMIT_EXCEEDEDClamp at 99

5.2 Add from a product card

POST /items is a delta — the only non-idempotent verb, because a product card cannot know the absolute target (the customer may already hold three in a cart the page never loaded). It is the only route taking an Idempotency-Key (optional): retry with the same key replays the stored response instead of adding again. Adding an out-of-stock product succeeds — stock is reported on the line and blocks checkout, never the add.

5.3 The checkout-validation pass

GET /checkout-validation re-reads everything and trusts nothing previously loaded — that re-read is the point. Product existence/purchasability, inventory shortfalls and price changes are re-checked; blockingReasonsempty, cart_locked, items_unavailable, insufficient_stock. Shipping, promotions and address validation are deliberately absent — the cart does not know the delivery address, and GET /mobile/shipping/quote already answers serviceability.

5.4 The cart page

Invalid lines stay in the cart — the cart never silently removes anything — and are excluded from pricing.subtotal, with excludedItemCount saying how many.

6. Admin Flows

Read-only: list (always paginated) and detail. The admin list aggregates over the full cart ⋈ cart_item ⋈ product join; totals are computed at live prices over every line, including ones the customer surface excludes as unbuyable — an operator looks at what is in the cart, not at what would be charged. Lines carry a three-field product reference, never the storefront contract; the customer reference is a public id and a name.

7. Lifecycle and State Transitions

7.1 Cart status

FromEvent/ActionToGuard ConditionSide Effects
activecheckout begins (future)checkout_lockedWritten by checkout — nothing in cart writes itHolds the live-cart slot; mutations refuse
checkout_lockedpayment succeeds (future)convertedFrees the slot (partial index excludes it)
checkout_lockedpayment fails / cancels / expiry (future)activeCheckout must own this edgeUnlock; otherwise absorbing

Empty and ready-for-checkout are derived, not stored; expired/abandoned was rejected because marking an untouched cart abandoned means the customer who returns in three weeks finds it gone — last_activity_at carries that signal instead.

9. Data and Side Effects by Flow

FlowDB WritesCache EffectsJobsRealtimeAnalyticsNotifications
Add/set/remove/clear/bulkcart_item rows (+ cart bump)summary cache cleared
Read cart
Summarysummary read (30s)
Checkout validation

10. Error and Recovery Flows

ScenarioTriggerUser/System ExperienceRecoverySource
Locked cartCheckout in flight409 CART_LOCKED_FOR_CHECKOUTGo to the in-flight checkoutstatus guard
Version conflictConcurrent device409 + current cartRefetch and reapplyversion check
Line cap50 distinct products409 CART_ITEM_LIMIT_REACHEDRemove something firstlocked count
Quantity capLine > 99 by accumulation409 CART_QUANTITY_LIMIT_EXCEEDEDClamp at 99locked count
> 99 in one requestMalformed request400 validationClient fixDTO
Bulk duplicate productSame product twice400 CART_BULK_DUPLICATE_PRODUCTCoalesce before sendingDTO
Add of unsaveable productdraft/archived/deleted/unknown404 — same code for allRefresh the productanti-enumeration

11. Diagrams Required Per Module

  • Actor capability diagram — §3/§4.
  • Sequence diagram per major flow — §5.1/§5.4.
  • State machine diagram — §7.1.
  • Data side-effect diagram — §9.
  • Error branch diagram — §10.

12. Mandatory Feature and Flow Deep-Dive Pack

12.1 Feature Inventory With Minor Behaviors

FeatureMinor BehaviorActorTriggerUser/System ResultBackend Side EffectSource
StepperAbsolute PUTCustomerCoalesced flushSafe retryupsertservice
StepperZero removesCustomerBottom of stepperLine gone, even archived
AddIdempotency headerCustomerRetried tapNo double addinterceptorcontroller
AddOut-of-stock allowedCustomerSold-out productLine with reason
BulkOne version bumpCustomerMulti-line flushNo self-conflictsingle bump
BulkCap after all opsCustomerRemove 2 + add 2 at capSucceedsevaluated once
ReadNever creates cartCustomerFirst visitEmpty cart, null cartno insert
ReadtotalQuantity counts every lineCustomerBadgeNever drops on stock-out
Price changeAcknowledgeCustomerRe-send quantityBanner clearssnapshot refresh
SummaryNo versionCustomerBadgeNo self-conflictdeliberate
Admin listNo pagination=falseAdminBadge400deliberate
Admin listDate-only createdToAdminEnd of dayInclusive whole daydeliberate

12.2 Business Process Diagram Pack

12.3 Business Rules and Policy Traceability

RuleBusiness ReasonActor ImpactEnforced InAPI ImpactBackend ImpactTests
One statement = lock + status + version + bumpNothing forgettableSafe concurrent writesSQL UPDATEversion semanticsWHERE clausespec
Exact 50-line capLocked count12 concurrent adds admit onelocked count read409vs wishlist's approximate capconcurrency spec
last_known_unit_price not a snapshot"Changed since you chose" signalBannerwrite-on-mutationpriceChangeno totals from itspec
PUT upsert + zero removesStepper one pathArchived lines removableupsert200
Add allows out-of-stockStock changes minute to minuteLine with reasonlifecycle gate onlyblocks checkout not add
No new stock utilityReuse the seamConsistent answerscheckQuantitiescart = 3rd consumerreviewed
Oversell not invalidavailable is 0 for oversellCheckout not blockedseam predicatenaive < would block foreverspec
Locked refuses all mutationsCheckout integrity409status guardabsorbing until checkout owns unlockspec
A cart line is (cart, variant), never (cart, product)The 256GB and the 512GB of one phone are two independent, independently priced linesSet-quantity and remove are addressed by variantPublicId; variant.name is null only when the variant IS the productuq_cart_item_cart_id_variant_idvariant block on every item; path param is :variantPublicIdvariant_id column, product_id retained denormalisedspec
Removal that removes nothing does not bump versionA stale product-id in the removal path must not invalidate every other device's token for a no-op200, cart unchangedCartWriteService.runRemoval splits lock+version-assert from the bumpbumpVersion called only when apply returns truespec

12.4 Tradeoffs and Product Rationale

Product DecisionUser BenefitEngineering BenefitAlternativeTradeoffRisk
Absolute PUT for stepperRetry-safe debounceOne code pathDelta PUTClient must know quantityDocumented
One non-idempotent POSTProduct card works blindSingle interceptor pathAll-deltaRetry needs headerOptional key
Live data on readNever staleNo snapshot syncSnapshot on addRead costBatched seam
Three stored statesDerived emptiness/readinessMinimal writesFive statesAbandoned not storedlast_activity_at
Locked absorbingFrozen contractCheckout owns returnAuto-unlock timerNo recovery until checkoutProminent obligation
Admin read-onlyNo cart tamperingNo audit burdenAdmin editsOperators can't fixAccepted

12.5 Flow Edge-Case Matrix

FlowEdge CaseTriggerExpected BehaviorUser/System FeedbackSource
AddRetry without headerDouble tapDoubles (documented)200non-idempotent verb
AddRetry with headerSame keyReplays stored response200interceptor
BulkSame LINE (resolved variant) twiceBad client, or mixing "always send variant" with "never send it" in one batch400BULK_DUPLICATE_PRODUCTtwo DIFFERENT variants of one product in one batch is legal
BulkCap reached mid-batchRemove+add at capSucceedsevaluated once
SetProduct archivedOld lineStill removable200zero-removes exemption
RemoveNever savedAny id, or a product id sent where a variant id belongs (both are uuid7)200 no-op, cart version not bumpedno CART_ITEM_NOT_FOUND, anti-enumeration
ReadNo cartFirst visitNull cart, zeroed countersGET never creates
SummaryCachedOther deviceUp to 30s stalebadge lagTTL
Checkout validationPrice changedAdmin repriceanyChanged truenever blocks
Untracked productAvailable nullLineAlways validavailableQuantity: nullseam

12.6 Flow-to-Data Trace

FlowReadsWritesCacheJobs/EventsResponse Fields
Read cartcart, items, products, inventorycart, customer, summary, items, pricing, validation
Summarycart, items30stotalItems, totalQuantity
Add/set/removecart, productcart_item, cart bumpclear summarywhole cart
Bulkcart, productsmany cart_itemclear summarywhole cart
Validationeverything freshreadyForCheckout + groups
Admin listcarts + aggregatesrows + pagination

12.7 Experience Quality Checklist

  • The doc explains what the actor is trying to accomplish.
  • The doc explains what the backend does that the actor does not see (one-statement UPDATE, seam, cache).
  • The doc covers every minor flow and branch.
  • The doc includes user, admin and system flows.
  • The doc explains business logic, tradeoffs, and rationale.
  • The doc maps every flow to API routes and backend side effects.
  • The doc includes diagrams appropriate to each flow type.
  • The doc covers edge cases and failure recovery.

13. Completion Checklist

  • Every feature, minor action, and submodule capability is listed.
  • Every actor has allowed and forbidden behavior.
  • Every major and minor flow includes steps, branches, and diagrams.
  • Every lifecycle has a transition table and state diagram.
  • Every flow links to the API and backend docs.
  • TDD dependencies are called out where they shape behavior (no TDD pages published yet).

See Also