1.8 KiB
Architect — F-018 Cart module
Feature
F-018 adds authenticated customer carts that persist only product/variant/quantity and recalculate price and availability on every read.
Design
Module boundaries
Create project/src/modules/cart/ with domain, application, infrastructure, api and tests. Cart owns cart persistence only; it does not own price or stock truth.
Cart depends only on public PricingServicePort and InventoryServicePort contracts injected by the composition root. It must not import pricing/inventory internals and must not store price, tax, stock or discount columns.
Data model
Add migration 013_cart.js:
cart_carts:id,user_id unique, timestamps.cart_items:id,cart_id,product_id,variant_id,quantity, timestamps.- Unique
(cart_id, variant_id)so adding the same variant increases quantity. - CHECK
quantity > 0.
Use cases
addItem(userId, { productId, variantId, quantity })changeQuantity(userId, variantId, quantity)removeItem(userId, variantId)getCart(userId)
getCart always recalculates every item with PricingService.calculate({ variantId, quantity }) and InventoryService.checkAvailability(variantId, quantity). If price is missing, mark item as unavailable for pricing instead of trusting stale data.
API
Authenticated routes:
GET /cartPOST /cart/itemsPATCH /cart/items/:variantIdDELETE /cart/items/:variantId
Request schemas strip unknown fields so client price payload is ignored.
Acceptance trace
- Price changed after add: cart stores no price, read recalculates through PricingService.
- Out-of-stock: cart read calls InventoryService and flags unavailable.
- Client price fields ignored: API schemas strip unknown fields and persistence has no price columns.
verify.shgreen after gates.