"Architecture Tells You What It Needs"

Most of this week's design decisions weren't made ahead of time. They were extracted from the code.

Week of June 29, 2026

The Hard Part

The store front work kicked off with what seemed like a question about page layout: products and cart on one page, or two separate pages? I landed on two pages — one for the product listing, one for the cart — mainly for simplicity. That part was easy.

What wasn't easy was everything that surfaced once I started writing the actual code.

A conversation with Claude Code about UX layout options included an offhand comment about my store.php: functions should be defined at the top to be more readable. That observation shouldn't have led to much, but pulling the functions upward made it obvious they belonged in a class. The class needed an interface. By the end of that thread, I had a formal LaBoutique service class and interface in place — which is the right structure, but none of it was planned. The code asked for it.

Then came the naming problem. I'd started with getProducts(), getCart(), and checkout(). Reasonable. But when I actually looked at what the LaBoutique /store endpoint returns, it's not just products — it's a product list and a cart summary bundled together. So getProducts() is technically wrong about what it returns. My first instinct was to store the cart ID and count in the service object so I wouldn't have to pass it around — but that would turn the service into a local cache and undermine LaBoutique as the single source of truth. A service is a pass-through, not a cache. The fix was simple: rename the function getStore() to match what the endpoint actually returns and let the rest of the code deal with the response intact. But I had to write my way to that conclusion.

The week closed out with a new problem that's still in progress: the add-to-cart button. A single-price item is easy — one button with the price. But subscriptions have both monthly and yearly pricing options, and a plain button can't hold two prices. The approach I'm taking is a select/button hybrid: a dropdown on the left to choose the price tier, a cart SVG on the right to confirm. The PHP builds two completely different structures depending on which case it's in. Getting the logic right has been the focus; styling comes next. There are new CSS appearance: base-select options landing in Chrome that would make native select elements finally style-able, which would be perfect for this. Chrome-only for now, but the progressive enhancement path seems viable.

Progress

  • Layout decision made: two-page store (products and cart)
  • LaBoutique service class with interface implemented
  • ProductDTO and CartDTO added, following the existing DTO pattern in the rest of the app
  • index.php restructured to accept page-specific CSS and JS as variables
  • Product listing rendering with names and descriptions — no styling yet
  • Add-to-cart button underway; single-price path mostly working, multi-price path in progress

Decision Made

getStore() instead of getProducts() — the endpoint returns more than products and naming it honestly prevents the urge to quietly reshape the response inside the service layer. If a narrower getProduct() is ever needed, it can be broken out at that point.

SVGs for all icons, defined as constants in icons.php — better scaling, less page weight, one place to maintain everything. Making this call now, before the UI gets further along, seemed cleaner than retrofitting it later.

The Question That Appeared

The new appearance: base-select CSS gives native <select> elements full styling control for the first time — which would be ideal for the multi-price cart button. But it's currently Chrome-only. The question is whether to lean into it now with a fallback, or wait for broader support. I'm going to try the progressive enhancement route and see how far it gets.

Next Week

Finish the multi-price cart button and get it wired up. Then the cart page itself. The store is close.