Frontend architecture¶
Source and runtime ownership¶
web/landing, web/store and web/admin are independent Git submodules. Each
owns a Next.js App Router application, strict TypeScript configuration, npm
lockfile, tests, static-export Dockerfile and CI workflows. No source imports
cross a submodule boundary and no JavaScript monorepo or module federation
runtime is introduced. Shared design/API conventions are documented centrally.
Each app uses src/app for routes, src/components for feature and shared UI,
src/lib for schemas/API access, and tests/ for browser checks and fixtures.
The platform owns Compose, Nginx, the submodule commits and integration docs.
Gateway/Account own the backend changes. Kubernetes frontends are deferred.
One domain, static zones¶
| Public path | Destination | Behavior |
|---|---|---|
/ and landing pages |
Landing export | Root assets at /_next/* |
/store/ |
Store export | Fixed /store base path; all assets/manifests remain under it |
/admin/ |
Admin export | Fixed /admin base path; independent assets |
/api/* |
Gateway | Nginx strips /api only; authenticated responses use no-store |
Legacy /auth, /accounts, /products, /orders, /exchange paths |
Gateway | Existing Bearer API clients remain supported |
Exact /store and /admin requests redirect to their slash form; prefix
matching cannot capture /storefront. Nginx serves actual exported paths and
returns a zone-specific 404 rather than rewriting unknown URLs to a shell.
Cross-zone links are ordinary anchors and load the destination app. Next.js
routing inside the store is used after demo order creation.
All apps use output: 'export', trailingSlash: true and unoptimized static
images. There are no server actions, BFFs or runtime Next.js session handlers.
Product/order IDs are validated query parameters to statically exported detail
pages. Public catalog data is fetched at runtime; the initial export does not
require an available Product service or embed stale demo product data.
The approach follows official static export, basePath and multi-zone guidance. The future CDN needs equivalent path-to-origin behavior, trailing-slash/404 rules and an API origin. Choose those with the owner at deployment time.
API-owned browser sessions¶
| Gateway endpoint | Purpose |
|---|---|
GET /session/csrf |
Return the Spring CSRF token/header; establish its cookie |
POST /session/login |
Call Auth, set the access token in an HttpOnly cookie, return 204 without a token body |
GET /session |
Validate the token and fetch the authenticated Account profile |
DELETE /session |
Clear the session cookie; no refresh/revocation capability is claimed |
Through the public proxy these endpoints are /api/session/*. The host-only
ms52-session cookie has path /api, SameSite=Lax and HttpOnly. It is Secure
by default; local HTTP Compose explicitly sets SESSION_COOKIE_SECURE=false.
Its lifetime follows Auth's access-token expiry. The frontend never stores or
reads JWTs in localStorage, sessionStorage or a JavaScript cookie.
Spring Security's cookie CSRF repository and default request-token handler protect cookie-authenticated writes and login. Every frontend mutation obtains a token first. The Gateway request wrapper turns the cookie into a Bearer header for token validation and downstream routing; explicit Bearer clients retain their existing behavior. Upstream login/profile requests have timeouts. The signing key stays in backend secret configuration.
Gateway maps the signed roles claim to authorities, requires ROLE_ADMIN for
Product writes and account list/detail/deletion, and blocks public internal
credential routes. Only GET product reads are anonymous. Account enforces its
own admin list/detail/deletion rules and exposes GET /accounts/me for the
authenticated user. Administrator/self deletion is rejected; account deletion
is not exposed in the frontend. Admin provisioning is an operator action,
never a registration input.
The route guard is presentation only. Tests verify backend role enforcement, CSRF and an actual CSRF-cookie/login/profile exchange with local upstream fixtures. Logout clears the cookie/client caches and notifies other tabs. Receiving tabs use TanStack's query reset to notify active subscribers and refetch session state, so an open private view does not retain the previous profile after another tab signs out. An already-issued token remains valid until expiry if copied elsewhere; there is no fabricated refresh/revocation endpoint.
State and optimization¶
TanStack Query owns API reads/mutations and schema-checked responses. Query
keys isolate private order data by identity; session changes reset cached data.
Reads have bounded retries/cancellation, mutations never retry automatically.
The current implemented cart stores only validated IDs/positive integer quantities under
ms52-cart-v1, synchronizes with browser storage and revalidates catalog data
before submission. Order receives IDs/quantities and calculates authoritative
prices. An uncertain submission directs users to order history before retry.
Search/sort operate locally on the unpaginated Product array. React deferred rendering keeps input responsive; it is not called network debounce. TanStack Table v9 uses explicit sorting/pagination features and 10-row pages. No virtual list is added without evidence of a large-list rendering problem, and no browser background queue is implemented. Backend Kafka opportunities are proposed separately from UI state. New server search/pagination, debounce or queue packages require a separate contract/approval.
Selected server cart and infrastructure direction¶
The future dedicated Cart service will own Redis carts with seven-day expiry after the last successful mutation; reads do not renew TTL. Preserve guest and account carts, ownership, CSRF, revision checks and explicit migration from browser storage. No Cart API or Redis-backed store behavior is implemented yet.
Grafana Cloud is selected for telemetry and Cloudflare for DDoS/WAF/Tunnel security. The static CDN provider and domain name remain undecided.
Remaining service prerequisites¶
- Product/Exchange source and usable local images are absent. Verify Product accepts anonymous GET, enforces its own write permissions and matches the documented id/name/price/unit payload before real-service acceptance.
- The checked-in Order returns
productIdin items and rejects non-USD totals. The frontend uses that payload and USD only. - Exchange route naming differs between docs and Gateway; currency UI is deferred.
- Order creation lacks documented idempotency; the frontend prevents duplicate clicks and automatic retries but cannot promise exactly-once order creation.
- All-orders administration, account editing/role changes, product editing, product media/stock/categories and financial reporting require new contracts.
The fixture-only Docker mode is for UI verification and never proves these real-service prerequisites. Backend and frontend release commits must be reviewed in their owning repositories before integration pointers are promoted.