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.
| Store | Domain | Example State |
|---|---|---|
useUserStore | User authentication/profile | Login state, user info, token |
useProductStore | Product | Product list, product detail, categories |
useCartStore | Shopping cart | Cart items, quantities, totals |
useNotificationStore | Notifications | Notification list, read status |
useThemeStore | Theme/UI settings | Dark 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.
| Criteria | Management Method | Example |
|---|---|---|
| State shared across multiple components | Pinia Store | Auth info, theme settings, permissions |
| State used only within a single component | Component-local ref | Form inputs, modal visibility, toggles |
| Asynchronous data fetched from a server | Composable (useFetch, etc.) | API response data, pagination |
| Temporary state spanning multiple pages | Pinia Store | Multi-step form wizard progress data |
| State representable via URL | Router (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 thecheckout()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}Storepattern. (e.g.,useAuthStore,useProductStore) defineStoreID: Use the domain name in lowercase kebab-case.- File name: Use the same name as the function:
use{Domain}Store.ts.
| Function Name | defineStore ID | File 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')