Skip to content

Commit e038573

Browse files
author
Lasim
committed
docs(storage): add naming conventions and best practices for storage keys
1 parent f1497be commit e038573

2 files changed

Lines changed: 86 additions & 98 deletions

File tree

docs/development/frontend/architecture.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ The application uses an **event bus** for cross-component communication without
272272

273273
For complete details on the event bus system, including usage patterns, naming conventions, and implementation examples, see the [Event Bus Documentation](/development/frontend/event-bus).
274274

275+
### Persistent State Management
276+
277+
The application includes a **storage system** built into the event bus for managing persistent state across route changes and browser sessions. This system provides type-safe localStorage access with automatic event emission for reactive updates.
278+
279+
For complete details on the storage system, including usage patterns, naming conventions, and best practices, see the [Frontend Storage System](/development/frontend/storage).
280+
275281
## Component Implementation Standards
276282

277283
### Vue Component Structure
@@ -455,7 +461,7 @@ For table implementations, use the shadcn-vue Table components as documented in
455461
### Frontend Security Principles
456462

457463
1. **Never Trust Client**: All validation must happen on backend
458-
2. **Secure Storage**: Never store sensitive data in localStorage
464+
2. **Secure Storage**: Never store sensitive data (passwords, API keys, tokens) in localStorage. See [Frontend Storage System](/development/frontend/storage) for proper storage patterns
459465
3. **XSS Prevention**: Sanitize user input, use Vue's built-in protections
460466
4. **CSRF Protection**: Include tokens in API requests
461467

docs/development/frontend/storage.mdx

Lines changed: 79 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,85 @@ console.log('All stored data:', allData)
114114
eventBus.clearAllState()
115115
```
116116

117+
## Storage Key Naming Convention
118+
119+
The storage system follows strict naming conventions to ensure consistency and prevent conflicts across the application.
120+
121+
### Naming Pattern
122+
123+
```
124+
{feature}_{description}
125+
```
126+
127+
All storage keys should:
128+
- Use **snake_case** (lowercase with underscores)
129+
- Start with the **feature name** or domain
130+
- End with a **descriptive identifier**
131+
- Be **specific and meaningful**
132+
133+
### Examples
134+
135+
```typescript
136+
// Good storage key names
137+
'selected_team_id' // Team selection
138+
'user_preferences' // User preferences object
139+
'dashboard_layout' // Dashboard configuration
140+
'recent_searches' // Search history
141+
'sidebar_collapsed' // UI state
142+
'notification_settings' // Notification preferences
143+
'selected_theme' // Theme selection
144+
'last_visited_page' // Navigation tracking
145+
146+
// Avoid these patterns
147+
'data' // Too generic
148+
'temp' // Not descriptive
149+
'userPref' // Use snake_case, not camelCase
150+
'TEAM_ID' // Use lowercase, not uppercase
151+
'team-id' // Use underscores, not hyphens
152+
```
153+
154+
### Storage Key Categories
155+
156+
1. **UI State**: `{component}_{state}`
157+
- `sidebar_collapsed`
158+
- `modal_shown`
159+
- `tab_selected`
160+
161+
2. **User Preferences**: `{feature}_preferences` or `user_{setting}`
162+
- `notification_preferences`
163+
- `user_language`
164+
- `user_timezone`
165+
166+
3. **Selection State**: `selected_{entity}_id`
167+
- `selected_team_id`
168+
- `selected_project_id`
169+
- `selected_workspace_id`
170+
171+
4. **Cache/History**: `{feature}_history` or `recent_{items}`
172+
- `search_history`
173+
- `recent_searches`
174+
- `visited_pages`
175+
176+
5. **Feature Data**: `{feature}_{data_type}`
177+
- `dashboard_layout`
178+
- `form_draft`
179+
- `wizard_state`
180+
117181
## Adding New Storage Values
118182

119-
### Step 1: Add to Configuration (Optional)
183+
### Step 1: Define Your Storage Key
120184

121-
For better organization, add your new storage key to the configuration:
185+
Follow the naming convention and optionally add your key to the configuration for better organization:
122186

123187
```typescript
124188
// In /composables/useEventBus.ts
125189
const STORAGE_CONFIG = {
126190
prefix: 'deploystack_',
127191
keys: {
128192
SELECTED_TEAM_ID: 'selected_team_id',
129-
SELECTED_THEME: 'selected_theme', // NEW
130-
USER_DASHBOARD_LAYOUT: 'dashboard_layout', // NEW
131-
RECENT_SEARCHES: 'recent_searches', // NEW
193+
SELECTED_THEME: 'selected_theme', // NEW - follows pattern
194+
USER_DASHBOARD_LAYOUT: 'dashboard_layout', // NEW - follows pattern
195+
RECENT_SEARCHES: 'recent_searches', // NEW - follows pattern
132196
}
133197
}
134198
```
@@ -252,67 +316,24 @@ const reorderPanels = (newOrder: string[]) => {
252316
</script>
253317
```
254318

