Skip to content

Store 設計規則

13.2.1. Store 分割基準

Store は ドメイン単位 で分割します。1つの Store は1つのドメインのみを担当しなければなりません。

  • 1 Store = 1 ドメイン原則を遵守します。
  • ドメインはビジネスエンティティまたは機能領域を基準に定義します。
  • 1つの Store が複数のドメインを管理することは禁止します。
Store担当ドメイン含まれる状態の例
useUserStoreユーザー認証/プロフィールログイン状態、ユーザー情報、トークン
useProductStore商品商品一覧、商品詳細、カテゴリ
useCartStoreカートカート項目、数量、合計
useNotificationStore通知通知一覧、既読状態
useThemeStoreテーマ/UI 設定ダークモード、言語、レイアウト

13.2.2. グローバル vs ローカル状態の判断

すべての状態を Store に配置しません。以下の判断基準に従って管理方式を決定しなければなりません。

判断基準管理方式
複数のコンポーネントが共有する状態Pinia Store認証情報、テーマ設定、権限
単一コンポーネントでのみ使用する状態コンポーネントローカル refフォーム入力値、モーダル表示有無、トグル
サーバーから取得する非同期データComposable(useFetch など)API レスポンスデータ、ページネーション
複数ページにまたがる一時的な状態Pinia Storeマルチステップフォームウィザードの進行データ
URL で表現可能な状態Router(query/params検索フィルター、ソート条件、ページ番号
  • 判断が曖昧な場合は ローカル状態から開始 し、共有の必要性が確認された時点で Store に昇格させます。

13.2.3. Store 間の依存関係

Store 間の依存が必要な場合は action 内部で 別の Store を呼び出します。

  • Store の最上位スコープで別の Store を呼び出しません。
  • 循環依存は禁止します。 A -> B -> A 形式の参照が発生した場合、共通 Store を分離します。
  • 依存方向は 上位ドメインから下位ドメインへ 単方向を維持します。
typescript
// src/stores/useCartStore.ts
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { useUserStore } from './useUserStore'
import type { CartItem } from '@/types/cart'

export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([])

  const totalPrice = computed(() =>
    items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
  )

  // action 内部で別の Store を呼び出します。
  async function checkout() {
    const userStore = useUserStore()

    if (!userStore.isLoggedIn) {
      throw new Error('ログインが必要です。')
    }

    await submitOrder(items.value, userStore.user!.id)
    items.value = []
  }

  return { items, totalPrice, checkout }
})
  • useUserStore()checkout() action 内部で呼び出すのが正しいパターンです。
  • Store 定義の最上位で const userStore = useUserStore() を宣言することは禁止します。

13.2.4. Store ネーミング

Store のネーミングは以下の規則に従います。

  • 関数名: use{Domain}Store パターンを使用します。(例: useAuthStoreuseProductStore
  • defineStore ID: ドメイン名を小文字ケバブ表記で使用します。
  • ファイル名: 関数名と同一の use{Domain}Store.ts で記述します。
関数名defineStore IDファイル名
useAuthStore'auth'useAuthStore.ts
useProductStore'product'useProductStore.ts
useOrderHistoryStore'order-history'useOrderHistoryStore.ts
typescript
// ファイル名: src/stores/useAuthStore.ts
// defineStore ID: 'auth'
// 関数名: useAuthStore

export const useAuthStore = defineStore('auth', () => {
  // ...
})
  • ID はアプリ全体で 一意でなければなりません。重複 ID はランタイムエラーを発生させます。
  • 複合ドメインの ID はケバブケースを使用します。(例: 'order-history''user-preference'

TIENIPIA QUALIFIED STANDARD