Every commerce project I've worked on started with a conversation about the storefront — the theme, the product grid, the checkout. And every one of them got hard somewhere the customer would never look.
The storefront is a solved problem. There are mature platforms, good component libraries, and decades of UX patterns. You can build a beautiful one quickly. What you can't shortcut is everything behind it, and that's where commerce projects actually live or die.
Pricing is a system, not a number
A price looks like a field on a product. It almost never is.
In any serious B2B catalogue, the price depends on who's asking: a contract tier, a volume break, a customer-specific agreement, a promotion that stacks (or doesn't), a currency, a tax regime. The "price" is the output of a small rules engine, evaluated per customer, per context, in real time.
The mistake is pushing that logic into the storefront or the commerce platform. Keep it in a service you own:
public interface IPricingEngine
{
// Price is computed from context, not read from a column.
Task<Price> ResolveAsync(ProductId product, PricingContext context, CancellationToken ct);
}
When pricing is its own service, you can test it, version it, and reason about it. When it's smeared across templates and platform config, every change is a guess.
The order lifecycle is the real product
A customer sees "Buy." What that triggers is a state machine that has to survive partial failure at every step:
Placed → Authorised → Reserved → Fulfilled → Settled
│ │ │
(fails) (fails) (fails) → compensate
Payment can authorise but inventory reservation can fail. Fulfilment can succeed but settlement can lag. Each transition crosses a system boundary — payments, inventory, ERP, the warehouse — and any of them can be down when you need it.
This is why event-driven design fits commerce so well. Each step emits an event; each downstream reacts and can be retried independently; nothing has to hold a giant distributed transaction open while a warehouse API thinks about it. (And every one of those handlers has to be idempotent — a reservation must not double-book because a message arrived twice.)
Integrations are most of the work
The honest breakdown of a mid-market commerce build is rarely "70% storefront." It's something closer to:
- a slice of storefront,
- a pricing and promotions service,
- an order orchestration layer,
- and a thick band of integrations — ERP, payment providers, tax, shipping, the CRM, the email platform, analytics.
The integrations are where the schedule goes. Other people's APIs are inconsistent, rate-limited, occasionally wrong, and never quite shaped like your domain. Front them with services you own so their mess stays at the edge instead of leaking into your core.
What this means in practice
When someone asks me to "build a store," the first questions are never about the storefront:
- How is a price actually decided, and for whom?
- What has to be true for an order to be considered complete?
- Which external systems must agree before money moves?
- What happens when one of them is down?
Get those right and the storefront is the easy part. Get them wrong and no amount of front-end polish will save the project.
Effortless experiences are built on the plumbing nobody sees. In commerce, the plumbing is the product.