docs/references/data/README.md
This is the main entry point for Cherry Studio's data management documentation. The application uses four data systems based on data characteristics and loading requirements.
withWriteTx) — the conventional wrapper for multi-statement / read-then-write atomicity (a direct db.transaction() is equivalent; single autocommit writes need neither), running as one synchronous BEGIN IMMEDIATE transaction on the one better-sqlite3 connectionfts_rowid, additive-vs-rebuild$defaultFn / service, PATCH derivation patterns| Service | Data Characteristics | Lifecycle | Data Loss Impact | Examples |
|---|---|---|---|---|
| BootConfigService | Process-level, pre-lifecycle | Permanent until changed | Low (can rebuild) | Hardware acceleration, Chromium flags, data directory |
| CacheService | Regenerable, temporary | ≤ App process or survives restart | None to minimal | API responses, computed results, UI state |
| PreferenceService | User settings, key-value | Permanent until changed | Low (can rebuild) | Theme, language, font size, shortcuts |
| DataApiService | Business data, structured | Permanent | Severe (irreplaceable) | Topics, messages, files, knowledge base |
app_state (table) | Internal continuity marker (main-process) | Until owner drops the key | Continuity break (re-runs a one-time flow) | Migration status, seeding journal |
Ask these questions in order:
Must this setting be loaded before the lifecycle system takes over?
Can this data be regenerated or lost without affecting the user?
Is this a user-configurable setting that affects app behavior?
Is this business data created/accumulated through user activity?
Is this an internal marker the app writes for itself to stay consistent across restarts (migration / seeding / one-time setup state)?
app_state table (main-process; see App State Overview)Use BootConfigService when:
Key characteristics:
boot-config.json)BootConfig.* prefix) after lifecycle starts// Early boot (src/main/main.ts) — direct access, only option at this stage
import { bootConfigService } from '@main/data/bootConfig'
if (bootConfigService.get('app.disable_hardware_acceleration')) {
app.disableHardwareAcceleration()
}
// Renderer / lifecycle services — via PreferenceService (standard access)
const [disableHwAccel, setDisableHwAccel] = usePreference('BootConfig.app.disable_hardware_acceleration')
Use CacheService when:
subscribeChange / subscribeSharedChange)Two sub-categories:
Three tiers based on persistence needs:
useCache (memory): Lost on app restart, per-renderer (no cross-window sync)useSharedCache (shared): Cross-window sharing via Main; lost on restartusePersistCache (persist): Survives app restart. Renderer persists to localStorage (renderer-authoritative); Main persists to its own JSON file (main-authoritative, via getPersist / setPersist / hasPersist). The two stores are independent; Main also relays renderer persist sync between windows.// Good: Temporary computed results
const [searchResults, setSearchResults] = useCache('search.results', [])
// Good: UI state that can be lost
const [sidebarCollapsed, setSidebarCollapsed] = useSharedCache('ui.sidebar.collapsed', false)
// Good: Recent items (nice to have, not critical)
// `usePersistCache` takes no initValue — Persist seeds every key from the schema on load
const [recentSearches, setRecentSearches] = usePersistCache('search.recent')
Use PreferenceService when:
Key characteristics:
// Good: App behavior settings
const [theme, setTheme] = usePreference('app.theme.mode')
const [language, setLanguage] = usePreference('app.language')
const [fontSize, setFontSize] = usePreference('chat.message.font_size')
// Good: Feature toggles
const [showTimestamp, setShowTimestamp] = usePreference('chat.display.show_timestamp')
Use DataApiService when:
Key characteristics:
// Good: User-generated business data
const { data: topics } = useQuery('/topics')
const { trigger: createTopic } = useMutation('/topics', 'POST')
// Good: Conversation history (irreplaceable)
const { data: messages } = useQuery('/messages', { query: { topicId } })
// Good: User files and knowledge base
const { data: files } = useQuery('/files')
app_state Table - Internal Continuity MarkersUse the app_state table when:
Key characteristics:
<scope>:<name>; no cross-domain readsSee App State Overview for full rules and the key registry.
| Wrong Choice | Why It's Wrong | Correct Choice |
|---|---|---|
| Storing AI provider configs in Cache | User loses configured providers on restart | PreferenceService |
| Storing conversation history in Preferences | Unbounded growth, complex structure | DataApiService |
| Storing topic list in Preferences | User-created records, can grow large | DataApiService |
| Storing theme/language in DataApi | Overkill for simple key-value settings | PreferenceService |
| Storing API responses in DataApi | Regenerable data, doesn't need persistence | CacheService |
| Storing window positions in Preferences | Can be lost without impact | CacheService (persist tier) |
| Storing hardware acceleration flag in Preferences | Too late — must load before lifecycle takes over | BootConfigService |
| Storing user theme in BootConfig | Doesn't need early boot loading | PreferenceService |
| Using DataApi for window/process control | No database backing, pure side effects, retry is harmful | IPC handler |
| Using DataApi for external service calls | Side effects, no CRUD semantics, timeout mismatch | IPC handler |
| Using DataApi to wrap existing IPC calls | Adds indirection without value, confuses layering | Keep as IPC |
| Side effects bundled into a DataApi write | Data business-logic layer only — side effects must not ride along, however deeply nested | IPC handler (+ Entity Service for DB part) |
| Storing migration/seed state in Cache | Lost on restart → user re-runs a one-time flow | app_state table |
usePersistCache - nice to have but not critical if lostuseSharedCache for cross-window, consider auto-save to DataApi for recoveryuseCache with TTL - regenerate when expired ┌─────────────────┐
│ React Components│
└─────────┬───────┘
│
┌─────────▼───────┐
│ React Hooks │ ← useDataApi, usePreference('...'),
└─────────┬───────┘ usePreference('BootConfig.*'), useCache
│
┌─────────▼───────┐
│ Services │ ← DataApiService, PreferenceService, CacheService
└─────────┬───────┘
│
┌─────────▼───────┐
│ IPC Layer │ ← Main Process Communication
└────┬────────┬───┘
│ │
┌────────────────────▼─┐ ┌───▼──────────────────────┐
│ PreferenceService │ │ Other Main Services │
│ (routes BootConfig.* │ │ (DataApi, Cache, etc.) │
│ to bootConfigService│ └──────────────────────────┘
│ for boot config keys│
└──────────┬───────────┘
│
┌───────────────▼─────────────┐
│ BootConfigService │
│ (sync load, ~/.cherrystudio/ │
│ boot-config.json — also used directly │
│ in early boot before lifecycle) │
└─────────────────────────────────────────┘
src/shared/data/api/ - API type systemsrc/shared/data/bootConfig/ - Boot config type definitions and schemassrc/shared/data/cache/ - Cache type definitions and schemas (cacheSchemas.ts, cacheTypes.ts, cacheValueTypes.ts, templateKey.ts)src/shared/data/preference/ - Preference type definitionssrc/main/data/bootConfig/ - Boot config servicesrc/main/data/api/ - API server and handlerssrc/main/data/CacheService.ts - Cache servicesrc/main/data/PreferenceService.ts - Preference service (also routes BootConfig.* keys)src/main/data/db/ - Database schemassrc/renderer/data/DataApiService.ts - API clientsrc/renderer/data/CacheService.ts - Cache servicesrc/renderer/data/PreferenceService.ts - Preference servicesrc/renderer/data/hooks/ - React hooks