Files
fullex 88f141790c refactor(core): dissolve paths & application barrels, seal lifecycle (naming §6.4)
Three core barrels, three diagnoses under naming-conventions §6.4:

- paths: remove the barrel — it only re-exported types while the real entry
  is application.getPath(). constants.ts and pathRegistry.ts are independent
  building blocks deep-imported by their specific consumers (rules 2/3/4).
- lifecycle: keep the barrel (a cohesive framework consumed as a set by 80+
  services), route the last deep-import leaks through it, and prune 13
  framework-internal machinery exports (resolver, decorator getters, capability
  guards) that no consumer touches — tightening the door to its public surface.
- application: remove the barrel and repoint @application directly at
  Application.ts. The barrel bundled the hot `application` locator (~218
  consumers) with the bootstrap-only serviceRegistry, so every @application
  import dragged in the whole service graph — the root of the JobManager-to-
  serviceRegistry eval-time cycle. The locator now loads on its own.

Relocate SHUTDOWN_TIMEOUT_MS from an Application static into
lifecycle/constants.ts: a shared shutdown-grace policy honored by both
Application and JobManager, so JobManager no longer imports the Application
class at all.

Docs (paths/README, application/README, application-overview) and per-file
test mocks (vi.mock('@main/core/application') to '@application';
Application.getPath vi.unmock) are updated in lockstep.
2026-07-05 22:06:52 -07:00
..

Lifecycle & Application Reference

This is the main entry point for Cherry Studio's service lifecycle and application orchestration documentation. The lifecycle system provides IoC container management, phased bootstrap, and service lifecycle control.

Quick Navigation

System Overview (Architecture)

Usage Guide (Code Examples)

  • Lifecycle Usage - Decorators, error handling, conditional activation, pause/resume

Reference Guides (Standards)

Testing


Choosing the Right Pattern

Quick Decision Table

Lifecycle Direct-import singleton
Examples DbService, CacheService, MainWindowService ExportService, BackupManager, OcrService
Long-lived resources Yes No (or request-scoped)
Persistent side effects Yes No
onInit / onStop Meaningful Would be empty
Pattern @Injectable + application.get() export const x = new X()

Decision Flowchart

    ┌───────────────────────────────────┐
    │ Owns long-lived resources?        │
    │ (connections, timers, native      │
    │  modules, servers, processes)     │
    └─────┬────────────────┬────────────┘
      yes │                │ no
          ▼                ▼
   ┌───────────┐  ┌──────────────────────────┐
   │ Lifecycle │  │ Registers persistent     │
   └───────────┘  │ side effects?            │
                  │ (listeners, shortcuts,   │
                  │  subscriptions, etc.)    │
                  └─────┬───────────┬────────┘
                    yes │           │ no
                        ▼           ▼
                 ┌───────────┐ ┌────────────────┐
                 │ Lifecycle │ │ Direct-import  │
                 └───────────┘ │ singleton      │
                               └────────────────┘

For the full decision framework with examples, condition tables, and common mistakes, see Lifecycle Decision Guide.


Cross-Phase Dependencies Are Automatic

WhenReady services do not need to @DependsOn BeforeReady services (PreferenceService, DbService, CacheService, DataApiService). The lifecycle container guarantees BeforeReady completes before any WhenReady service starts. Declaring these dependencies is redundant, creates misleading noise in the dependency graph, and may confuse future readers about same-phase coupling. Only use @DependsOn for services within the same phase, or for WhenReady → WhenReady dependencies.

See Dependency Rules for the full matrix and Common Mistakes for a code-level example.


Common Anti-patterns

Wrong Choice Why It's Wrong Correct Choice
Using lifecycle for ExportService No onInit/onStop needed — all work is method-scoped Direct-import singleton
Using lifecycle for MessageRepository Just wraps DB queries; the DB connection belongs to DbService Direct-import singleton
Using direct-import for CacheService Owns a GC timer that needs cleanup on shutdown Lifecycle
Manual getInstance() singleton Lifecycle container manages singletons automatically @Injectable + application.get()
Calling application.get() at module scope Runs before bootstrap — service not yet registered Call inside onInit() or methods
Redundant @DependsOn on cross-phase deps (e.g. WhenReady → PreferenceService) BeforeReady phase is guaranteed to finish before WhenReady starts — declaration is noise and mis-signals coupling Omit @DependsOn for cross-phase deps; only declare same-phase deps

Core Infrastructure

  • src/main/core/lifecycle/ — IoC container, service lifecycle management
  • src/main/core/application/ — Application singleton, service registry, bootstrap orchestration

Service Implementations

  • src/main/services/ — Business services registered in the lifecycle system
  • src/main/data/ — Data layer services (Cache, Preference, DataApi)