255-
### Example 3: Search History
256-
257-
```typescript
258-
// SearchComponent.vue
259-
<script setup lang="ts">
260-
import { ref, onMounted } from 'vue'
261-
import { useEventBus } from '@/composables/useEventBus'
262-
263-
const eventBus = useEventBus()
264-
const searchQuery = ref('')
265-
const searchHistory = ref<string[]>([])
266-
267-
// Initialize search history
268-
onMounted(() => {
269-
const storedHistory = eventBus.getState<string[]>('recent_searches', [])
270-
searchHistory.value = storedHistory
271-
})
272-
273-
// Add search to history
274-
const addToHistory = (query: string) => {
275-
if (!query.trim()) return
276-
277-
// Remove duplicates and add to beginning
278-
const newHistory = [query, ...searchHistory.value.filter(item => item !== query)]
279-
280-
// Keep only last 10 searches
281-
searchHistory.value = newHistory.slice(0, 10)
282-
283-
// Persist to storage
284-
eventBus.setState('recent_searches', searchHistory.value)
285-
}
286-
287-
// Clear search history
288-
const clearHistory = () => {
289-
searchHistory.value = []
290-
eventBus.clearState('recent_searches')
291-
}
292-
293-
const performSearch = () => {
294-
if (searchQuery.value.trim()) {
295-
addToHistory(searchQuery.value.trim())
296-
// Perform actual search...
297-
}
298-
}
299-
</script>
300-
```
301-
302319
## Best Practices
303320

304-
### 1. Use Descriptive Keys
321+
### 1. Follow Naming Conventions
322+
323+
Always use the established naming pattern `{feature}_{description}` with snake_case:
305324

306325
```typescript
307-
// Good
326+
// Good - follows convention
308327
eventBus.setState('selected_team_id', teamId)
309328
eventBus.setState('user_dashboard_layout', layout)
310329
eventBus.setState('notification_preferences', prefs)
330+
eventBus.setState('sidebar_collapsed', true)
311331

312-
// Avoid
313-
eventBus.setState('data', someData)
314-
eventBus.setState('temp', tempValue)
315-
eventBus.setState('x', value)
332+
// ❌ Bad - violates convention
333+
eventBus.setState('data', someData) // Too generic
334+
eventBus.setState('selectedTeamId', teamId) // Wrong case (camelCase)
335+
eventBus.setState('TEAM_ID', teamId) // Wrong case (uppercase)
336+
eventBus.setState('team-id', teamId) // Wrong separator (hyphen)
316337
```
317338

318339
### 2. Provide Default Values
@@ -422,45 +443,6 @@ The storage system uses `localStorage`, which is supported in all modern browser
422443
- **Storage Limits**: localStorage typically has a 5-10MB limit per domain
423444
- **Event Frequency**: Storage change events are emitted for every setState/clearState call
424445

425-
## Migration Guide
426-
427-
### From Component State to Storage
428-
429-
**Before:**
430-
```typescript
431-
// Component-level state
432-
const selectedTeam = ref<Team | null>(null)
433-
434-
onMounted(() => {
435-
// Initialize from API or default
436-
selectedTeam.value = await getDefaultTeam()
437-
})
438-
```
439-
440-
**After:**
441-
```typescript
442-
// Storage-backed state
443-
const selectedTeam = ref<Team | null>(null)
444-
445-
onMounted(() => {
446-
// Initialize from storage with fallback
447-
const storedTeamId = eventBus.getState<string>('selected_team_id')
448-
if (storedTeamId) {
449-
selectedTeam.value = await getTeamById(storedTeamId)
450-
} else {
451-
const defaultTeam = await getDefaultTeam()
452-
selectedTeam.value = defaultTeam
453-
eventBus.setState('selected_team_id', defaultTeam.id)
454-
}
455-
})
456-
457-
// Update storage when state changes
458-
const selectTeam = (team: Team) => {
459-
selectedTeam.value = team
460-
eventBus.setState('selected_team_id', team.id)
461-
}
462-
```
463-
464446
## Related Documentation
465447

466448
- **[Global Event Bus](/development/frontend/event-bus)** - Core event system that powers storage

0 commit comments

Comments
 (0)