ADR-004: React SPA + BFF アーキテクチャの採用
- ステータス: Accepted
- 日付: 2025-12-16
- 決定者: アーキテクトチーム
コンテキスト
TASHIKA は4種類のユーザーロール(従業員、企業管理者、代行事業者、システム管理者)に対して、それぞれ異なるワークフローと UI を提供する必要がある。フロントエンドアーキテクチャの選定にあたり、以下の要件が判断を左右する:
- モードレス自動保存: 明示的な保存ボタンを持たず、ビュー遷移時に自動保存する。リアルタイムの入力→計算プレビュー→保存のサイクルが必要
- Born Digital 原則: 給与データ・家族データ・電子的控除証明書をプリフィルし、従業員は「確認するだけ」の UX を目指す。データ取得・変換・表示のレイテンシを最小化する必要がある
- 4ポータル分離: 従業員(7言語対応、モバイル重視)、管理者(日本語のみ)、代行事業者(マルチテナント横断)、システム管理(監査・設定)の4つの独立したワークフロー
- 税年度スコーピング: 全操作が
{taxYear}に紐づき、URL・API・キャッシュすべてに年度コンテキストが必要 - セキュリティ: マイナンバー等の機密データの表示制御がポータル・ロールごとに異なる
決定
React SPA(Single Page Application) をフロントエンドとし、BFF(Backend for Frontend) をバックエンドとの中間層として配置する4ポータル構成を採用する。
- SPA: React 19 + TypeScript 5.x + Vite
- ルーティング: TanStack Router(型安全ファイルベースルーティング)
- サーバー状態: TanStack Query(キャッシュ・再検証)
- BFF: ASP.NET Core(API Gateway + ポータル固有のデータ変換)
理由
- モードレス自動保存の実現: SPA はページ遷移なしにフォームデータを保持し、ビュー遷移時に非同期で保存できる。従来の MPA(Multi-Page Application)ではページ遷移のたびにフォーム状態が失われ、自動保存の実装が困難になる
- リアルタイム計算プレビュー: 所得見積額の変更時に Tax Calculation Core の Stage 3 のみを再実行し、控除判定結果をリアルタイムでプレビューする。SPA のクライアントサイド状態管理(TanStack Query の Optimistic Updates)がこれを効率的に実現する
- Born Digital のプリフィル UX: BFF が給与システム・マイナポータル・証明書 XML から取得したデータを、ポータル固有のビューモデルに変換して SPA に提供する。SPA は「プリフィル済みの確認画面」をスムーズに描画できる
- 4ポータルの独立性: ポータルごとに Code Splitting(遅延読み込み)を適用し、従業員ポータルにログインしたユーザーが管理者ポータルのコードをダウンロードしない。攻撃対象面(attack surface)の最小化にも寄与する
- BFF によるドメイン複雑性の吸収: 税法カテゴリ判定(CategoryJudgmentResult の 12 種のフラグ)やパイプラインの 11 ステップといったドメイン概念を、ポータルごとに適切な粒度に変換して SPA に提供する。従業員ポータルは「属性の確認」、管理者ポータルは「判定結果の詳細」と、同じデータを異なるビューで表示する
- 税年度の型安全ルーティング: TanStack Router の型安全パラメータにより、
{taxYear}の欠落をコンパイル時に検出できる。税年度を忘れたクエリの実行を構造的に防止する
却下した代替案
| 代替案 | 却下理由 |
|---|---|
| MPA(サーバーサイドレンダリング、従来型フォーム) | ページ遷移のたびにフォーム状態がリセットされ、モードレス自動保存の実現が困難。リアルタイム計算プレビューにも不向き |
| SSR/SSG(Next.js 等) | TASHIKA は認証必須のアプリケーションであり、SEO 要件がない。SSR のサーバーサイド実行コストと複雑性に対してメリットが少ない。BFF との責務重複が発生する |
| マイクロフロントエンド | 4ポータルの独立デプロイは現時点では不要。開発チームが単一であり、モノレポ内での Code Splitting で十分な分離が得られる。ランタイムの統合コストが高い |
| BFF なし(SPA から直接 API 呼び出し) | フロントエンドが CategoryJudgmentResult の 12 フラグや 11 ステップパイプラインといったドメイン概念を直接扱う必要があり、複雑性が増す。ポータルごとのデータ変換ロジックが SPA 側に散在する |
トレードオフ・リスク
- 初期ロードサイズ: SPA は初期 JavaScript バンドルのダウンロードが必要。パフォーマンスバジェット(初期ロード合計 ≤ 300KB gzip 後)を設定し、Code Splitting とルート単位の遅延読み込みで制御する(→ PE-001 パフォーマンス)
- SEO の制約: SPA はクローラーによるインデックスが困難。ただし TASHIKA は認証必須アプリケーションであり、SEO 要件はない
- BFF の追加レイヤー: BFF はシステムのレイヤーを1つ増やし、開発・運用コストが増加する。ただし BFF はバックエンド API と同一の ASP.NET Core 上で構築するため、技術スタックの追加は最小限に抑えられる
- オフライン対応の限界: SPA は基本的にオンライン前提。オフライン時は IndexedDB にローカル保存し、復帰後にサーバーと同期する設計とするが、長時間のオフラインは想定しない
- アクセシビリティの追加対応: SPA のクライアントサイドルーティングでは、ページ遷移時のフォーカス管理やスクリーンリーダーへの通知を明示的に実装する必要がある(→ AC-006 スクリーンリーダー対応)
結果
docs/design/frontend/に SPA + BFF の全体構成、マルチポータル設計、状態管理戦略を記載- 4ポータル(
/employee/,/admin/,/agency/,/system/)をルートレベルで分離し、Code Splitting を適用 - TanStack Router で
{taxYear}パラメータを型安全に管理 - TanStack Query でサーバー状態のキャッシュ・再検証を一元管理
- BFF は OpenAPI 3.1 スキーマから TypeScript 型を自動生成し、フロントエンド・バックエンド間の型安全性を確保