Files
NetMount/ARCHITECTURE.md
VirtualHotBar b7d2b78e43 feat(repository): 实现数据访问抽象层并统一日志服务
- 完成 Repository 层实现,包括 BaseRepository、ConfigRepository、
  StorageRepository、MountRepository、TaskRepository
- 迁移所有 console.log/warn/error 到统一的 LoggerService
- 实现 MountRepository 的原子性更新逻辑,支持配置变更检测
- 实现 TaskRepository 的任务状态管理和生命周期控制
- 添加完整的单元测试覆盖 StorageManager、FileManager、
  TransferService 及各 Repository 模块
- 新增 CacheManager 的 localStorage 持久化功能
- 优化 ChunkTransferService 的并发控制和信号量实现
- 修复 MountRepository ID 生成逻辑中的特殊字符冲突问题
- 实现 TaskRepository.update 的真正就地更新而非删除重建
- 补充完整的类型定义文档和 JSDoc 注释
2026-04-07 00:31:16 +08:00

14 KiB

NetMount Architecture Documentation

Overview

NetMount is a unified cloud storage management and mounting application built with:

  • Frontend: React + TypeScript + Vite
  • Backend: Tauri (Rust)
  • Dependencies: Rclone, OpenList

Architecture Layers

┌─────────────────────────────────────────────────────────────┐
│                      Presentation Layer                      │
│                    (React Components in src/page)            │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      Controller Layer                        │
│          (Business logic in src/controller/*)               │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      Service Layer                           │
│    (Core business services in src/services/*)               │
│  - storage/: Storage management, file operations, transfers  │
│  - ConfigService.ts: Configuration management                │
│  - hook/: Event hooks                                        │
│  - rclone/: Rclone integration                               │
│  - openlist/: OpenList integration                           │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      Repository Layer                        │
│          (Data access abstraction - Future)                 │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                      Infrastructure Layer                    │
│  - utils/rclone/: Rclone API calls                           │
│  - utils/openlist/: OpenList API calls                       │
│  - utils/: Utility functions                                 │
│  - type/: TypeScript type definitions                        │
│  - constants/: Application constants                         │
└─────────────────────────────────────────────────────────────┘

Module Structure

Storage Module (src/services/storage/)

The storage module has been refactored from a monolithic file into focused sub-modules:

StorageManager.ts

Core storage management operations:

  • reupStorage() - Refresh storage list from rclone/OpenList
  • delStorage() - Delete a storage
  • getStorageParams() - Get storage configuration
  • searchStorage() - Find storage by name
  • filterHideStorage() - Filter out hidden storages
  • convertStoragePath() - Convert paths between formats
  • getStorageSpace() - Get storage capacity info
  • formatPathRclone() - Format paths for rclone
  • getFileName() - Extract filename from path

FileManager.ts

File operations:

  • getFileList() - List files in a directory
  • delFile() - Delete a file
  • delDir() - Delete a directory
  • mkDir() - Create a directory
  • uploadFileRequest() - Upload files with progress

TransferService.ts

Transfer operations between storages:

  • copyFile() - Copy files
  • copyDir() - Copy directories
  • moveFile() - Move files
  • moveDir() - Move directories
  • sync() - Sync directories (with bisync option)

StorageService.ts

Callback registration pattern to avoid circular dependencies:

  • registerDeleteStorage() - Register delete implementation
  • deleteStorage() - Call registered delete function

ConfigService (src/services/ConfigService.ts)

Encapsulates global state (nmConfig, osInfo) with:

  • Controlled access via getter/setter methods
  • Subscription pattern for config changes
  • Type-safe configuration updates
  • Callback registration pattern to avoid circular dependencies
  • Persistence (load/save to disk)

Backward Compatibility: Original exports (nmConfig, osInfo) still work via getters.

Repository Layer (src/repositories/)

The Repository layer abstracts data access operations, decoupling business logic from data sources:

Purpose

  • Encapsulate all Tauri invoke calls
  • Provide type-safe data access
  • Enable caching and retry mechanisms
  • Support testing with mock repositories
  • Emit data change events

BaseRepository (base/BaseRepository.ts)

Abstract base class providing:

  • invokeCommand<R>() - Unified Tauri invoke wrapper with retry and logging
  • clearCache() - Cache management
  • addChangeListener() - Data change subscription
  • validate() - Entity validation helper

ConfigRepository (config/ConfigRepository.ts)

Configuration data access:

  • getConfig() - Load full configuration
  • saveConfig() - Persist configuration
  • updatePartialConfig() - Merge partial updates
  • getConfigPath() / setConfigPath() - Path-based access
  • getOsInfo() - Operating system info

StorageRepository (storage/StorageRepository.ts)

Storage data access:

  • getAll() - List all storages
  • getById() - Get specific storage
  • create() / update() / delete() - CRUD operations
  • getStorageSpace() - Storage capacity info
  • getVisibleStorages() - Filter hidden storages
  • refreshStorageList() - Sync from Rclone/OpenList

Usage Example

import { configRepository, storageRepository } from '@/repositories'

// Get configuration
const config = await configRepository.getConfig()

// Update configuration path
await configRepository.setConfigPath('settings.themeMode', 'dark')

// List storages
const storages = await storageRepository.getAll()

// Create storage
await storageRepository.create({
  name: 'my-storage',
  type: 's3',
  framework: 'rclone'
})

// Subscribe to data changes
const unsubscribe = storageRepository.addChangeListener((event) => {
  console.log(`Storage ${event.id} ${event.type}`)
})

Design Benefits

  1. Testability: Mock repositories in tests without touching Tauri
  2. Decoupling: Business logic isolated from data sources
  3. Caching: Automatic caching with configurable timeout
  4. Logging: Unified logging via LoggerService
  5. Retry: Automatic retry on transient failures
  6. Events: React to data changes in real-time

Utils Module (src/utils/)

Refactored into focused sub-modules:

format/

  • formatSize() - Format file sizes
  • formatETA() - Format time estimates
  • formatPath() - Normalize paths

string/

  • randomString() - Generate random strings
  • takeMidStr() / takeRightStr() - String extraction
  • compareVersions() - Version comparison

file/

  • downloadFile() - File downloads
  • getWinFspInstallState() / installWinFsp() / openWinFspInstaller() - WinFsp management
  • openUrlInBrowser() - Open external URLs
  • showPathInExplorer() - Open file explorer
  • fs_exist_dir() / fs_make_dir() - Directory operations

system/

  • set_devtools_state() - Toggle devtools
  • restartSelf() - Restart application
  • sleep() - Async delay
  • getAvailablePorts() - Port availability

general.ts

  • isEmptyObject() - Object emptiness check
  • getURLSearchParam() - URL parsing
  • getProperties() - Object property extraction
  • mergeObjects() - Deep object merging

Constants (src/constants/index.ts)

Centralized application constants:

  • THEME_MODES / LANGUAGE_OPTIONS - UI options
  • API_TIMEOUT - Request timeout settings
  • RETRY_CONFIG - Retry logic configuration
  • WATCHDOG_CONFIG - Health check settings
  • STATS_CONFIG - Statistics refresh intervals
  • UPDATE_CONFIG - Update check intervals
  • DEFAULT_LANGUAGE / FALLBACK_LANGUAGE - i18n defaults

Design Patterns

1. Circular Dependency Resolution

Problem: utils/rclone/process.ts imported delStorage from controller/storage/storage.ts, creating a circular dependency.

Solution: Callback registration pattern via StorageService.ts:

// utils/rclone/process.ts
import { deleteStorage } from '@/services/storage/StorageService'
await deleteStorage(name) // calls registered implementation

// controller/storage/storage.ts
import { registerDeleteStorage } from '@/services/storage/StorageService'
registerDeleteStorage(delStorage)

2. Global State Encapsulation

Problem: nmConfig, rcloneInfo, openlistInfo were exported as mutable objects, allowing any module to modify them.

Solution: ConfigService class with subscription pattern:

// New pattern (recommended)
import { configService } from '@/services/ConfigService'
configService.updateConfig({ settings: { themeMode: 'dark' }})
const unsubscribe = configService.subscribeConfig((new, old) => { ... })

// Backward compatible (still works)
import { nmConfig } from '@/services/config'
nmConfig.settings.themeMode = 'dark' // via getter

3. Module Re-export Pattern

Pattern: New modules export from index, old files re-export for backward compatibility:

// src/services/storage/index.ts - New structure
export { reupStorage, delStorage, ... } from './StorageManager'
export { getFileList, delFile, ... } from './FileManager'
export { copyFile, moveFile, ... } from './TransferService'

// src/controller/storage/storage.ts - Backward compatibility
export * from '../../services/storage'

Import Guidelines

Use direct imports to avoid circular dependency warnings:

// ✅ Good - Direct import from specific module
import { filterHideStorage } from '@/services/storage/StorageManager'
import { getFileList } from '@/services/storage/FileManager'
import { copyFile } from '@/services/storage/TransferService'

// ⚠️ Avoid - Index imports can cause circular dependency warnings in some cases
import { filterHideStorage, getFileList } from '@/services/storage'

Deprecated Imports (Still Work)

// Old path - maintained for backward compatibility
import { filterHideStorage } from '@/controller/storage/storage'

Refactoring Progress

Completed (P0 - High Priority)

  1. Deleted backup file (storage.ts.bak)
  2. Fixed circular dependencies (utils/rclone/process.ts ↔ controller/storage)
  3. Merged duplicate constants files
  4. Extracted magic numbers to named constants
  5. Created ConfigService for state encapsulation
  6. Split storage.ts into 3 focused modules
  7. Split utils.ts into 6 focused modules
  8. Updated imports to use new modular paths
  9. Migrated all console.log/warn/error to unified LoggerService
  10. Repository layer fully implemented (BaseRepository, ConfigRepository, StorageRepository, MountRepository, TaskRepository)

In Progress (P1 - Medium Priority)

  1. Repository layer for data access abstraction (已完成)
  2. Migrate console logs to unified LoggerService (已完成)
  3. Update remaining code to use new patterns (已完成)

Completed (P1 - Medium Priority)

  1. Added comprehensive unit tests for StorageManager
  2. Added comprehensive unit tests for FileManager
  3. Added comprehensive unit tests for TransferService
  4. Added unit tests for StorageRepository
  5. Added unit tests for ConfigRepository
  6. Added unit tests for TaskRepository
  7. Added unit tests for MountRepository
  8. Added unit tests for ErrorService
  9. Added integration tests for storage operations

Completed (P2 - Low Priority)

  1. Performance optimization guidance documented
  2. Code quality checks implemented
  3. Module size validation (<300 lines)

Code Quality Guidelines

Do's

  • Use named constants instead of magic numbers
  • Import from specific modules for new code
  • Use ConfigService for configuration access
  • Keep modules focused and under 300 lines
  • Use TypeScript strict mode
  • Add JSDoc comments for public APIs

Don'ts

  • Import mutable state objects directly
  • Create new circular dependencies
  • Mix concerns in a single module
  • Use any type
  • Suppress TypeScript errors with @ts-ignore

Testing Strategy

Unit Tests (Future)

  • Test each repository method in isolation
  • Mock API calls
  • Test edge cases and error handling

Integration Tests (Future)

  • Test component interactions
  • Test storage operations end-to-end
  • Test configuration persistence

Migration Notes

For New Features

  1. Import from @/services/storage/* directly
  2. Use ConfigService for configuration
  3. Follow the module boundaries

For Existing Code

  • No immediate changes required
  • Gradual migration encouraged
  • Old imports continue to work

References