Skip to content

Store Design Rules

13.2.1. Store Segmentation Criteria

Stores must be segmented by domain. Each Store must be responsible for a single domain only.

  • The 1 Store = 1 domain principle must be followed.
  • Domains must be defined based on business entities or functional areas.
  • A single Store managing multiple domains is prohibited.
StoreDomainExample State
useUserStoreUser authentication/profileLogin state, user info, token
useProductStoreProductProduct list, product detail, categories
useCartStoreShopping cartCart items, quantities, totals
useNotificationStoreNotificationsNotification list, read status
useThemeStoreTheme/UI settingsDark mode, language, layout

13.2.2. Global vs. Local State Determination

Not all state should be placed in a Store. The management method must be determined according to the following criteria.

CriteriaManagement MethodExample
State shared across multiple componentsPinia StoreAuth info, theme settings, permissions
State used only within a single componentComponent-local refForm inputs, modal visibility, toggles
Asynchronous data fetched from a serverComposable (useFetch, etc.)API response data, pagination
Temporary state spanning multiple pagesPinia StoreMulti-step form wizard progress data
State representable via URLRouter (query/params)Search filters, sort criteria, page number
  • When the determination is ambiguous, start with local state and promote to a Store when the need for sharing is confirmed.

13.2.3. Inter-Store Dependencies

When dependency between Stores is necessary, the other Store must be invoked inside an action.

  • Other Stores must not be invoked at the top-level scope of a Store.
  • Circular dependencies are prohibited. If an A -> B -> A reference occurs, a common Store must be extracted.
  • The dependency direction must maintain a unidirectional flow from higher-level domains to lower-level domains.
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)
  )

  // Invoke other Stores inside an action.
  async function checkout() {
    const userStore = useUserStore()

    if (!userStore.isLoggedIn) {
      throw new Error('Login is required.')
    }

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

  return { items, totalPrice, checkout }
})
  • Calling useUserStore() inside the checkout() action is the correct pattern.
  • Declaring const userStore = useUserStore() at the top level of a Store definition is prohibited.

13.2.4. Store Naming

Store naming must follow the rules below.

  • Function name: Use the use{Domain}Store pattern. (e.g., useAuthStore, useProductStore)
  • defineStore ID: Use the domain name in lowercase kebab-case.
  • File name: Use the same name as the function: use{Domain}Store.ts.
Function NamedefineStore IDFile Name
useAuthStore'auth'useAuthStore.ts
useProductStore'product'useProductStore.ts
useOrderHistoryStore'order-history'useOrderHistoryStore.ts
typescript
// File name: src/stores/useAuthStore.ts
// defineStore ID: 'auth'
// Function name: useAuthStore

export const useAuthStore = defineStore('auth', () => {
  // ...
})
  • The ID must be unique across the entire application. Duplicate IDs will cause runtime errors.
  • For compound domain IDs, kebab-case must be used. (e.g., 'order-history', 'user-preference')

TIENIPIA QUALIFIED STANDARD