December 14, 2025
3 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

2025-12-14 HTTP 架构 vs NativeCore 抽象取舍记录

背景

  • 当前已确定短中期内,UI 与 Go 核心通过 HTTP 网络通信:
    • Go 核心作为本地/局域网服务进程运行。
    • Flutter UI 通过 HTTP 调用核心能力。
  • 代码中存在两套并行的“核心访问层”设计:
    • 一套是实际在用的 ApiClient
      • 定义:client/lib/core/api/api_client.dart:9
      • main.dart 中实例化,在 AppState 中注入使用:
        • client/lib/main.dart:33-40
        • client/lib/core/state/app_state.dart:86
    • 另一套是抽象的 ICoreService / HttpCoreService / NativeCoreService
      • 接口:client/lib/core/services/core_service.dart:4-53
      • HTTP 实现:client/lib/core/services/http_core_service.dart
      • Native 预置实现:client/lib/core/services/native_core_service.dart

本次决定的目标:在“不改现有代码”的前提下,把关于“是否还需要维护这套抽象”的思考沉淀成记录,方便未来回顾和重构时参考。

现状梳理

1. 实际使用路径

  • 真实调用链:
    • UI → AppStateApiClient → Go HTTP 服务。
  • ApiClient 已实现并在使用的功能包括:
    • 认证
      • initPassword / unlockVault
    • 文件操作
      • uploadFile / downloadFile / deleteFile / updateFileContent
    • 目录与元数据操作
      • createFolder / renameFile / moveFiles / copyFiles
      • getMetadata
    • 缩略图
      • listThumbnails / getThumbnail / uploadThumbnail / deleteThumbnail
    • 流式播放
      • getStreamUrl / getStreamHeaders

这一套 API 与实际业务和 UI 行为完全对齐,是当前“唯一事实来源”。

2. 抽象服务层的状态

  • ICoreService 仅定义了一部分核心操作:
    • 认证:initPassword / unlockVault
    • 元数据:getMetadata / syncMetadata
    • 文件:uploadFile / downloadFile / deleteFile / updateFileContent / listFiles
  • 当前问题:
    • 没有被 UI / AppState 使用,属于游离抽象。
    • 与真实能力不匹配:
      • 缺少目录与复制移动接口(createFolder / renameFile / moveFiles / copyFiles)。
      • 缺少缩略图相关接口。
      • 流式播放相关只定义了 getStreamUrl / getStreamHeaders,但也未真正被上层依赖。
  • NativeCoreService
    • 目前全部方法均抛出 UnimplementedError
    • 文件内注释详细描述了 gomobile / cgo 方案,这是一个“未来可能的路线草图”,而非已经在排期中的工作。

总结:抽象层是一个“设计草案 + 未完成功能”的混合体,目前没有任何实际调用。

不继续推进抽象层的理由

在已经基本确定“短中期不会改成原生接口”的前提下,目前如果强行维护这套抽象,会带来以下成本:

  1. 接口与事实脱节的维护成本

    • 要让 ICoreService 真正有用,需要:
      • 补全所有已经存在于 ApiClient 的能力。
      • 为每一个新增的后端接口同步更新接口定义与实现。
    • 一旦未来忘记同步某个方法,就会出现“接口说有/业务没用”或者“业务有/接口没更新”的不一致,增加心智负担。
  2. 层次复杂度与项目规模不匹配

    • 当前调用栈已经足够清晰:AppState 直接依赖 ApiClient
    • 再引入 ICoreService / HttpCoreService,会变成:
      • UI → AppStateICoreServiceHttpCoreServiceDio → Go。
    • 在项目规模尚不大的阶段,这样的间接层更多是负担而不是帮助。
  3. 造成 roadmap 误读的风险

    • NativeCoreService 注释写得很完整,容易让未来的自己误以为:
      • “Native 路线已经确定要做,只是暂时没空实现。”
    • 实际当前判断是:
      • UI 与 Go 核心继续通过 HTTP 通信即可满足需求;
      • 并没有明确排期要做 FFI/gomobile 方案。

