本章讨论连接器“如何进入进程”。当前扩展只绑定 Port 到 Adapter,不负责注册厂商 SDK;顺序、配置与健康验证都属于 Host 组合根。
1. 条件注册算法
三个连接器独立执行同一算法。bool.TryParse 让缺失或非法值按 false 处理,这是配置层的 fail-closed。
2. 正确的组合顺序
// ① SDK 必须先进入 IServiceCollection,条件注册才能看见其 ServiceType。if (configuration.GetValue<bool>("Connectors:Ocr:Enabled")) services.AddOCRService(configuration.GetSection("Connectors:Ocr"));
// ② 然后选择 Port 的真实或不可用实现。services.AddBitzOrcasToolConnectorsAdapters(configuration);
// ③ 启动验证应解析 IOcrPort,并执行不泄露敏感数据的 readiness probe。源码 XML 示例把 Adapter 注册放在 SDK 之前;若照抄,探测时看不到 SDK,先放入 Unavailable。由于方法使用 TryAddScoped,之后再次调用也不会覆盖早先的 Port 注册。这是文档必须纠正的顺序敏感点。
3. TryAdd 的含义
TryAddScoped<TPort,TAdapter> 保留此前注册。它支持宿主显式替换测试实现,也可能让错误顺序永久保留 unavailable 或第三方实现。
生产组合根应:
- 集中注册,避免多个扩展隐式争夺同一 Port;
- 在启动测试中断言最终
ImplementationType; - 明确自定义覆盖发生在默认注册之前还是之后;
- 不依赖重复调用“修复”先前选择;
- 对 Enabled=true 但 SDK 缺失记录结构化启动诊断。
4. 当前 Host 状态
BitzOrcas.Api.csproj 引用 Contracts 是治理可见性安排。源码未引用 Infrastructure,未调用 Tool Connectors DI,也未调用三个 SDK 扩展。
因此当前可以说:
- 商业包目录可发布 Contracts/Infrastructure;
- 防腐 Port 和 Adapter 可被其他 Host 复用;
- 未配置时的 Unavailable 行为有实现;
当前不能说:
- API Host 已开放连接器;
- 租户开启 Feature 即可使用;
- 健康检查证明供应商可达;
- 权限已经保护了某条路由。
5. 配置与 Feature 分工
| 层次 | 当前 Key | 应回答的问题 |
|---|---|---|
| 进程配置 | Connectors:*:Enabled | 本实例是否装配 SDK/Adapter |
| 产品 Feature | tool-connectors.* | 哪个租户/用户可使用能力 |
| 权限 | tool-connectors.*.execute | 哪个角色可执行操作 |
| 业务规则 | owner 状态/审批 | 这次操作是否允许 |
四层不能互相替代。Enabled=false 应使依赖不可用;Feature=false 应在请求层稳定拒绝;无权限应返回授权错误;业务拒绝应给出领域原因。
6. 治理重复声明
owner-local ToolConnectorsModule 已声明模块、依赖、权限和 Feature。PlatformModuleGovernance.cs 仍保留同名旧声明,且注释与现实不符。
重复来源会带来三类风险:扫描器重复注册、描述/默认值漂移、维护者修改错误位置。建议迁移步骤:
- 确认治理扫描能发现 Contracts;
- 为模块码、权限码、Feature Key 建唯一性测试;
- 删除 Platform 中旧声明;
- 更新治理快照和文档;
- 在双 ORM/无数据库 Host 下验证目录一致。
7. 密钥与配置安全
Enabled 可放普通配置,供应商凭据不得进入仓库或日志。配置模型应使用 secret provider 引用、启动时校验必需字段并避免 IOptions dump。
iManage 是按租户创建客户端,更需要定义租户到凭据引用的安全映射。Crawler proxy/cookie、OCR 数据目录也应作为运维资产管理,不能由请求任意覆盖。
8. 健康模型
“DI 可解析”只证明有真实或 unavailable 实现。健康应分层:
- composition:期望的 Adapter 是否被选中;
- configuration:SDK 的必需配置是否完整;
- connectivity:供应商是否可达;
- capability:最小无副作用操作是否成功;
- dependency state:限流、熔断或凭据是否异常。
Readiness 不应执行上传、签出或打开任意公网 URL。探针必须低成本、可限流且不泄露租户数据。
9. 启动验证示例
// ① Enabled 来自部署期望,而不是从 Port 类型猜测。bool expected = configuration.GetValue<bool>("Connectors:Ocr:Enabled");
// ② 从作用域解析,因为 Port 注册为 Scoped。await using AsyncServiceScope scope = provider.CreateAsyncScope();IOcrPort port = scope.ServiceProvider.GetRequiredService<IOcrPort>();
// ③ 生产代码宜暴露显式 capability descriptor,避免依赖类型名。if (expected && port is UnavailableOcrPort) throw new InvalidOperationException("OCR enabled but adapter unavailable.");最后一行可用于当前集成测试;正式健康契约应由公共 capability 接口表达,避免 Host 依赖 Infrastructure 内部类型。
10. 发布配置清单
每个连接器的发布说明至少列出:
- SDK 包版本和兼容矩阵;
- Enabled 与 Feature 默认值;
- 必需 secret/config;
- 网络出口、DNS、代理、证书要求;
- 超时、并发、速率限制;
- 数据区域与供应商处理位置;
- 健康探针和告警;
- 禁用、回滚和凭据轮换步骤。
11. 组合合同测试
参数化测试应覆盖三连接器 × 四状态:Disabled、Enabled+SDK 缺失、Enabled+SDK 存在、非法配置。另测预注册覆盖和错误调用顺序。
// ① 构造 Enabled=true,但故意不注册 IOCRService。var services = new ServiceCollection();var configuration = BuildConfig(("Connectors:Ocr:Enabled", "true"));
// 未调用 AddOCRService。services.AddBitzOrcasToolConnectorsAdapters(configuration);
// ② Port 可解析,但选择结果必须是 unavailable。using ServiceProvider provider = services.BuildServiceProvider();using IServiceScope scope = provider.CreateScope();var port = scope.ServiceProvider.GetRequiredService<IOcrPort>();
Assert.IsType<UnavailableOcrPort>(port);12. 源码核查
# ① 检查条件选择、SDK 探测和三个配置键。rg -n "TryAddScoped|IsSdkServiceRegistered|EnabledKey" \ src/Platform/ToolConnectors -g '*.cs'# ② 对照治理声明与 Host 组合调用。rg -n "ToolConnectorsModule|tool-connectors\." src/Platform -g '*.cs'rg -n "AddBitzOrcasToolConnectorsAdapters" src/Hosts -g '*.cs'