# Design ## 关键决策 ### 1. 为什么是三档而不是两档或四档 **两档(公共/独立)的问题**:把「中等复杂度独立部署」一刀切要么进公共池(污染共享 jar),要么直接独立仓(运维成本高、与基座脱节)。中等客户无处安放。 **四档(极轻/轻/中/重)的问题**:极轻和轻的边界在实操里区分意义小,都是「不写 Controller,只配凭据」。增加判定成本不增加决策价值。 **三档(A/B/C)的依据**:以「是否需要新写 Java 代码」(A→B 分界)和「是否在我们机房部署」(B→C 分界)作两个硬切点。两个切点正交、可观测、不依赖主观判断。 ### 2. 各档物理落点约定 | 档 | 物理位置 | artifactId / git 仓 | 部署形态 | |---|---|---|---| | A | `mjava-pro/` 共享 jar | `mjava-pro`(仓内) | 一个 jar 跑 N 客户,端口 9010 | | B | `mjava-ai/mjava-{客户}/` | `mjava-{客户}`(仓内,独立子模块) | 一客户一 jar,端口由客户分配 | | C | 独立 git 仓(不在 mjava-ai) | `mjava-{客户}`(独立仓 root artifact) | 客户自管部署 | **B 档命名**:直接用客户拼音/英文短名,如 `mjava-akds` / `mjava-sikuwh`。禁带客户中文全名或冗长缩写。 **C 档参照**:现有 `cur/mjava-guangming/`(4 模块仓) + akds(独立仓)。命名沿用历史习惯,本 change 不追溯改名。 ### 3. A→B→C 升级路径的硬指标 判定矩阵以「**触发即升档**」为准则,触发条件必须可观测(不靠主观判断「这个客户复杂」): | 升档 | 触发条件(任一即触发) | |---|---| | A → B | (1) 客户需要写自定义 Controller / Service / Entity;(2) 客户需要私有数据库 schema;(3) 客户需要独立部署节点(隔离故障域) | | B → C | (1) 客户提供自己的部署环境(私网/客户机房/客户 K8s);(2) 客户要求源代码进客户 git;(3) 客户要求 mjava 基座做大幅分叉(≥3 个文件改基座) | **反向降档**:不支持。一旦升档,物理资产已经独立,回退成本远高于继续维护。 ### 4. mjava-pro 入驻 SOP(A 档具体步骤) 新客户走 A 档时操作步骤: 1. **校验触发条件**:确认客户不命中 A→B 任一硬指标 2. **宜搭应用表加一行**:tenantId / vendor / appKey / appSecret / extraJson / enabled 3. **凭据热验证**:调 `mjava-pro` 的 `/api/pro/_diag/tenant/{tenantId}` 探活(接口归 add-mjava-pro 实现) 4. **上线通告**:邮件抄送(运维 + 客户对接人),记录 tenantId 进 README 客户清单段 **必填字段(spec R3 正式定义)**:tenantId(业务唯一)/ vendor(dingtalk|aliwork|...)/ appKey / appSecret / corpId(钉钉系才需)/ enabled。其余按 vendor 走 extraJson。 **隔离边界**: - DB:A 档客户共享 mjava-pro 的 DB schema(用 tenantId 字段隔离行级数据) - 缓存:UtilToken key 命名空间扩为 `{tenant}:{vendor}:{appKey}`(add-mjava-pro 已约定) - 日志:traceId 自动带 tenantId 维度(MDC 追加) ### 5. mjava-com 对外暴露白名单规则 `mjava-com` 暴露的是「**mjava 基座的 Client/Service 能力**」,但不是所有方法都自动开放,按以下规则: - **白名单制**:在 `application.yml` 显式列出可暴露的 `{vendor}.{action}`(如 `dingtalk.user.get`、`aliwork.form.save`),未列入即 404 - **危险动作禁开**:删除类(delete)、批量改类(batch update)、租户管理类(tenant.create)默认禁开,需主仓维护者 ACK 才能加 - **限流默认值**:每调用方 60 QPS(本地 RateLimiter,进程级),可在 `application.yml` 按调用方覆盖 - **审计强制**:每次调用打全字段审计日志(调用方 / vendor / action / 入参摘要 / 出参摘要 / 耗时 / 成功标志),不可关闭 **审批流程**:新增白名单条目走 PR + 主仓维护者 review,commit message 标 `feat(com): 开放 {vendor}.{action} 暴露`。 ### 6. C 档客户独立仓与 mjava-ai 的关系 C 档客户仓**不依赖** mjava-ai 发布的 jar,而是 fork 基座源码到自己仓内(如 `mjava-guangming/mjava/`)。原因: - 客户私网 / 客户机房环境通常无法访问我们的 maven 私服 - 客户要求源码进客户 git,jar 依赖模型不可行 - 客户的基座分叉常需要快速改(如临时加字段、兼容客户老接口),等上游回流太慢 **版本号约定**:C 档客户仓内 `mjava` 基座 pom 的 `version` 应**保持与上游同步的语义版本**(如 mjava-ai 的 mjava 是 0.0.3,则客户仓的 mjava 也是 0.0.3,分叉改动通过 git 历史追溯,**不**改版本号)。这样未来客户问「我用的是哪版上游」时,git diff 直接可比。 **回流通道(可选)**:客户仓如有通用价值的改动(修 bug、加产品方接口),鼓励上游 cherry-pick 回 mjava-ai。具体流程不强制,按工程师习惯。 **R4 跨仓 grep 范围**:C 档客户仓内的代码**不**纳入 mjava-ai 的 R4 grep(已在 standardize-client-service-layering 的 design 里约定「基础建设期默认仅本仓」),与本 change 一致。 ### 7. README / CLAUDE.md 改写策略 README 「子项目速览」表加「档位」列,但**不在 README 直接展开判定矩阵**(避免重复)。改为: ```markdown | 子模块 | 端口 | Context | 档位 | 状态 | |--------|------|---------|------|------| | `mjava-mcli` | 9001 | /api/mcli | B 模板 | 新客户模板 | | `mjava-pro` | 9010 | /api/pro | A 容器 | 多租户单部署骨架 | | `mjava-com` | 9020 | /api/com | — | BaaS 网关骨架 | ``` 「新客户接入」段改为指向 `openspec/specs/customer-tiering/spec.md`(archive 后位置),三档分流由 spec 权威。 CLAUDE.md 加一行:「**新客户接入**:先查 customer-tiering 决策树选档 → 按档执行 SOP」。 ## 拒绝的方案 - **只写文档不入 spec** — 拒绝。客户接入决策是高频判断点,必须走 capability spec 才能被 R4 类规则交叉引用 - **强制所有客户必先入 A 档观察 1 个月再决定升 B/C** — 拒绝。增加客户接入摩擦,且部分客户初次接入就明显是 C 档(如客户机房),强观察期是无效流程 - **B 档不在 mjava-ai 仓内,单独建 mjava-customers 仓** — 拒绝。B 档客户少时拆仓成本高于聚合;mjava-ai 仓内子模块隔离已足够,pom 聚合也能并行编译 - **A→B 触发条件包含「客户数据量」(如 > 1M 行)** — 拒绝。数据量是结果不是原因;真正的原因是「需要私有 schema」,已在 A→B (2) 覆盖 - **C 档客户仓强制每月同步上游 mjava** — 拒绝。客户仓独立维护权属客户工程师,强同步反而干扰客户工程师节奏 - **将「客户复杂度」作为判定因子** — 拒绝。「复杂」不可观测,会变成主观打分;改用「需要写 Java 代码」「需要客户机房」两个硬切点