Co-authored-by: aider (openai/andrew/openrouter/google/gemini-2.5-pro) <aider@aider.chat>
10 KiB
Refactoring Plan to Reduce Code Duplication
Implementation Order:
- 1. Create core module structure and error types
- 2. Create common data structures
- 3. Update database layer for batch operations
- 4. Create core services with clear boundaries (synchronous)
- 5. Add async wrappers for API use
- 6. Refactor CLI modes to use services (DONE)
- 7. Refactor REST API to use async services
- 8. Refactor MCP tools to use services
- 9. Create unified error handling
- 10. Add integration tests
- 11. Add performance optimization guidelines (partially done)
1. Create Core Module Structure and Error Types (DONE)
Files:
- Add:
src/core/error.rs - Add:
src/core/mod.rs
Functions:
- Add:
CoreErrorenum with comprehensive variants - Add: Conversion traits for all error types
Reason: Establish foundation for consistent error handling Implementation:
- Define base error enum with conversions to all interface-specific error types
- Use
#[derive(thiserror::Error)]for easyDisplayandErrorimplementations - Provide user-friendly error messages with error codes
2. Create Common Data Structures (DONE)
Files:
- Add:
src/core/types.rs
Functions:
- Add:
ItemWithMetastruct - Add:
ItemWithContentstruct - Add:
From<db::Item>forItemWithMeta - Add:
From<ItemWithMeta>forItemInfo(API response) - Add: Serialization/deserialization implementations
Reason: Standardize data structures used across modes and APIs Implementation:
- Define structs for common data types
- Include conversion functions from database types
- Add serialization/deserialization support for JSON/YAML
- Ensure all fields are properly documented
3. Update Database Layer for Batch Operations (DONE)
Files:
- Change:
src/db.rs
Functions:
- Add:
get_items_with_meta_batch - Add:
get_items_with_tags_batch - Add:
insert_item_with_meta_transaction - Add:
delete_item_with_meta_transaction - Change: Optimize existing queries for batch operations
Reason: Support efficient batch operations needed by services Implementation:
- Add functions to get multiple items with their metadata and tags
- Add batch insertion/updates for tags and metadata
- Add transaction support for atomic operations
- Optimize queries for common access patterns
4. Create Core Service Layer with Clear Boundaries (DONE)
Files:
- Add:
src/core/item_service.rs - Add:
src/core/compression_service.rs - Add:
src/core/meta_service.rs
Functions:
- Add:
get_item_fullinitem_service.rs - Add:
save_iteminitem_service.rs - Add:
list_itemsinitem_service.rs - Add:
delete_iteminitem_service.rs - Add:
get_compressed_contentincompression_service.rs - Add:
process_metadatainmeta_service.rs
Reason: Extract common business logic from modes and APIs into reusable services Implementation:
- Move logic from modes (get, save, list, info) and API handlers into service functions
- Services should return structured data, not format output
- Handle compression, metadata, and database operations
- Keep core services synchronous for CLI performance
- Use streaming for pipeline efficiency (process data in chunks)
Layer Division:
db.rs: Low-level SQL operations (e.g.,insert_item,query_all_items)item_service.rs: Higher-level business logic (e.g., "get item with metadata and tags," "validate item before save")
5. Add Async Wrappers for API Use
Files:
- Add:
src/core/async_item_service.rs
Functions:
- Add:
get_item_asyncinasync_item_service.rs - Add:
save_item_asyncinasync_item_service.rs - Add:
list_items_asyncinasync_item_service.rs - Add:
delete_item_asyncinasync_item_service.rs
Reason: Ensure services can be safely used in asynchronous contexts Implementation:
- Document thread-safety guarantees for all core services
- Use
Arc<Mutex<T>>or connection pooling for shared resources - Provide examples for safe async/sync boundaries
- Use
tokio::task::spawn_blockingfor CPU-bound or blocking I/O operations
6. Refactor CLI Modes to Use Services (DONE)
Files:
- Change:
src/modes/get.rs(DONE) - Change:
src/modes/save.rs(DONE) - Change:
src/modes/list.rs(DONE) - Change:
src/modes/info.rs(DONE) - Change:
src/modes/delete.rs(DONE) - Change:
src/modes/diff.rs(DONE) - Change:
src/modes/status.rs(DONE, uses shared function)
Functions:
- Change:
mode_getto useitem_service(DONE) - Change:
mode_saveto useitem_service(DONE) - Change:
mode_listto useitem_service(DONE) - Change:
mode_infoto useitem_service(DONE) - Change:
mode_deleteto useitem_service(DONE) - Change:
mode_diffto useitem_service(DONE) - Change:
mode_statusto use new status service functions (DONE, uses shared function)
Reason: Remove direct database and file system access from modes Implementation:
- Replace current implementations with calls to core services
- Keep only CLI-specific formatting and output logic
- Handle command-line argument parsing and validation
- Use synchronous services directly
- Implement streaming for stdin/stdout to maintain pipeline performance
7. Refactor REST API to Use Async Services (DONE)
Files:
- Change:
src/modes/server/api/item.rs(DONE) - Change:
src/modes/server/api/status.rs(DONE)
Functions:
- Change:
handle_get_itemto useasync_item_service::get_item_async(DONE) - Change:
handle_get_item_latestto useasync_item_service::get_item_async(DONE) - Change:
handle_list_itemsto useasync_item_service::list_items_async(DONE) - Change:
handle_post_itemto useasync_item_service::save_item_async(Not implemented) - Change:
handle_get_item_contentto useasync_item_service::get_item_content_async(DONE) - Change:
handle_get_item_metato useasync_item_service::get_item_meta_async(DONE) - Change:
handle_statusto useasync_item_service::get_status_async(DONE, uses shared function)
Reason: Remove business logic from HTTP handlers Implementation:
- Convert handlers to call async core services
- Keep only HTTP-specific code (status codes, headers, etc.)
- Use common error handling with conversions to HTTP responses
- Wrap synchronous service calls in
tokio::task::spawn_blocking
8. Refactor MCP Tools to Use Services
Files:
- Change:
src/modes/server/mcp/tools.rs
Functions:
- Change:
save_itemto useitem_service::save_item - Change:
get_itemto useitem_service::get_item_full - Change:
get_latest_itemto useitem_service::get_latest_item - Change:
list_itemsto useitem_service::list_items - Change:
search_itemsto useitem_service::search_items
Reason: Remove duplication with REST API and CLI modes Implementation:
- Replace current implementation with calls to core services
- Keep only MCP protocol-specific logic
- Use synchronous services directly (MCP is typically local/short-lived)
- Standardize response format to match API/CLI
9. Create Unified Error Handling (DONE)
Files:
- Change: All files that handle errors
Functions:
- Add: Conversion traits for all error types
- Change: All error handling to use new error system
Reason: Standardize error types across the application Implementation:
- Define comprehensive error enum with conversions:
- From database errors
- From I/O errors
- From compression errors
- From validation errors
- Implement conversions to:
anyhow::Error(for CLI)axum::http::StatusCode(for API)ToolError(for MCP)
- Provide user-friendly error messages
- Include error codes for programmatic handling
10. Add Integration Tests
Files:
- Add:
tests/integration/core_tests.rs - Add:
tests/integration/cli_tests.rs - Add:
tests/integration/api_tests.rs - Add:
tests/integration/performance_tests.rs
Functions:
- Add: Test cases for all core service functions
- Add: Test cases for CLI modes
- Add: Test cases for API endpoints
- Add: Performance benchmarks
Reason: Ensure refactored code maintains functionality and performance Implementation:
- Test core services independently
- Test CLI modes and APIs through their public interfaces
- Verify compression, metadata, and database operations
- Include performance benchmarks for critical paths
- Use in-memory databases and tempfiles for isolation
- Test both sync and async service implementations
11. Performance Optimization Guidelines (PARTIALLY DONE)
Files:
- Change: All core service files
- Change: All mode files
- Change: All API handler files
Functions:
- Add: Streaming implementations for I/O operations
- Add: Benchmark functions for critical paths
- Change: Buffer management to minimize copies
Reason: Ensure the refactored version doesn't slow down pipelines Implementation:
- Use streaming for stdin/stdout processing (chunked I/O)
- Minimize buffering and memory copies
- Offload CPU-bound work (compression, plugins) to thread pools
- Provide fast-path options (e.g.,
--fastflag to skip metadata plugins) - Benchmark critical operations before/after refactoring
- Document performance characteristics and tradeoffs
Benefits:
- Reduced code duplication between CLI, API, and MCP
- Easier maintenance with clear separation of concerns
- Consistent behavior across all interfaces
- Better testability with isolated service layer
- Maintained or improved pipeline performance
- Flexible architecture supporting both sync and async use cases
Key Risks and Mitigations:
-
Performance Regression:
- Risk: Refactoring could slow down pipeline operations
- Mitigation: Benchmark before/after, use streaming, minimize overhead
-
Increased Complexity:
- Risk: Adding service layer could make code harder to understand
- Mitigation: Clear documentation, gradual refactoring, maintain simple interfaces
-
Async/Sync Boundaries:
- Risk: Mixing sync/async could lead to deadlocks or inefficiencies
- Mitigation: Clear boundaries, use
spawn_blockingfor sync work in async context
-
Breaking Changes:
- Risk: Refactoring could change behavior in subtle ways
- Mitigation: Comprehensive tests, gradual rollout, maintain backward compatibility
Design Principles:
- Zero-Copy Where Possible: Use slicing instead of copying data
- Streaming Processing: Handle data in chunks for memory efficiency
- Clear Boundaries: Separate core logic from interface-specific code
- Performance First: Optimize for common pipeline use cases
- Consistent Errors: Unified error handling across all interfaces
- Backward Compatibility: Maintain existing CLI/API behavior