# 架构设计评估与改进建议 ## 一、架构设计不足 ### 1.1 模块划分过细,增加复杂度 **问题**: 当前 16 个项目模块划分过于细碎 ``` 当前结构 (16个项目): FreeCode/ # 主入口 FreeCode.Core/ # 接口+模型 FreeCode.Engine/ # QueryEngine FreeCode.State/ # 状态管理 FreeCode.Features/ # 特性开关 FreeCode.ApiProviders/ # API提供商 FreeCode.Tools/ # 工具 FreeCode.Commands/ # 命令 FreeCode.Services/ # 业务服务 FreeCode.Mcp/ # MCP FreeCode.Lsp/ # LSP FreeCode.Bridge/ # 桥接 FreeCode.Skills/ # 技能 FreeCode.Plugins/ # 插件 FreeCode.Tasks/ # 任务 FreeCode.TerminalUI/ # 终端UI ``` **问题分析**: - 过度拆分导致项目间依赖复杂 - 每个项目都需要单独的 ServiceCollectionExtensions - 增加构建时间和维护成本 - 违反 YAGNI 原则 **建议合并方案**: ``` 优化后结构 (8个项目): FreeCode/ # 主入口 + CLI FreeCode.Core/ # 接口 + 模型 + 枚举 FreeCode.Engine/ # QueryEngine + PromptBuilder + Tools + Commands FreeCode.Integrations/ # MCP + LSP + Bridge + ApiProviders FreeCode.Services/ # 所有业务服务 (Auth/Memory/Voice/...) FreeCode.Extensions/ # Skills + Plugins + Tasks FreeCode.UI/ # TerminalUI + Components FreeCode.Tests/ # 所有测试 ``` ### 1.2 缺少领域驱动设计 (DDD) 分层 **问题**: 当前架构按技术划分,而非按业务领域 ``` 当前: 按技术划分 ├── Tools/ (技术: 工具执行) ├── Commands/ (技术: 命令处理) ├── Services/ (技术: 服务层) └── Mcp/ (技术: 协议) ``` ``` 原始 TypeScript: 按功能划分 ├── tools/ (功能: Agent 能力) ├── commands/ (功能: 用户交互) ├── services/ (功能: 支撑服务) │ ├── api/ (API 调用) │ ├── mcp/ (MCP 协议) │ ├── oauth/ (认证) │ └── SessionMemory/ (记忆) ``` **建议**: 采用垂直切片架构 ```csharp // 建议: src/FreeCode.Features/Conversation/ public class ConversationFeature { // 对话相关的一切: QueryEngine + Messages + Tools + Commands } // src/FreeCode.Features/Integration/ public class IntegrationFeature { // 集成相关: MCP + LSP + Bridge } // src/FreeCode.Features/Configuration/ public class ConfigurationFeature { // 配置相关: Settings + Plugins + Skills } ``` ### 1.3 状态管理设计问题 **问题**: `AppState` 是一个巨大的 record,违反单一职责 ```csharp // src/FreeCode.State/AppState.cs - 当前设计 public sealed record AppState { public SettingsJson Settings { get; init; } // 配置 public PermissionMode PermissionMode { get; init; } // 权限 public IReadOnlyDictionary Tasks { get; init; } // 任务 public McpState Mcp { get; init; } // MCP public PluginState Plugins { get; init; } // 插件 public RemoteConnectionStatus RemoteConnectionStatus { get; init; } // 远程 public Companion? Companion { get; init; } // 同伴 public NotificationState Notifications { get; init; } // 通知 // ... 30+ 属性 } ``` **问题分析**: 1. 任何状态变更都会触发整个 AppState 的重新创建 2. 订阅者无法只订阅感兴趣的状态切片 3. 测试困难,需要构造完整的 AppState **建议**: 采用分片状态设计 ```csharp // 建议: 分片状态管理 public interface IStateSlice { T Value { get; } IObservable Observe(); void Update(Func updater); } // 分片 public class ConversationSlice : IStateSlice { } public class McpSlice : IStateSlice { } public class TaskSlice : IStateSlice { } public class UiSlice : IStateSlice { } // 组合 public class AppStateManager { public ConversationSlice Conversation { get; } public McpSlice Mcp { get; } public TaskSlice Tasks { get; } public UiSlice Ui { get; } } ``` ### 1.4 依赖注入设计问题 **问题**: 服务注册过于分散,缺少模块化 ```csharp // src/FreeCode/Program.cs - 当前设计 services.AddCoreServices(); services.AddFeatures(); services.AddState(); services.AddEngine(); services.AddFreeCodeApiProviders(); services.AddFreeCodeTools(); services.AddCommands(); services.AddServices(); services.AddMcp(); services.AddLsp(); services.AddTasks(); services.AddBridge(); services.AddSkills(); services.AddPlugins(); services.AddTerminalUI(); // 16 个扩展方法! ``` **问题分析**: 1. 注册顺序敏感,隐式依赖 2. 难以单独测试某个模块 3. 无法按需加载模块 **建议**: 采用模块化 DI 设计 ```csharp // 建议: 模块化设计 public interface IFreeCodeModule { string Name { get; } void ConfigureServices(IServiceCollection services); void Configure(IApplicationBuilder app); } public class CoreModule : IFreeCodeModule { public string Name => "Core"; public void ConfigureServices(IServiceCollection services) { // Core 相关的所有服务 } } public class McpModule : IFreeCodeModule { public string Name => "Mcp"; public void ConfigureServices(IServiceCollection services) { // MCP 相关的所有服务 } } // Program.cs var modules = new IFreeCodeModule[] { new CoreModule(), new EngineModule(), new McpModule(), // 按需添加 }; foreach (var module in modules) module.ConfigureServices(services); ``` --- ## 二、设计模式问题 ### 2.1 工具系统缺少责任链模式 **问题**: 当前工具执行是直接 switch-case ```csharp // src/FreeCode.Tools/ToolRegistry.cs:162-214 return tool.Name switch { "Agent" => await ExecuteAsync(...), "Bash" => await ExecuteAsync(...), "Read" => await ExecuteAsync(...), // ... 48 个 case _ => ($"Unsupported tool execution for {tool.Name}", true, false) }; ``` **建议**: 使用责任链 + 策略模式 ```csharp // 建议: 工具执行管道 public interface IToolExecutor { bool CanExecute(ITool tool); Task<(string Output, bool IsAllowed, bool ShouldContinue)> ExecuteAsync( ITool tool, JsonElement input, ToolExecutionContext context, CancellationToken ct); } public class ToolExecutorPipeline { private readonly IEnumerable _executors; public async Task<(string, bool, bool)> ExecuteAsync(...) { foreach (var executor in _executors) { if (executor.CanExecute(tool)) return await executor.ExecuteAsync(tool, input, context, ct); } throw new NotSupportedException($"No executor for tool: {tool.Name}"); } } ``` ### 2.2 缺少事件溯源 **问题**: 消息历史直接存储在内存列表 ```csharp // src/FreeCode.Engine/QueryEngine.cs private readonly List _messages = new(); private void AppendMessage(Message message) { lock (_gate) { _messages.Add(message); // 直接添加,无事件发布 } } ``` **建议**: 采用事件溯源模式 ```csharp // 建议: 事件溯源 public abstract record MessageEvent { public sealed record MessageAdded(Message Message) : MessageEvent; public sealed record MessageRemoved(string MessageId) : MessageEvent; public sealed record MessagesCompacted(string Reason) : MessageEvent; } public class MessageStore { private readonly List _messages = new(); private readonly Subject _events = new(); public IObservable Events => _events.AsObservable(); public void Add(Message message) { _messages.Add(message); _events.OnNext(new MessageEvent.MessageAdded(message)); } } ``` ### 2.3 缺少策略模式的动态切换 **问题**: API 提供商切换是硬编码的 ```csharp // src/FreeCode.ApiProviders/ApiProviderRouter.cs public IApiProvider GetActiveProvider() { var providerType = GetProviderType(); // 从环境变量读取 return providerType switch { ApiProviderType.Anthropic => _anthropicProvider, ApiProviderType.OpenAICodex => _codexProvider, // ... 硬编码 }; } ``` **建议**: 使用策略模式 + 工厂 ```csharp // 建议: 动态策略注册 public interface IApiProviderFactory { IApiProvider Create(ApiProviderConfig config); } public class ApiProviderRegistry { private readonly Dictionary _factories = new(); public void Register(string providerType, IApiProviderFactory factory) => _factories[providerType] = factory; public IApiProvider Create(ApiProviderConfig config) => _factories.TryGetValue(config.Type, out var factory) ? factory.Create(config) : throw new NotSupportedException(config.Type); } ``` --- ## 三、性能设计问题 ### 3.1 JSON 处理效率低 **问题**: 大量使用 `JsonDocument.Parse` 和 `Clone()` ```csharp // src/FreeCode.Mcp/McpClient.cs:170 private static JsonRpcMessage? ParseMessage(string line) { using var document = JsonDocument.Parse(line); // 分配 var root = document.RootElement; // ... return new JsonRpcResponse(responseId.ToString(), result.Clone(), error); // 再次分配 } ``` **建议**: 使用 `Utf8JsonReader` 和 `Span` ```csharp // 建议: 零分配 JSON 解析 public static JsonRpcMessage? ParseMessage(ReadOnlySpan utf8Json) { var reader = new Utf8JsonReader(utf8Json); while (reader.Read()) { if (reader.TokenType == JsonTokenType.PropertyName) { var propName = reader.GetString(); // 直接解析,无中间分配 } } } ``` ### 3.2 消息渲染效率问题 **问题**: 每次消息更新都重绘整个列表 ```csharp // src/FreeCode.TerminalUI/REPLScreen.cs:227 private void RefreshMessages() { _messageList.SetSource(_messages); // 重置整个数据源 if (_messages.Count > 0) _messageList.SelectedItem = _messages.Count - 1; _messageList.SetNeedsDisplay(); // 触发完全重绘 } ``` **建议**: 增量更新 ```csharp // 建议: 增量渲染 private void OnNewMessage(Message message) { var index = _messages.Count - 1; _messageList.AddSourceItem(message); // 只添加新项 _messageList.ScrollTo(index); _messageList.SetNeedsDisplay(index); // 只重绘新行 } ``` ### 3.3 缺少连接池 **问题**: MCP HTTP 连接每次都创建新的 HttpClient ```csharp // src/FreeCode.Mcp/McpClientManager.cs:300 private static HttpClient CreateHttpClient(IReadOnlyDictionary? headers = null) { var client = new HttpClient(); // 每次创建新实例 ApplyHeaders(client, headers); return client; } ``` **建议**: 使用 `IHttpClientFactory` ```csharp // 建议: 连接池 services.AddHttpClient("Mcp") .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(15), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5) }); public class McpClientManager { private readonly IHttpClientFactory _httpClientFactory; private HttpClient CreateHttpClient(...) => _httpClientFactory.CreateClient("Mcp"); } ``` --- ## 四、可扩展性问题 ### 4.1 插件系统设计局限 **问题**: 当前插件系统只支持命令扩展 ```csharp // src/FreeCode.Plugins/PluginManager.cs:74-110 public IReadOnlyList GetPluginCommands() { // 只加载 ICommand 类型 foreach (var type in assembly.GetTypes()) { if (!typeof(ICommand).IsAssignableFrom(type)) continue; // ... } } ``` **建议**: 支持多种扩展点 ```csharp // 建议: 多扩展点设计 public interface IPluginExtension { string ExtensionPoint { get; } // "tools", "commands", "services", "hooks" } public interface IToolExtension : IPluginExtension { IEnumerable GetTools(); } public interface IHookExtension : IPluginExtension { Task OnBeforeToolExecute(string toolName, object input); Task OnAfterToolExecute(string toolName, object output); } // PluginManager public class PluginManager { public IEnumerable GetTools() => GetExtensions().SelectMany(e => e.GetTools()); public IEnumerable GetCommands() => GetExtensions().SelectMany(e => e.GetCommands()); } ``` ### 4.2 缺少中间件管道 **问题**: 工具执行没有预处理/后处理钩子 **原始 TypeScript 设计**: ```typescript // 原始: hooks/ 目录下有完整的中间件系统 export async function executeWithHooks(toolName, input, context) { await runPreHooks(toolName, input); const result = await execute(toolName, input, context); await runPostHooks(toolName, result); return result; } ``` **建议**: 添加中间件管道 ```csharp // 建议: 中间件设计 public interface IToolMiddleware { Task ExecuteAsync(ToolContext context, ToolDelegate next); } public delegate Task ToolDelegate(ToolContext context); public class ToolPipeline { private readonly IList _middlewares = new List(); public void Use(IToolMiddleware middleware) => _middlewares.Add(middleware); public async Task ExecuteAsync(ToolContext context) { ToolDelegate pipeline = ctx => ctx.Tool.ExecuteAsync(ctx); foreach (var middleware in _middlewares.Reverse()) { var next = pipeline; pipeline = ctx => middleware.ExecuteAsync(ctx, next); } return await pipeline(context); } } ``` --- ## 五、测试性问题 ### 5.1 缺少接口隔离 **问题**: `ITool` 接口职责过多 ```csharp // src/FreeCode.Core/Interfaces/ITool.cs public interface ITool { string Name { get; } string[]? Aliases { get; } string? SearchHint { get; } ToolCategory Category { get; } bool IsEnabled(); JsonElement GetInputSchema(); Task GetDescriptionAsync(object? input = null); bool IsConcurrencySafe(object input); bool IsReadOnly(object input); // 9 个成员! } ``` **建议**: 接口隔离 ```csharp // 建议: 接口隔离 public interface IToolInfo { string Name { get; } string[]? Aliases { get; } ToolCategory Category { get; } } public interface IToolSchema { JsonElement GetInputSchema(); Task GetDescriptionAsync(object? input = null); } public interface IToolBehavior { bool IsEnabled(); bool IsConcurrencySafe(object input); bool IsReadOnly(object input); } public interface IToolExecutor { Task> ExecuteAsync(TInput input, ToolExecutionContext context, CancellationToken ct); } // 组合接口 public interface ITool : IToolInfo, IToolSchema, IToolBehavior { } ``` ### 5.2 缺少测试替身支持 **问题**: 难以为外部依赖创建测试替身 ```csharp // 当前: 直接依赖具体类型 public class BashTool : ToolBase { private readonly IBackgroundTaskManager _taskManager; // 接口,好 public override async Task> ExecuteAsync(...) { using var process = new Process { StartInfo = psi }; // 具体类型,难测试 process.Start(); // ... } } ``` **建议**: 抽象进程执行 ```csharp // 建议: 进程执行抽象 public interface IProcessExecutor { Task ExecuteAsync(ProcessStartInfo info, CancellationToken ct); } public class BashTool : ToolBase { private readonly IProcessExecutor _processExecutor; public override async Task> ExecuteAsync(...) { var result = await _processExecutor.ExecuteAsync(psi, ct); return new ToolResult(new BashToolOutput { Stdout = result.Stdout, Stderr = result.Stderr, ExitCode = result.ExitCode }); } } // 测试 public class FakeProcessExecutor : IProcessExecutor { public Task ExecuteAsync(...) => Task.FromResult(new ProcessResult { ExitCode = 0, Stdout = "fake output" }); } ``` --- ## 六、总结与优先级 ### 6.1 高优先级改进 (影响架构稳定性) | # | 改进项 | 收益 | 工作量 | |---|--------|------|--------| | 1 | 状态分片管理 | 可测试性、性能 | 2天 | | 2 | 工具执行管道 | 可扩展性、可测试性 | 2天 | | 3 | 模块化 DI | 可维护性、可测试性 | 1天 | | 4 | JSON 零分配 | 性能 | 2天 | ### 6.2 中优先级改进 (提升可维护性) | # | 改进项 | 收益 | 工作量 | |---|--------|------|--------| | 5 | 项目合并 (16→8) | 构建速度、维护成本 | 1天 | | 6 | 中间件管道 | 可扩展性 | 2天 | | 7 | 接口隔离 | 可测试性 | 1天 | | 8 | 进程执行抽象 | 可测试性 | 1天 | ### 6.3 低优先级改进 (锦上添花) | # | 改进项 | 收益 | 工作量 | |---|--------|------|--------| | 9 | 事件溯源 | 可追溯性 | 2天 | | 10 | 增量渲染 | 性能 | 1天 | | 11 | HttpClient 池 | 性能 | 0.5天 | | 12 | 多扩展点插件 | 可扩展性 | 2天 | --- ## 七、最终建议 ### 当前架构评分: 6.5/10 ### 主要优点 - 模块化思路正确 - 接口驱动设计 - 依赖注入完整 - 测试基础扎实 ### 主要不足 - 模块划分过细 - 状态管理臃肿 - 缺少中间件机制 - 性能优化不足 ### 建议路径 1. **短期** (1周): 状态分片 + 工具管道 + 模块化 DI 2. **中期** (2周): 项目合并 + 接口隔离 + 中间件 3. **长期** (持续): 性能优化 + 事件溯源 ### 重构风险 **中等** - 建议在完成核心功能后再进行架构重构,避免过早优化。