# 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.
