"Success, Reported Prematurely"

Most of the open questions from last week got answered. Then a new one showed up right at the end.

Week of July 27, 2026

Progress

  • Cart quantity updates and deletions fully wired up in the UI
  • Cart snapshot assembly built to package the current cart state for laBoutique
  • Debounced update pattern implemented (500ms), backed by a sendBeacon() call in case the user navigates away before the timer fires
  • Command DTO and handler added for cart updates, following the same class pattern already used for items
  • laBoutique's API routing unified onto a single front-facing pattern, with context (store vs. catalog) folded into the path
  • New PUT /cart endpoint added on laBoutique for sending a full cart snapshot, alongside the existing POST /cart for item-level updates
  • Two days went to outside client work (PPH), which is part of why the bug below is still open

The Hard Part

This answered last week's open question about when to sync cart updates: debounce (500ms) with a sendBeacon() fallback, sending the complete cart snapshot every time instead of tracking a diff. Simpler, and it doesn't require any client-side state tracking. I'd also assumed I needed to send a cart ID along with the snapshot, but talking it through, laBoutique doesn't need one — it identifies the cart from the user ID and key alone, and decides on its own whether that's a new cart or an existing one.

Building the backend side of that surfaced something less clean. I started implementing cart updates using the same command/DTO/handler pattern as items, for consistency, and ran into an interface I hadn't actually adhered to yet, which broke things until I fixed it. That work exposed a real inconsistency in laBoutique's API: routing was split between a "front-facing" pattern for reads and an /api/{interface} pattern for everything else, and "context" — whether a request is scoped to the store or the catalog — was handled inconsistently. /store and /catalog implied context on their own; /cart needed it passed as a parameter. I unified all of it onto the front-facing pattern, with context always living in the path (/store/cart vs. /catalog/cart).

The week closed with a new problem still open: the cart update was returning an "invalid JSON" error partway through, and once that cleared, the handler turned out to report success regardless of what laBoutique actually returned — which right now is failure.

Decision Made

Send a complete cart snapshot on every update rather than a diff, and drop the cart ID entirely. laBoutique resolves the cart from the user's ID and key; tracking a diff or a cart ID on the client would just be state I don't need to own.

Debounce cart updates (500ms) with a sendBeacon() fallback for navigation-away cases. Answers the update-timing question from last week without needing to track what specifically changed.

PUT /cart for a full snapshot replace, POST /cart for item-level updates. Two different shapes of update, so two endpoints — matches how the two cases are actually used rather than overloading one.

Next Week

Chase down the invalid JSON error and fix the handler so it actually reflects what laBoutique returns, instead of assuming success.