コンテンツにスキップ

Storefront lane / provider 再設計方針

1. 目的

Storefront は会員制 B2B EC を正本としており、公開 Web 向けの SSG / shared cache 最適化だけでは受け入れ観点を満たしにくい。
本書は、Vite 実装へ移行した後も、route ごとの目的に合わせて provider と最適化方針を分ける基準を定める。

2. 基本方針

  • 正本は apps/storefront (Vite) である。旧 apps/storefront-next 実装は削除済み。
  • Storefront 全体を 1 つの同質なアプリとして扱わず、lane 単位で最適化目標を分ける
  • 認証依存 route では、public page と同じ cache 発想を前提にしない。
  • layout と global provider は、対象 route に必要な責務だけへ狭める。

2.1 切替後の命名状態

  • 正本 (Vite): apps/storefront / ritsubi-storefront
  • apps/storefront-next / ritsubi-storefront-next 実装は 2026-05 に削除済み。

3. Lane 定義

3.1 Storefront lane

対象の例:

  • / (home — featured products と ProductCard を含む commerce surface)
  • /products
  • /search
  • /products/[slug]
  • /cart
  • /checkout
  • 商品導線と一体の campaign route

優先する観点:

  • 商品や価格など、主要情報の first visible を早く出すこと
  • deep link で開いた時に、主要コンテンツが先に見えること
  • 商品本体以外の付帯情報が後着しても、購入導線が阻害されないこと
  • 顧客別の表示制御・価格・購入条件が前提のため、SPA shell を返す前に activeCustomer を検証すること

受け入れ時に見る観察結果:

  • 商品一覧で、ブランド一覧や付帯 UI より先に商品グリッドが見える
  • 商品詳細で、主要画像・商品名・価格・購入可否が先に表示される
  • カート / チェックアウトで、注文処理に不要な UI に待たされない

3.2 App lane

対象の例:

  • /account/*
  • /quick-order
  • 今後の業務寄り一覧・フォーム・履歴画面

優先する観点:

  • 画面内操作のしやすさ
  • 状態保持、mutation 後の再表示、連続操作時の安定性
  • SSR よりも interactive な利用体験

受け入れ時に見る観察結果:

  • 一覧画面の絞り込みや更新操作が連続しても破綻しない
  • 保存や再計算の結果が、画面遷移なしでも確認できる
  • 顧客情報・注文情報が操作に必要な範囲で一貫して見える

3.3 Content lane

対象の例:

  • /articles/*
  • /announcements/*
  • /support/*
  • /policies/*
  • /pages/*
  • /products/preview (Vendure Dashboard が発行する signed preview token で保護する商品プレビュー)

優先する観点:

  • CMS コンテンツの表示安定性
  • commerce / account 向け state を持ち込まない軽さ
  • 運用担当者が内容更新後の反映を追いやすいこと

受け入れ時に見る観察結果:

  • 記事・お知らせ・サポートページが、顧客状態に依存せず安定表示される
  • content route に商品購入系の余計な待ち時間や UI が混ざらない
  • 商品プレビューは通常の商品詳細ページと同じ表示構成を保つが、購入・同意確認・閲覧履歴記録などの副作用は発生しない

4. Provider 配置方針

4.0 認証境界

  • /_site 配下は activeCustomer 必須 とする。//products/search/products/$slug/campaigns/*/collections/*/cart/checkout/quick-order/reservations/account/* は cookie の存在だけでは描画しない。
  • Storefront Worker の navigation guard と browser route guard は、どちらも /api/auth-session / validateStorefrontSession() 経由で Vendure activeCustomer.id を確認する。stale cookie や削除済み customer は login redirect と cookie clear に倒す。
  • 表示性能のために遅延してよいのは CMS shell、active order、下段 carousel、 product detail preload などの付帯 fetch であり、認証境界そのものは遅延・省略しない。
  • 未認証公開面は /_content 配下へ明示的に分離する。公開例外を増やす場合は route を contentRoute へ置き、商品・価格・顧客別表示制御に依存しないことを仕様に残す。
  • browse-shell や cookie-only session 判定のような軽量認証経路は使わない。過去の performance workaround を戻す場合も、activeCustomer 検証を bypass してはいけない。

4.1 Root に残すもの

原則として、全 route に必要な軽量 provider のみを置く。

候補:

  • PublicConfigProvider
  • DisableDarkTheme
  • MockServiceWorkerProvider
  • 必要最小限の TooltipProvider

期待結果:

  • content route や KPI route でも、root 配下の client boot が増えすぎない

4.2 Storefront / content lane に置くもの

候補:

  • ConsentProvider
  • CustomerProvider
  • 商品購入導線に必要な provider

期待結果:

  • 商品・カート・チェックアウトでは必要な機能が使える
  • 記事・お知らせ・サポートでも B2B chrome の顧客バーが必ず成立する
  • signed preview 以外で顧客バー欠落を匿名 fallback として隠さない

4.3 App lane に置くもの

候補:

  • CustomerProvider
  • 顧客 / 注文 / mutation を前提にした state provider

期待結果:

  • /account/*/quick-order で顧客情報と注文情報を一貫して扱える
  • Storefront lane の KPI route に customer / active order query を持ち込まない

