42 lines
1.8 KiB
Markdown
42 lines
1.8 KiB
Markdown
# 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.
|