# Design ## 关键决策 ### 1. 为什么用 capability spec 而不是只改 baseline `project-baseline.md` 现状是「指路 + 锚点 + 子项目清单」,纯结构指引。R1~R7 是**代码组织约束**(行为规则),属于 capability 层面。新建 `client-service-layering` capability spec 后: - baseline 只加一行锚点指向新 spec - 权威正文进共享 `mjava-baseline.md`(跨仓库引用) - 仓库内 spec 用 `## ADDED Requirements` 段落对齐 OpenSpec 模式 这样 baseline 保持「薄」,规则改动走 capability spec 的 propose/archive 流程,演进路径清晰。 ### 2. R5 命名:为什么选 `Impl` 后缀 仓内现状: - 后缀:宜搭 3 文件(YDClient_FormImpl/YDClient_ProcessImpl/YDClientImpl/YDServiceImpl) - 后缀:北森 4 文件(BSClient_AttendanceImpl/BSClient_EmployeeImpl/BSClientImpl/BSServiceImpl) - 后缀:其他 6 个产品所有 impl(EKB/FXK/CY/VK/XBB/TB/INTP/ALY 共 ~10 文件) - 中缀:钉钉 14 文件(DDImplClient + DDImplClient_X×12 + DDImplService) **14 文件中缀 vs ~17 文件后缀** —— 后缀是事实多数派。R5 选后缀,钉钉作为少数派改名,影响面已知(钉钉 14 文件 + `@Qualifier` 字面引用)。 ### 3. R7 准入条件:为什么是「≥2 个客户子项目复用」 mjava 基座的价值是「**多客户复用的二次封装**」。单客户编排放基座会污染共享层: - 客户 A 的特殊错误码翻译进 `XxxService` → 客户 B 调用时被强加上 - 客户 A 的特定字段映射进 Service → 客户 B 的字段被误转 阈值定 2 是最低门槛:**第 2 个复用方出现 = 抽象有市场**。第 1 个复用方需要时,先在自己客户子项目里写编排;第 2 个想用时再上提到基座 Service。 > 实操:第 1 个复用方写代码时,可以在子项目放在 `service/{product}/` 下用同样命名,后续上提时一份代码两边引用。 ### 4. R4 变更确认:grep 范围 **当前阶段(基础建设期)**:mjava-ai 是纯基础建设项目,无实际客户子项目依赖本仓基座 jar,grep 范围 = **mjava-ai 本仓**。 **客户接入后**:当某客户独立仓(如未来 akds / 光明独立仓 / 新客户)开始依赖 mjava-ai 发布的基座 jar 时,由用户在 ACK 流程中显式补充该仓库路径,扫描范围随之扩展。 > 历史背景:光明独立仓 `/Users/malk/server/cur/mjava-guangming/` 自维护了一份 mjava 基座的分叉副本(独立 git 仓),不依赖 mjava-ai;akds 同样独立仓。这些客户仓的 R4 grep 由各自仓内自行执行,不与 mjava-ai 的 R4 流程联动。 grep 模板: ```bash # 接口签名 grep -rE "\.{methodName}\(" --include="*.java" {repo} # 类名引用(迁移/重命名) grep -rE "(import|extends|implements)\s+.*\.{ClassName}" --include="*.java" {repo} ``` 报告格式: ``` 方法/类: com.malk.service.dingtalk.DDClient.foo mjava-ai: 3 处 - mjava-mcli/src/.../Boot.java:42 - mjava-pro/src/.../Xxx.java:88 - mjava/src/.../impl/DDImplClient.java:156(自定义,不计) akds: 1 处 - apps/web/src/.../Yyy.java:33 光明独立仓: 0 处 ``` ### 5. O3 ALYConf 内容 `ALYInvoiceImpl` 内 4 个 URL 字面量: ``` https://fapiao.market.alicloudapi.com/v2/invoice/pdf → URL_INVOICE_PDF https://fapiao.market.alicloudapi.com/v2/invoice/query → URL_INVOICE_QUERY https://fapiao.market.alicloudapi.com/v2/invoice/qrcode → URL_INVOICE_QRCODE https://invoice.market.alicloudapi.com/v2/invoice/ocr → URL_INVOICE_OCR ``` `ALYConf` 不用 `@ConfigurationProperties`(appcode 是调用方传参,不进 yml),只放 `public static final String` 常量。 ### 6. 为什么 O1 钉钉改名拆独立 change - 文件改动量大(14 文件 + Bean 名 + Qualifier 字面引用扫) - 跨仓引用扫范围至少 4 个仓库(mjava-ai/akds/光明/其他客户) - 失败回滚成本:1 个文件改坏可能拖累整个钉钉栈 独立 change `rename-dingtalk-impl-suffix` 让改名风险隔离,可按文件批次推进。 ## 拒绝的方案 - **直接进 mjava-baseline.md 不建仓库 spec** — 拒绝。OpenSpec 是仓内单一事实源,跨仓库共享文档需仓内 spec 作锚点 - **R7 阈值定 1**(只要有 1 个复用就上提)— 拒绝。会鼓励过早抽象 - **R5 选中缀** — 拒绝。事实多数派是后缀 - **同时改名钉钉 12 文件** — 拒绝。爆炸半径过大,单独 change 隔离