# 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 /cart` - `POST /cart/items` - `PATCH /cart/items/:variantId` - `DELETE /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.sh` green after gates.