状态:已采纳的当前基线。本文描述普通业务 API 的目标形态和迁移收尾标准;源码仍处于迁移完成前的过渡状态时,必须明确标注残留位置。
SeaLantern 的普通业务使用 RESTful RPC:
- Web 宿主只有一个 Axum 应用和一个监听入口,由
server/src/adapter/http/router.rs统一组装 SPA、普通业务 API 和插件 API。 - 普通业务按资源和业务动作组织路径,例如
/api/instances/{id}、/api/instances/{id}/start、/api/downloads/{id};生命周期动作可以使用动作子路径,不要求为了形式上的 CRUD 强行改写。 - HTTP 方法、状态码和 JSON 响应表达业务结果。成功响应直接返回类型化 JSON;错误统一为
{ "code": "...", "message": "..." },不使用普通业务通用的method_id + JSON调度包络。 application/src/port定义类型化业务能力端口,AppServices由宿主 composition root 创建并显式注入 TauriState或 AxumAppState;Tauri command 和 Axum handler 都是薄适配器,共用同一组端口和实现,不通过隐式全局 locator 取服务。宿主退出前调用AppServices::shutdown(),统一停止容器启动的后台服务。- 插件 v2 的
POST /api/rpc/plugin/v2/invoke是有意保留的 RPC 例外。插件能力调用具有动态 capability、scope、trust 和授权令牌语义,必须经过独立 Bearer 认证和应用层策略检查,不能借此为普通业务恢复通用 RPC。
这里的“合二为一”指普通业务的 Web 入口和路由模型合并为同一个 Axum RESTful API;不把 Desktop 的 Tauri IPC 强行改成 HTTP,也不取消插件的专用 RPC 边界。
server/src/main.rs只创建一个TcpListener,并把build_router交给axum::serve。application/src/services.rs的AppServices::build()只由宿主启动装配调用;Desktop 通过 TauriState持有,Web 通过AppState传入 handler;两个宿主在退出路径调用AppServices::shutdown()。server/src/adapter/http/router.rs是生产路由入口,已挂载普通业务 REST 路由和插件 v2 RPC 路由。server/src/rpc/router.rs仍保留旧的通用 RPC 方法注册器,当前只被 RPC 模块自己的测试使用,没有被生产入口嵌套。这是迁移残留,不是第二个监听服务,也不是普通业务的目标架构。src/api/invoke.ts是前端的统一调用入口,负责 Tauri invoke 与 Axum REST 映射;src/api/rpc.ts没有生产调用方,待调用清理后删除。
HTTP/Tauri adapter
│
▼
application::port ───────► contract DTO / error
│
▼
application::service + AppServices
│
├──────────────► core
├──────────────► feature
└──────────────► infra
适配器负责解析请求、调用端口、选择传输响应;端口负责业务能力的类型化契约;应用实现负责用例编排和错误收敛;contract 不依赖任何具体实现层。
完成普通业务 RESTful RPC 迁移需要同时满足:
- 生产入口不再挂载普通业务的通用 method dispatcher;旧
server/src/rpc/router.rs在没有消费者后删除,或明确限于插件/测试用途。 - 前端所有需要 Web 的业务都通过
src/api/invoke.ts和已注册 REST 路由访问;未支持能力继续显式返回NotImplementedError。 src/api/rpc.ts没有调用方后删除,并同步删除无消费者的映射和类型。- 每个新业务端点都有 handler、路由方法/状态码、JSON 契约和至少一个路由级测试;错误不得泄漏底层路径、进程或网络细节。
- 路由、
application::port、contractDTO 和本目录设计文档在同一变更中更新。