# `dbtool-usable-v1` Shared App / CLI Contract Snapshot 日期:2026-03-27 作者:Senior Backend Engineer 对应 issue:`CMP-46` ## 1. 目标 本文从 `dbtool-usable-v1` 视角冻结当前 `crates/db-app` 与 `apps/cli` 的共享契约、错误边界与当前 gap。 本文不是新的架构提案,只记录当前代码与当前阶段可承诺的 backend 行为边界,供 CTO、Frontend、QA 和 PM 直接引用。 ## 2. 当前后端分层 - `crates/db-core` - 定义数据库无关的领域对象与基础校验: - `ConnectionTarget` - `InspectRequest` - `QueryRequest` - `ExportRequest` - `InspectResult` - `QueryResult` - `crates/db-config` - 定义 `ConnectionProfile` - 负责 password env 注入与脱敏展示 - `crates/db-drivers` - 收敛 PostgreSQL / MySQL / SQLite 的 connect / inspect / query 差异 - `crates/db-app` - 是当前唯一共享应用编排入口 - 对 connect / inspect / query / export 暴露统一结构化结果与统一错误对象 - `apps/cli` - 只负责参数解析、stdout/stderr 渲染、退出码 - 当前不应重新定义 shared app 语义 ## 3. 领域模型与持久化边界 ### 3.1 当前领域对象 - 连接目标:`ConnectionTarget` - 连接 profile:`ConnectionProfile` - inspect 请求:`InspectRequest` - query 请求:`QueryRequest` - export 请求:`ExportRequest` - 共享错误:`db_app::AppError` - 共享状态: - `AppOperation` - `OperationState` - `AppEvent` ### 3.2 当前持久化边界 - 当前产品仍是单次命令执行模型 - backend 当前不引入: - profile 持久化库 - 历史记录库 - 本地状态库 - 后台 daemon / session 存储 - 当前可见写路径只有: - 用户显式指定的 export 输出文件 - 用户显式选择的 SQLite 数据库文件 ### 3.3 迁移结论 - 当前阶段没有 backend 自身的持久化 schema,因此没有迁移脚本或迁移兼容问题 - usable-v1 当前不需要新增 migration 策略 - 若未来引入本地状态存储,必须先单独定义存储边界与兼容策略,再进入实现 ## 4. `db-app` 当前稳定契约 ### 4.1 Connect - 输入:`ConnectionProfile` - 成功结果:`ConnectResponse` - 当前状态语义: - `success` - 不存在 `empty` ### 4.2 Inspect - 输入:`InspectRequest` - 成功结果:`InspectResponse` - `payload.kind`: - `schemas` - `tables` - `columns` - 当 `payload.kind=schemas` 时,schema item 额外包含: - `name` - `availability`:`ready | restricted` - `note`:可选 restricted 说明 - 当前状态语义: - 非空集合 => `success` - 空集合 => `empty` - 执行失败 => `error` - PostgreSQL root inspect 现会保留 restricted schema 名称,并通过 schema item 暴露权限可见性 - 显式 inspect 某个存在但当前连接无权访问的 PostgreSQL schema 时,当前返回 `error`,不再把 restricted schema 误判为 `empty` ### 4.3 Query - 输入:`QueryRequest` - 成功结果:`QueryResponse` - `result` 字段固定包含: - `columns` - `rows` - `rows_affected` - `row_count` - 当前状态语义: - `rows_affected != null` => command-style success - 返回至少一行 => `success` - 零行且 `rows_affected == null` => `empty` - 执行失败 => `error` ### 4.4 Export - 输入: - `QueryRequest` - `ExportRequest` - 成功结果:`ExportResponse` - 成功结果固定包含: - `target` - `source` - `export` - `row_count` - 当前状态语义: - 文件写入成功 => `success` - `row_count == 0` 仍算 success ## 5. CLI 当前契约 ### 5.1 命令面 - `connect` - `inspect` - `query` - `export` ### 5.2 输出模式 - `--result-format text` - `--result-format json` ### 5.3 成功输出 - text:面向人工阅读 - json:返回 envelope ```json { "status": "success", "operation": "query", "state": "success", "data": {} } ``` ### 5.4 当前退出码约定 - 成功:`0` - 当前 CLI 错误统一返回:`2` ## 6. 错误边界矩阵 ### 6.1 共享应用层已统一的错误 当请求已经进入 `db-app` 后,当前错误会统一收敛为 `AppError`: - `validation` - `connection` - `authentication` - `inspect` - `query` - `export` 这些错误在 CLI `--result-format json` 下当前会输出结构化 error envelope,并显式带出 `state: error`。 ### 6.2 当前实际存在的 CLI 前置错误层 在进入 `db-app` 之前,`apps/cli` 仍有一层前置错误来源: - CLI 参数缺失 / 非法 - `ConnectionTarget` 组装失败 - `ConnectionProfile` 组装失败 - `InspectRequest` 构造失败 - `QueryRequest` 构造失败 - `ExportRequest` 构造失败 - SQL 文件读取失败 - password env 缺失 但在当前源码下,只要用户显式传入 `--result-format json`,这组前置错误也会统一包裹成 JSON `error` envelope,`kind` 固定为 `validation`,`state` 固定为 `error`。 这些错误当前仍是字符串错误,不是 `AppError`,因此不完全受 shared error contract 保护。 ## 7. 当前 gap / blocker 清单 ### 7.1 当前 CLI JSON 状态映射 - CLI JSON envelope 当前固定包含: - `status` - `operation` - `state` - 当前映射规则: - envelope 级成功仍返回 `status: success` - envelope 级失败仍返回 `status: error` - 共享应用层状态通过 `state` 暴露: - `connect`:`success` - `inspect`:`success | empty` - `query`:`success | empty` - `export`:`success` - 所有错误:`error` 当前判断: - 这让 CLI JSON 回归样本能显式区分 `empty` - 同时不破坏既有 `status=success/error` 的 envelope 语义 ### 7.2 运行环境说明 - 当前 runner 默认缺少 `cargo` / `rustc` / `cc` - 本轮通过用户态 Rust toolchain 与 `zig cc` / `zig ar` 包装器完成了源码重建与测试 - 这属于运行环境债务,不是 backend contract defect ### 7.3 Blocker 判断 - 当前没有新的持久化 blocker - 当前没有 migration blocker - 当前没有新的 shared contract blocker - 当前没有新的 backend contract blocker - 当前 restricted-schema 剩余缺口已收敛为: - TUI / GUI consumer 侧消费 schema item `availability` - live fixture 权限模型仍需 QA 与 demo 环境配合验证 ## 8. 本轮验证证据 静态核对: - `crates/db-core/src/lib.rs` - `crates/db-config/src/lib.rs` - `crates/db-drivers/src/lib.rs` - `crates/db-app/src/lib.rs` - `apps/cli/src/main.rs` - `TUI_BACKEND_CONTRACT.md` 命令复核: - `cargo run -q -p dbtool-cli -- inspect --driver sqlite --path /tmp/notfound.sqlite --table accounts --result-format json` - 实际返回 JSON error envelope,`kind=validation`,`state=error` - `cargo run -q -p dbtool-cli -- query --driver sqlite --path /tmp/notfound.sqlite --sql '' --result-format json` - 实际返回 JSON error envelope,`kind=validation`,`state=error` - `cargo run -q -p dbtool-cli -- connect --driver postgres --host 127.0.0.1 --port 5432 --database demo --username demo --password-env MISSING_ENV --result-format json` - 实际返回 JSON error envelope,`kind=validation`,`state=error` - `cargo run -q -p dbtool-cli -- query --driver sqlite --path --sql 'SELECT ticket_id FROM tickets WHERE status = ?1' --param missing --result-format json` - 实际返回 `status=success` 且 `state=empty` - `cargo test --workspace` - 当前 runner 经过用户态 Rust + `zig cc` 补齐后,全工作区测试通过 ## 9. 当前结论 - usable-v1 当前 shared app 主契约可继续冻结在 `db-app` - 当前 CLI 主体没有重新发明 shared domain 语义 - 当前源码已经补齐 CLI JSON 模式下的前置 validation error 一致性 - 当前源码已经补齐 CLI JSON 对共享 `empty` 状态的显式映射 - 当前 backend 这条 contract snapshot 主线没有新增 open blocker - 当前 backend 更像进入“保持 snapshot 与源码一致 + 发现 defect 就单独拆单”的阶段