Data System Reference

July 25, 2026 · View on GitHub

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.

Quick Navigation

System Overview (Architecture)

Usage Guides (Code Examples)

Reference Guides (Coding Standards)

Testing

  • Test Mocks - Unified mocks for Cache, Preference, and DataApi

Choosing the Right System

Quick Decision Table

ServiceData CharacteristicsLifecycleData Loss ImpactExamples
BootConfigServiceProcess-level, pre-lifecyclePermanent until changedLow (can rebuild)Hardware acceleration, Chromium flags, data directory
CacheServiceRegenerable, temporary≤ App process or survives restartNone to minimalAPI responses, computed results, UI state
PreferenceServiceUser settings, key-valuePermanent until changedLow (can rebuild)Theme, language, font size, shortcuts
DataApiServiceBusiness data, structuredPermanentSevere (irreplaceable)Topics, messages, files, knowledge base
app_state (table)Internal continuity marker (main-process)Until owner drops the keyContinuity break (re-runs a one-time flow)Migration status, seeding journal

Decision Flowchart

Ask these questions in order:

  1. Must this setting be loaded before the lifecycle system takes over?

    • Yes → BootConfigService (process-level flags, Chromium switches, data directory)
    • No → Continue to #2
  2. Can this data be regenerated or lost without affecting the user?

    • Yes → CacheService
    • No → Continue to #3
  3. Is this a user-configurable setting that affects app behavior?

    • Yes → Does it have a fixed key and stable value structure?
      • Yes → PreferenceService
      • No (structure changes often) → DataApiService
    • No → Continue to #4
  4. Is this business data created/accumulated through user activity?

    • Yes → DataApiService
    • No → Continue to #5
  5. Is this an internal marker the app writes for itself to stay consistent across restarts (migration / seeding / one-time setup state)?

    • Yes → app_state table (main-process; see App State Overview)
    • No → Reconsider #2 (most data falls into one of these categories)

System Characteristics

BootConfigService - Early Boot Configuration

Use BootConfigService when:

  • Setting must be loaded synchronously before the lifecycle system takes over
  • Setting affects process-level behavior that cannot change at runtime (Chromium flags, data directory)
  • Setting cannot wait for database initialization

Key characteristics:

  • Synchronous file-based loading (boot-config.json)
  • Minimal key set — only process-level configuration
  • Accessed through PreferenceService (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')

CacheService - Runtime & Cache Data

Use CacheService when:

  • Data can be regenerated or lost without user impact
  • No backup or cross-device synchronization needed
  • Lifecycle is tied to component, window, or app session
  • You need other main-process services to react to cache changes (subscribeChange / subscribeSharedChange)

Two sub-categories:

  1. Performance cache: Computed results, API responses, expensive calculations
  2. UI state cache: Temporary settings, scroll positions, panel states

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 restart
  • usePersistCache (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')

PreferenceService - User Preferences

Use PreferenceService when:

  • Data is a user-modifiable setting that affects app behavior
  • Structure is key-value with predefined keys (users modify values, not keys)
  • Value structure is stable (won't change frequently)
  • Data loss has low impact (user can reconfigure)

Key characteristics:

  • Auto-syncs across all windows
  • Each preference item should be atomic (one setting = one key)
  • Values are typically: boolean, string, number, or simple array/object
// 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')

DataApiService - User Data

Use DataApiService when:

  • Data is business data accumulated through user activity
  • Data is structured with dedicated schemas/tables
  • Users can create, delete, modify records (no fixed limit)
  • Data loss would be severe and irreplaceable
  • Data volume can be large (potentially GBs)

Key characteristics:

  • No automatic window sync (fetch on demand for fresh data)
  • May contain sensitive data (encryption consideration)
  • Requires proper CRUD operations and transactions
// 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 Markers

Use the app_state table when:

  • Data is an internal marker the app writes for itself, not a user-facing setting
  • It must survive restarts, and losing it would make the user re-experience a one-time flow (re-run migration, re-seed, repeat setup)
  • It is needed at app startup — current consumers run at or before the lifecycle's earliest phase

Key characteristics:

  • Main-process only; no dedicated service — the owner reads/writes the table via its own DB handle
  • One owner per key; keys namespaced <scope>:<name>; no cross-domain reads

See App State Overview for full rules and the key registry.


Common Anti-patterns

Wrong ChoiceWhy It's WrongCorrect Choice
Storing AI provider configs in CacheUser loses configured providers on restartPreferenceService
Storing conversation history in PreferencesUnbounded growth, complex structureDataApiService
Storing topic list in PreferencesUser-created records, can grow largeDataApiService
Storing theme/language in DataApiOverkill for simple key-value settingsPreferenceService
Storing API responses in DataApiRegenerable data, doesn't need persistenceCacheService
Storing window positions in PreferencesCan be lost without impactCacheService (persist tier)
Storing hardware acceleration flag in PreferencesToo late — must load before lifecycle takes overBootConfigService
Storing user theme in BootConfigDoesn't need early boot loadingPreferenceService
Using DataApi for window/process controlNo database backing, pure side effects, retry is harmfulIPC handler
Using DataApi for external service callsSide effects, no CRUD semantics, timeout mismatchIPC handler
Using DataApi to wrap existing IPC callsAdds indirection without value, confuses layeringKeep as IPC
Side effects bundled into a DataApi writeData business-logic layer only — side effects must not ride along, however deeply nestedIPC handler (+ Entity Service for DB part)
Storing migration/seed state in CacheLost on restart → user re-runs a one-time flowapp_state table

Edge Cases

  • Recently used items (e.g., recent files, recent searches): Use usePersistCache - nice to have but not critical if lost
  • Draft content (e.g., unsaved message): Use useSharedCache for cross-window, consider auto-save to DataApi for recovery
  • Computed statistics: Use useCache with TTL - regenerate when expired
  • User-created templates/presets: Use DataApiService - user-generated content that can grow

Architecture Overview

                              ┌─────────────────┐
                              │ 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)        │
         └─────────────────────────────────────────┘

Type Definitions

  • src/shared/data/api/ - API type system
  • src/shared/data/bootConfig/ - Boot config type definitions and schemas
  • src/shared/data/cache/ - Cache type definitions and schemas (cacheSchemas.ts, cacheTypes.ts, cacheValueTypes.ts, templateKey.ts)
  • src/shared/data/preference/ - Preference type definitions

Main Process Implementation

  • src/main/data/bootConfig/ - Boot config service
  • src/main/data/api/ - API server and handlers
  • src/main/data/CacheService.ts - Cache service
  • src/main/data/PreferenceService.ts - Preference service (also routes BootConfig.* keys)
  • src/main/data/db/ - Database schemas

Renderer Process Implementation

  • src/renderer/data/DataApiService.ts - API client
  • src/renderer/data/CacheService.ts - Cache service
  • src/renderer/data/PreferenceService.ts - Preference service
  • src/renderer/data/hooks/ - React hooks