基于以上原因,当前阶段不再在这套抽象上投入额外精力更符合现实需求。

为什么暂时“只记笔记,不动代码”

虽然从工程整洁角度看,完全可以考虑删除整套 core_service.dart / http_core_service.dart / native_core_service.dart,但本次选择暂时不删代码,只立一个决策记录,原因包括:

  1. 给未来的自己一个观察窗口

    • 可以在接下来的开发中观察:
      • 是否真的完全不会引用到这套抽象;
      • 是否会出现“需要抽象层”的新需求。
    • 在没有时间压力的前提下,多观察一阵可以减少“删多了又重写”的往返。
  2. 把取舍过程显式化

    • 这份笔记记录了:
      • 当前真实使用路径(ApiClient 为唯一事实来源)。
      • 抽象层与真实能力错位的具体点。
      • 决定暂不推进、也暂不删除的理由。
    • 未来无论是要彻底删除,还是要复活抽象层,都可以先回到这份记录上重新审视当时的判断。
  3. 与“清理废弃逻辑”的整体节奏对齐

    • 项目里已经在进行一轮代码清理(移除前端兜底、废弃函数、未使用逻辑)。
    • 对于抽象层这种“尚有潜在价值但当前不用”的部分,更谨慎的做法是先通过笔记冻结决策,再择机统一处理。

未来可能的两个方向

方向 A:彻底删除抽象层,收敛到 ApiClient

触发条件:

  • 项目继续沿着 HTTP 架构发展,没有任何 native 诉求。
  • 实际编码中完全没有新地方需要“多实现抽象”。

重构策略(届时可以参考):

  • 删除以下文件:
    • client/lib/core/services/core_service.dart
    • client/lib/core/services/http_core_service.dart
    • client/lib/core/services/native_core_service.dart
  • 如果文档里仍然需要保留 Native 设想,可以把相关注释迁移到 devdaily,而不是放在代码里。

预期收益:

  • 减少一层概念和文件,整体结构更直观:
    • UI → AppStateApiClient → Go。
  • 降低未来阅读和维护门槛。

方向 B:重新设计一套真正有用的领域抽象

触发条件:

  • 出现以下任意情况:
    • 除了 HTTP 实现之外,确实需要第二种实现(例如:离线模式、本地缓存层、Native FFI)。
    • 核心逻辑明显复杂到需要一个稳定的、与传输协议解耦的“领域接口”。

重构策略(届时可以参考):

  • 不直接复活现在的 ICoreService,而是:
    • 以当前 ApiClientAppState 中实际用到的能力为准,重新定义接口。
    • 将接口按“领域边界”设计,而不是简单映射 HTTP 路径。
    • 为 HTTP 实现写一个适配层,内部仍然复用已有 ApiClient
  • 根据实际需求决定是否还需要 NativeCoreService,避免为了“可能有一天”而提前设计。

预期收益:

  • 如果真的走到多实现阶段,可以给 UI 留一个更稳定的依赖面。
  • 抽象层的设计会基于“真实演进”而不是“提前臆测”。

当前共识(2025-12-14)

  • 架构共识:
    • 短中期内,UI 与 Go 核心继续通过 HTTP 通信。
    • 不主动推进 Native FFI / gomobile 方案。
  • 代码层策略:
    • ApiClient 作为唯一事实来源和 HTTP 边界层,继续维护和演进。
    • 现有 ICoreService / HttpCoreService / NativeCoreService 暂时不改动,也不再扩展。
    • 等后续有更明确的需求或清理窗口时,再在这份笔记的基础上决定:
      • 是彻底删除抽象层,还是重启、重设计。

这份记录的作用是:给“当下的决定”一个可追溯的解释,避免未来回头看到这些文件时,需要重新花时间推断当时的意图。