4.4 Signed preview に置かないもの

/products/preview は Vendure Dashboard が発行する signed preview token で保護する例外 route とし、 以下を持ち込まない。

  • CustomerProvider
  • ConsentProvider

期待結果:

  • 商品プレビューでは認証済み顧客 chrome や購入系副作用を発生させない

/products/preview も content lane に含める。商品詳細と同じ構成を確認するため、 関連商品・購入共起・閲覧履歴などの表示セクションは通常ページと同じ描画経路を通す。 一方で、signed preview では ConsentProvider を mount しないため、 AddToCartButton のように購入 hooks / 同意 hooks を呼ぶコンポーネントを直接描画しない。 購入 CTA の位置は hook を使わない disabled 表示で保ち、プレビュー識別は本文へ専用 ブロックを差し込まず floating indicator に集約する。

5. Header の扱い

  • Header は lane 再配置の制約点になるため、header 用の最小表示データと、account / cart 用の完全 customer context を分ける。
  • 具体的には、navigation guard で activeCustomer が確認済みであることと、Header が activeOrder の full context を初期表示で待たないことを分ける。
  • /productsactiveOrder 非依存 route として扱える状態を目標にするが、 activeCustomer 非依存 route にはしない。

受け入れ時に見る観察結果:

  • /products で商品一覧の表示が先に出て、header の顧客情報待ちで遅くならない
  • /account/* では必要な顧客表示・注文状態が引き続き見える

6. KPI route と計測

KPI route:

  • /products
  • /products/[slug]
  • /cart
  • /checkout

最低限見る項目:

  • Document TTFB
  • first visible
  • hydration 完了までの時間
  • 初回 query 数
  • 初回 JS 量

判断基準:

  • 認証済み route では、shared cache の有無だけで良否を判断しない
  • 主要コンテンツが先に見えるかどうかを優先する

7. 段階的な移行順

  1. lane 定義を文書で固定する
  2. Header の customer / order 依存を分解する
  3. Storefront lane から不要な customer / order query を外す
  4. desktop の顧客バーをカート導線の正本にし、匿名右上カート fallback を外す
  5. signed preview だけ customer / consent provider を外す

8. 切替後の判断 (2026-05)

  • Storefront 正本は Vite 実装 (apps/storefront / ritsubi-storefront) へ移行済み
  • apps/storefront-next / ritsubi-storefront-next 実装は 2026-05 に削除済み
  • Storefront の主戦場は「公開 Web」ではなく「認証済み storefront」である
  • framework 置換だけでなく、lane 分割と provider 境界の整理を移行の前提条件として優先する

8.1 routing facade の現状

  • useRouter / usePathname / useSearchParams の互換 hook は撤去済み。
  • 現在 apps/storefront/src/lib/router.tsx に残すのは Link wrapper のみとする。
  • 理由: 現行 codebase には string ベースの route 遷移(例: /account/orders/${code}, /products?collection=${slug})が広く残っており、TanStack Router の厳密な route literal typing へ一気に寄せると変更範囲が過大になるため。
  • 今後 Link wrapper を削除する条件は、主要 UI で使う to を route object / typed param 化し、string route 依存が十分に減った時点とする。

8.2 legacy product route の扱い

  • canonical product route は /products/[slug] とする。
  • /product/[slug] redirect route は削除済み。
  • 新規実装・CMS 正規化・モック・テストでは /product/ を生成しない。
  • WordPress / CMS 由来の内部リンクは content-processor.ts/products/ へ正規化する。
  • 今後 /product/ へのアクセスが必要になった場合は、router へ戻す前に、どの upstream が旧 URL を出力しているかを先に修正する。

9. 昇格条件(完了済み)

以下を満たした状態で切替を実施した。

  1. 会員制 storefront の主要導線が Vite 側で成立していること
  2. 認証済み route に必要な query / mutation が TanStack Query ベースで揃っていること
  3. Playwright CT / 主要 Vitest / typecheck が Vite 側の正本導線で回ること
  4. docs / CI / just / Nx の ritsubi-storefront 参照を Vite 側へ移しても運用上の空白が生じないこと
  5. apps/storefront-next 実装は 2026-05 に削除済み。すべての検証・保守は apps/storefront (Vite) へ一本化されている。

10. 実施バックログ

本節は PLAN.md § 15(2026-05-01 バックログ)から移植し、lane 設計の正本としてここに集約したものです。

背景

  • Storefront は会員制 catalog を正本としており、公開 Web 向けの SSG / shared cache 最適化より、認証済み request-time rendering の負荷抑制が重要である。
  • 現状は route 単位の最適化が進んでいる一方、apps/storefront/src/providers.tsx 配下の global provider が広く効いており、/products のような KPI route に account / app 寄りの client-side cost が混入している。
  • Vite + TanStack Router を正本としたうえで、Storefront を lane ごとに責務分離して継続利用する方針を取る。

優先実施項目

優先度 領域 改善点 根拠 / 観測 実施方針
P0 Storefront 設計 Storefront の route を storefront lane / app lane / content lane に分類する /products・商品詳細・カート・チェックアウトは first visible 重視、/account/*/quick-order は操作性重視、記事/お知らせ/サポートは CMS / content 重視で、同一設計では最適化目標が衝突する。 本文書(§ 3 Lane 定義・§ 4 Provider 配置方針)を正本として lane 定義、対象 route、期待する観察結果を確定し、以後の UI/性能改善は lane 単位で判断する。
P0 Provider 境界 global provider を root から棚卸しし、lane 単位に再配置する apps/storefront/src/providers.tsxCustomerProvider / ConsentProvider / Sentry UI provider が全 route に適用されており、signed preview に不要な hydration / query cost が混入する。 root には軽量 provider のみを残し、CustomerProvider は signed preview を除く site shell 全体で mount する。ConsentProvider は通常 site shell で mount し、signed preview だけ外す。
P0 Header / 顧客表示 Header の customer / order 依存を分離し、顧客バー欠落を fallback で隠さない HeaderuseCustomer() に依存しているため、provider を lane 配置へ動かす前提条件として「header 用最小データ」と「account / cart 用完全 context」の分離が必要。 header 用の最小 customer summary と full customer / active order context を別導線に分ける。desktop のカート導線は顧客バー内の cart summary を正本にし、匿名右上カート fallback は置かない。
P1 KPI 管理 Storefront lane の性能指標を TTFB 単独から first visible / hydration / initial query 数へ拡張する 会員制 catalog では shared cache より request-time work 削減が支配的で、TTFB のみでは /products の検索・絞り込み後 UX を十分に捉えられない。 § 6 KPI route と計測の定義を基準として、Document TTFB、first visible、hydration 完了時間、初回 query 数、初回 JS 量を継続観測する。
P1 Route group 構成 既存 (site) / (protected) を lane 指向に再整理する 現在の route group は認証保護の境界としては有効だが、storefront / app / content の最適化責務までは明示できていない。 必要に応じて storefront lane 用 route group を追加し、既存 (protected) は app lane の責務として再定義する。命名と境界は実装変更前に文書で確定する。

