From 62ccb6105d39aa8b50adbe4363d265ef9f3d455a Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Sat, 1 Nov 2025 18:36:28 +0800 Subject: [PATCH] feat: enhance CacheService and DataApiService documentation and structure - Added detailed file overview comments to CacheService and DataApiService, clarifying their roles as infrastructure components rather than business services. - Updated README.md to reflect the new structure and naming conventions, emphasizing the distinction between infrastructure and business logic components. - Introduced a new TestService for API testing scenarios, providing mock data and various test cases. - Created IBaseService interface to standardize service operations across the codebase. - Improved organization of API handlers and services for better clarity and maintainability. --- src/main/data/CacheService.ts | 19 + src/main/data/DataApiService.ts | 25 ++ src/main/data/README.md | 363 +++++++++++++++--- src/main/data/api/handlers/index.ts | 3 +- .../data/{api => }/services/TestService.ts | 0 .../base}/IBaseService.ts | 0 src/renderer/src/data/CacheService.ts | 19 + src/renderer/src/data/DataApiService.ts | 32 ++ 8 files changed, 405 insertions(+), 56 deletions(-) rename src/main/data/{api => }/services/TestService.ts (100%) rename src/main/data/{api/services => services/base}/IBaseService.ts (100%) diff --git a/src/main/data/CacheService.ts b/src/main/data/CacheService.ts index db05692d5a..d7571205d2 100644 --- a/src/main/data/CacheService.ts +++ b/src/main/data/CacheService.ts @@ -1,3 +1,22 @@ +/** + * @fileoverview CacheService - Infrastructure component for multi-tier caching + * + * NAMING NOTE: + * This component is named "CacheService" for management consistency, but it is + * actually an infrastructure component (cache manager) rather than a business service. + * + * True Nature: Cache Manager / Infrastructure Utility + * - Provides low-level caching primitives (memory/shared/persist tiers) + * - Manages TTL, expiration, and cross-window synchronization via IPC + * - Contains zero business logic - purely technical functionality + * - Acts as a utility for other services (PreferenceService, business services) + * + * The "Service" suffix is kept for consistency with existing codebase conventions, + * but developers should understand this is infrastructure, not business logic. + * + * @see {@link CacheService} For implementation details + */ + import { loggerService } from '@logger' import type { CacheEntry, CacheSyncMessage } from '@shared/data/cache/cacheTypes' import { IpcChannel } from '@shared/IpcChannel' diff --git a/src/main/data/DataApiService.ts b/src/main/data/DataApiService.ts index aa913e279e..7218364218 100644 --- a/src/main/data/DataApiService.ts +++ b/src/main/data/DataApiService.ts @@ -1,3 +1,28 @@ +/** + * @fileoverview DataApiService - API coordination and orchestration (Main Process) + * + * NAMING NOTE: + * This component is named "DataApiService" for management consistency, but it is + * actually a coordinator/orchestrator rather than a business service. + * + * True Nature: API Coordinator / Orchestrator + * - Initializes and coordinates the Data API framework + * - Wires together ApiServer (routing) and IpcAdapter (IPC communication) + * - Manages lifecycle (startup/shutdown) of API infrastructure + * - Contains zero business logic - purely infrastructure plumbing + * + * Architecture: + * DataApiService → coordinates → ApiServer + IpcAdapter + * ApiServer → routes requests → Handlers → Services → DB + * IpcAdapter → bridges → IPC ↔ ApiServer + * + * The "Service" suffix is kept for consistency with existing codebase conventions, + * but developers should understand this is a coordinator, not a business service. + * + * @see {@link ApiServer} For request routing logic + * @see {@link IpcAdapter} For IPC communication bridge + */ + import { loggerService } from '@logger' import { ApiServer, IpcAdapter } from './api' diff --git a/src/main/data/README.md b/src/main/data/README.md index 3a43436e0f..30167d6537 100644 --- a/src/main/data/README.md +++ b/src/main/data/README.md @@ -6,58 +6,201 @@ This directory contains the main process data management system, providing unifi ``` src/main/data/ -├── api/ # Data API framework -│ ├── core/ # Core API infrastructure -│ │ ├── ApiServer.ts # Request routing and handler execution +├── api/ # Data API framework (interface layer) +│ ├── core/ # Core API infrastructure +│ │ ├── ApiServer.ts # Request routing and handler execution │ │ ├── MiddlewareEngine.ts # Request/response middleware -│ │ └── adapters/ # Communication adapters -│ ├── handlers/ # API endpoint implementations -│ ├── services/ # Business logic services -│ └── index.ts # API framework exports -├── db/ # Database layer -│ ├── schemas/ # Drizzle table definitions -│ ├── seeding/ # Database initialization -│ └── DbService.ts # Database connection and management -├── migrate/ # Data migration system -│ └── dataRefactor/ # v2 data refactoring migration tools -├── CacheService.ts # Main process cache management -├── DataApiService.ts # Data API coordination service -└── PreferenceService.ts # User preferences management +│ │ └── adapters/ # Communication adapters (IPC) +│ ├── handlers/ # API endpoint implementations +│ │ └── index.ts # Thin handlers: param extraction, DTO conversion +│ └── index.ts # API framework exports +│ +├── services/ # Business logic layer +│ ├── base/ # Service base classes and interfaces +│ │ └── IBaseService.ts # Service interface definitions +│ └── TestService.ts # Test service (placeholder for real services) +│ # Future business services: +│ # - TopicService.ts # Topic business logic +│ # - MessageService.ts # Message business logic +│ # - FileService.ts # File business logic +│ +├── repositories/ # Data access layer (selective usage) +│ # Repository pattern used selectively for complex domains +│ # Future repositories: +│ # - TopicRepository.ts # Complex: Topic data access +│ # - MessageRepository.ts # Complex: Message data access +│ +├── db/ # Database layer +│ ├── schemas/ # Drizzle table definitions +│ │ ├── preference.ts # Preference configuration table +│ │ ├── appState.ts # Application state table +│ │ └── columnHelpers.ts # Reusable column definitions +│ ├── seeding/ # Database initialization +│ └── DbService.ts # Database connection and management +│ +├── migrate/ # Data migration system +│ └── dataRefactor/ # v2 data refactoring migration tools +│ +├── CacheService.ts # Infrastructure: Cache management +├── DataApiService.ts # Infrastructure: API coordination +└── PreferenceService.ts # System service: User preferences ``` -## Core Services +## Core Components -### CacheService -- **Purpose**: Main process caching with TTL support -- **Features**: Memory cache, IPC synchronization, cross-window broadcasting -- **Usage**: Internal caching for main process services +### Naming Note -### PreferenceService -- **Purpose**: Type-safe user configuration management -- **Features**: SQLite persistence, multi-window sync, batch operations -- **Usage**: Managing user settings and application configuration +Three components at the root of `data/` use the "Service" suffix but serve different purposes: -### DataApiService -- **Purpose**: Coordinates API server and IPC communication -- **Features**: Request routing, error handling, type safety -- **Usage**: Central hub for all data API operations +#### CacheService (Infrastructure Component) +- **True Nature**: Cache Manager / Infrastructure Utility +- **Purpose**: Multi-tier caching system (memory/shared/persist) +- **Features**: TTL support, IPC synchronization, cross-window broadcasting +- **Characteristics**: Zero business logic, purely technical functionality +- **Note**: Named "Service" for management consistency, but is actually infrastructure -## Data API Framework +#### DataApiService (Coordinator Component) +- **True Nature**: API Coordinator (Main) / API Client (Renderer) +- **Main Process Purpose**: Coordinates ApiServer and IpcAdapter initialization +- **Renderer Purpose**: HTTP-like client for IPC communication +- **Characteristics**: Zero business logic, purely coordination/communication plumbing +- **Note**: Named "Service" for management consistency, but is actually coordinator/client -### API Server (`api/core/ApiServer.ts`) +#### PreferenceService (System Service) +- **True Nature**: System-level Data Access Service +- **Purpose**: User configuration management with caching and multi-window sync +- **Features**: SQLite persistence, full memory cache, cross-window synchronization +- **Characteristics**: Minimal business logic (validation, defaults), primarily data access +- **Note**: Hybrid between data access and infrastructure, "Service" naming is acceptable + +**Key Takeaway**: Despite all being named "Service", these are infrastructure/coordination components, not business services. The "Service" suffix is kept for consistency with existing codebase conventions. + +## Architecture Layers + +### API Framework Layer (`api/`) + +The API framework provides the interface layer for data access: + +#### API Server (`api/core/ApiServer.ts`) - Request routing and handler execution - Middleware pipeline processing - Type-safe endpoint definitions -### Handlers (`api/handlers/`) -- Endpoint implementations for business logic -- Currently contains test handlers (production handlers pending) -- Must implement types defined in `@shared/data/api` +#### Handlers (`api/handlers/`) +- **Purpose**: Thin API endpoint implementations +- **Responsibilities**: + - HTTP-like parameter extraction from requests + - DTO/domain model conversion + - Delegating to business services + - Transforming responses for IPC +- **Anti-pattern**: Do NOT put business logic in handlers +- **Currently**: Contains test handlers (production handlers pending) +- **Type Safety**: Must implement all endpoints defined in `@shared/data/api` -### Services (`api/services/`) -- Business logic layer -- Database operations and data validation -- Called by API handlers +### Business Logic Layer (`services/`) + +Business services implement domain logic and workflows: + +#### When to Create a Service +- Contains business rules and validation +- Orchestrates multiple repositories or data sources +- Implements complex workflows +- Manages transactions across multiple operations + +#### Service Pattern + +Just an example for understanding. + +```typescript +// services/TopicService.ts +export class TopicService { + constructor( + private topicRepo: TopicRepository, // Use repository for complex data access + private cacheService: CacheService // Use infrastructure utilities + ) {} + + async createTopicWithMessage(data: CreateTopicDto) { + // Business validation + this.validateTopicData(data) + + // Transaction coordination + return await DbService.transaction(async (tx) => { + const topic = await this.topicRepo.create(data.topic, tx) + const message = await this.messageRepo.create(data.message, tx) + return { topic, message } + }) + } +} +``` + +#### Current Services +- `TestService`: Placeholder service for testing API framework +- More business services will be added as needed (TopicService, MessageService, etc.) + +### Data Access Layer (`repositories/`) + +Repositories handle database operations with a **selective usage pattern**: + +#### When to Use Repository Pattern +Use repositories for **complex domains** that meet multiple criteria: +- ✅ Complex queries (joins, subqueries, aggregations) +- ✅ GB-scale data requiring optimization and pagination +- ✅ Complex transactions involving multiple tables +- ✅ Reusable data access patterns across services +- ✅ High testing requirements (mock data access in tests) + +#### When to Use Direct Drizzle in Services +Skip repository layer for **simple domains**: +- ✅ Simple CRUD operations +- ✅ Small datasets (< 100MB) +- ✅ Domain-specific queries with no reuse potential +- ✅ Fast development is priority + +#### Repository Pattern + +Just an example for understanding. + +```typescript +// repositories/TopicRepository.ts +export class TopicRepository { + async findById(id: string, tx?: Transaction): Promise { + const db = tx || DbService.db + return await db.select() + .from(topicTable) + .where(eq(topicTable.id, id)) + .limit(1) + } + + async findByIdWithMessages( + topicId: string, + pagination: PaginationOptions + ): Promise { + // Complex join query with pagination + // Handles GB-scale data efficiently + } +} +``` + +#### Direct Drizzle Pattern (Simple Services) +```typescript +// services/SimpleService.ts +export class SimpleService extends BaseService { + async getItem(id: string) { + // Direct Drizzle query for simple operations + return await this.database + .select() + .from(itemTable) + .where(eq(itemTable.id, id)) + } +} +``` + +#### Planned Repositories +- **TopicRepository**: Complex topic data access with message relationships +- **MessageRepository**: GB-scale message queries with pagination +- **FileRepository**: File reference counting and cleanup logic + +**Decision Principle**: Use the simplest approach that solves the problem. Add repository abstraction only when complexity demands it. ## Database Layer @@ -89,32 +232,144 @@ import { dataApiService } from '@/data/DataApiService' ### Adding New API Endpoints 1. Define endpoint in `@shared/data/api/apiSchemas.ts` -2. Implement handler in `api/handlers/index.ts` -3. Create service in `api/services/` if needed -4. Add database schema in `db/schemas/` if required +2. Implement handler in `api/handlers/index.ts` (thin layer, delegate to service) +3. Create business service in `services/` for domain logic +4. Create repository in `repositories/` if domain is complex (optional) +5. Add database schema in `db/schemas/` if required ### Adding Database Tables 1. Create schema in `db/schemas/{tableName}.ts` 2. Generate migration: `yarn run migrations:generate` 3. Add seeding data in `db/seeding/` if needed -4. Create corresponding service in `api/services/` +4. Decide: Repository pattern or direct Drizzle? + - Complex domain → Create repository in `repositories/` + - Simple domain → Use direct Drizzle in service +5. Create business service in `services/` +6. Implement API handler in `api/handlers/` + +### Creating a New Business Service + +**For complex domains (with repository)**: +```typescript +// 1. Create repository: repositories/ExampleRepository.ts +export class ExampleRepository { + async findById(id: string, tx?: Transaction) { /* ... */ } + async create(data: CreateDto, tx?: Transaction) { /* ... */ } +} + +// 2. Create service: services/ExampleService.ts +export class ExampleService { + constructor(private exampleRepo: ExampleRepository) {} + + async createExample(data: CreateDto) { + // Business validation + this.validate(data) + + // Use repository + return await this.exampleRepo.create(data) + } +} + +// 3. Create handler: api/handlers/example.ts +import { ExampleService } from '../../services/ExampleService' + +export const exampleHandlers = { + 'POST /examples': async ({ body }) => { + return await ExampleService.getInstance().createExample(body) + } +} +``` + +**For simple domains (direct Drizzle)**: +```typescript +// 1. Create service: services/SimpleService.ts +export class SimpleService extends BaseService { + async getItem(id: string) { + // Direct database access + return await this.database + .select() + .from(itemTable) + .where(eq(itemTable.id, id)) + } +} + +// 2. Create handler: api/handlers/simple.ts +export const simpleHandlers = { + 'GET /items/:id': async ({ params }) => { + return await SimpleService.getInstance().getItem(params.id) + } +} +``` ## Data Flow +### Complete Request Flow + ``` -Renderer IPC Request - ↓ -DataApiService (coordination) - ↓ -ApiServer (routing) - ↓ -Handler (endpoint logic) - ↓ -Service (business logic) - ↓ -DbService (data persistence) +┌─────────────────────────────────────────────────────┐ +│ Renderer Process │ +│ React Component → useDataApi Hook │ +└────────────────┬────────────────────────────────────┘ + │ IPC Request +┌────────────────▼────────────────────────────────────┐ +│ Infrastructure Layer │ +│ DataApiService (coordinator) │ +│ ↓ │ +│ ApiServer (routing) → MiddlewareEngine │ +└────────────────┬────────────────────────────────────┘ + │ +┌────────────────▼────────────────────────────────────┐ +│ API Layer (api/handlers/) │ +│ Handler: Thin layer │ +│ - Extract parameters │ +│ - Call business service │ +│ - Transform response │ +└────────────────┬────────────────────────────────────┘ + │ +┌────────────────▼────────────────────────────────────┐ +│ Business Logic Layer (services/) │ +│ Service: Domain logic │ +│ - Business validation │ +│ - Transaction coordination │ +│ - Call repository or direct DB │ +└────────────────┬────────────────────────────────────┘ + │ + ┌──────────┴──────────┐ + │ │ +┌─────▼─────────┐ ┌──────▼──────────────────────────┐ +│ repositories/ │ │ Direct Drizzle │ +│ (Complex) │ │ (Simple domains) │ +│ - Repository │ │ - Service uses DbService.db │ +│ - Query logic │ │ - Inline queries │ +└─────┬─────────┘ └──────┬──────────────────────────┘ + │ │ + └──────────┬─────────┘ + │ +┌────────────────▼────────────────────────────────────┐ +│ Database Layer (db/) │ +│ DbService → SQLite (Drizzle ORM) │ +└─────────────────────────────────────────────────────┘ ``` +### Architecture Principles + +1. **Separation of Concerns** + - Handlers: Request/response transformation only + - Services: Business logic and orchestration + - Repositories: Data access (when complexity demands it) + +2. **Dependency Flow** (top to bottom only) + - Handlers depend on Services + - Services depend on Repositories (or DbService directly) + - Repositories depend on DbService + - **Never**: Services depend on Handlers + - **Never**: Repositories contain business logic + +3. **Selective Repository Usage** + - Use Repository: Complex domains (Topic, Message, File) + - Direct Drizzle: Simple domains (Agent, Session, Translate) + - Decision based on: query complexity, data volume, testing needs + ## Development Guidelines - All services use singleton pattern diff --git a/src/main/data/api/handlers/index.ts b/src/main/data/api/handlers/index.ts index 39122449ed..817a882be8 100644 --- a/src/main/data/api/handlers/index.ts +++ b/src/main/data/api/handlers/index.ts @@ -5,10 +5,9 @@ * TypeScript will error if any endpoint is missing. */ +import { TestService } from '@data/services/TestService' import type { ApiImplementation } from '@shared/data/api/apiSchemas' -import { TestService } from '../services/TestService' - // Service instances const testService = TestService.getInstance() diff --git a/src/main/data/api/services/TestService.ts b/src/main/data/services/TestService.ts similarity index 100% rename from src/main/data/api/services/TestService.ts rename to src/main/data/services/TestService.ts diff --git a/src/main/data/api/services/IBaseService.ts b/src/main/data/services/base/IBaseService.ts similarity index 100% rename from src/main/data/api/services/IBaseService.ts rename to src/main/data/services/base/IBaseService.ts diff --git a/src/renderer/src/data/CacheService.ts b/src/renderer/src/data/CacheService.ts index 191111f6c8..9a1602adcf 100644 --- a/src/renderer/src/data/CacheService.ts +++ b/src/renderer/src/data/CacheService.ts @@ -1,3 +1,22 @@ +/** + * @fileoverview CacheService - Infrastructure component for multi-tier caching (Renderer) + * + * NAMING NOTE: + * This component is named "CacheService" for management consistency, but it is + * actually an infrastructure component (cache manager) rather than a business service. + * + * True Nature: Cache Manager / Infrastructure Utility + * - Provides 3-tier caching system (memory/shared/persist) + * - Manages synchronization with Main process and other windows + * - Contains zero business logic - purely technical functionality + * - Used by React components via hooks (useCache, useSharedCache, usePersistCache) + * + * The "Service" suffix is kept for consistency with existing codebase conventions, + * but developers should understand this is infrastructure, not business logic. + * + * @see {@link CacheService} For implementation details + */ + import { loggerService } from '@logger' import type { RendererPersistCacheKey, diff --git a/src/renderer/src/data/DataApiService.ts b/src/renderer/src/data/DataApiService.ts index 5a49bc9bc9..2d3791139a 100644 --- a/src/renderer/src/data/DataApiService.ts +++ b/src/renderer/src/data/DataApiService.ts @@ -1,3 +1,35 @@ +/** + * @fileoverview DataApiService - API client for data requests (Renderer Process) + * + * NAMING NOTE: + * This component is named "DataApiService" for management consistency, but it is + * actually an API client rather than a business service. + * + * True Nature: API Client / Gateway + * - Provides HTTP-like interface for making data requests to Main process + * - Wraps IPC communication with type-safe, retry-enabled interface + * - Acts as a Gateway/Facade for all data operations from renderer + * - Contains zero business logic - purely communication infrastructure + * + * Key Features: + * - Type-safe requests with full TypeScript inference + * - Automatic retry with exponential backoff (network, timeout, 500/503 errors) + * - Request timeout management (3s default) + * - Batch request support for performance + * - Subscription management (real-time updates) + * + * Architecture: + * React Component → DataApiService (this file) → IPC → Main Process + * Main Process → Handlers → Services → DB → IPC Response + * DataApiService → Updates component state + * + * The "Service" suffix is kept for consistency with existing codebase conventions, + * but developers should understand this is an API client (similar to axios, fetch). + * + * @see {@link DataApiService} Main process coordinator + * @see {@link useDataApi} React hook for data requests + */ + import { loggerService } from '@logger' import type { ApiClient, ConcreteApiPaths } from '@shared/data/api/apiSchemas' import type {