現状(2026-05-21 時点)

  • lane 定義・Provider 配置方針・KPI route の定義は本文書で確定済み。
  • signed preview の query 抑制: CustomerContextProvider/products/preview を signed preview と判定し、activeCustomerContext / activeOrderContext の 2 query を enabled: false で skip する (apps/storefront/src/contexts/customer-context.tsx)。
  • Header の customer 依存分離完了: Header から useCustomer() 直接呼び出しを撤去し、customerState prop 経由で受け取る形に変更 (apps/storefront/src/components/layout/header.tsx)。RootLayout 内の SiteShell コンポーネントが customerState を resolve して渡す責務を持つ。
  • signed preview 以外で customer provider を mount: RootLayout/products/preview 以外の site shell を StorefrontCustomerProviders / ConsentProvider で wrap し、content route でも顧客バーを成立させる (apps/storefront/src/layout/root-layout.tsx)。root の NoopCustomerProvider は preview / SSR fallback のためだけに残す。
  • lane 判定の SSoT: apps/storefront/src/lib/storefront-lanes.tsisContentLanePath / isAppLanePath / isStorefrontLanePath を集約し、CustomerContextRootLayout の双方が同一基準で動く。
  • 残作業: home / を独立 lane として明示する整理は未着手 (現状は非 content lane として storefront lane の provider 群を流用)。route group の物理的な再編 ((content) / (commerce) 等) も未着手で、現状は path-prefix 判定で代替している。