学习 BitzOrcas 通用接口的调用方不应先去理解某个垂直行业的对象、法规、角色或历史数据库列名。行业渗透的经典路径是静默的:某个”通用”合同实体上突然出现 LawyerFee 字段,某条错误文案提到法院,默认 Profile 通过传递依赖悄悄拽进法律计算器——等到发现时,中立性已经无法重建。BitzOrcas 把边界画在所有权层面:Legal、Finance、HR 与 Auction 能力位于显式可选模块或商业包中;Framework 共享内核、通用 Profile 的平台模块与 Host 注册保持行业中立。
必须保持无垂直属性的部分
src/Framework/**- 通用 Profile 默认的平台模块
src/Hosts/**中用于通用组合的注册/配置/展示文本- Consumer Solution 与
src/Modules/Sandbox/**示例代码
所有权规则
- 垂直能力(Legal/Finance/HR/Auction)放入显式可选模块或商业包——绝不放入 Framework 共享内核。
- 垂直模块的类型、错误码与枚举由 owner 包发布;默认 Host 不为未选中的包注册运行时 enum/适配器。
- 通用模块的 CLR 名称、持久化列、配置键、错误文本、通知模板与运维诊断使用行业中立术语。
- 示例仅展示通用业务动作及直接依赖;垂直示例位于可选模块自己的测试与文档中。
- 带行业语义的历史物理列通过显式、幂等、遇冲突即停止的数据迁移迁移到中立列名——绝不依赖 ORM 自动建列来强制人工数据搬运。
- 规则日期、节假日与动态标准属于调用接口;纯计算模块不得隐式读取进程当前时间或把动态法规伪造为固定默认值。
当前所有权
| 能力 | Owner | 默认表面 |
|---|---|---|
| 通用 + 贷款利息计算 | IndustryExtensions.Finance | 不在 Framework 或通用 Profile 中 |
| 法律期限 + 法院案号 | LegalCalculators | 不在 Framework 或默认 Host enum 目录中 |
| 法律费用、诉讼时效、损害赔偿 | LegalCalculators | 仅显式可选 |
| HR 假期策略 + 日期规则 | IndustryExtensions.Hr | 显式传 effectiveFrom;不读取进程日期 |
| Website 联系线索 owner | Website.AssignedOwnerId | CLR + 物理列均为中立名 |
AssignedLawyerId → AssignedOwnerId 迁移
WebsiteLead.AssignedLawyerId 仅作为遗留 DB 升级输入存在。迁移脚本 20260716-website-owner-industry-neutral.sql 在新旧列同时存在时拒绝继续,避免静默覆盖 owner 数据。
模板采纳契约
模板暴露单一耦合预设参数 ProfileChoice。合法取值空间为 mini-api-single、default-business-{single|multi}-{none|finance|hr|auction|legal},默认为 default-business-single-none。mini+api+industry 组合无法通过公开参数表达;旧的 --IndustryExtension 标志已未知并在任何磁盘写入前失败。每个行业预设向 Business Starter Application 添加恰好一个根 PackageReference,并生成一个 IndustryExtensionAdoptionTests.cs 直接调用所选包的公开 API。四个行业包绝不进入 Profile.Mini.Api 或 Profile.Default.Business 默认闭包。
行业预设的真实采纳方式——一条命令,非法组合在任何磁盘写入之前即被拒绝:
# 以 legal 行业预设生成多租户业务 Host;模板在校验 ProfileChoice 之后才会落盘。dotnet new bitzorcas-host --ProfileChoice default-business-multi-legal --output LegalHost生成的解决方案自带 IndustryExtensionAdoptionTests.cs:直接调用法律包公开 API,让”行业包真的被消费”成为测试事实而非评审承诺。模板产物的全链复核由框架仓库的 scripts/build/verify-template.sh 承担(安装模板 → 按 ProfileChoice 组合生成 → 空缓存还原/构建/测试)。
未选中的三个行业包不会出现在生成的项目引用里——中立性由空闭包证明,而不是靠注释声明。
源码证据边界
| 要证明的事实 | 应检查的 owner 证据 |
|---|---|
| Framework 没有行业类型 | src/Framework 的命名空间、公开 API 与依赖图 |
| 默认 Host 不隐式采用行业包 | Host 项目引用、组合注册与生成的 Profile 闭包 |
| 行业能力由自己的包发布 | IndustryExtensions、LegalCalculators 与 connector Contracts |
| Website owner 已中立化 | AssignedOwnerId 聚合、持久化元数据与双 ORM migration |
| 旧列升级不丢数据 | 两列冲突即停止、旧列复制、重跑幂等与迁移目录测试 |
| 模板选择不可组合出非法 Profile | 模板参数 schema、空缓存生成和 AdoptionTests |
中立性不是关键词扫描就能完全证明。一个通用名称如果仍返回法律行业 DTO,或默认 Profile 通过传递依赖带入行业包,同样违反边界。评审时要同时看公开 API、包闭包、生成模板和运行时注册。
变更评审问题
- 新类型属于通用平台事实,还是只有某个行业才能解释的事实?
- 名称、错误码、配置键、表列和诊断文本是否都使用同一套中立语言?
- 时间、地区规则和政策版本是否由调用方显式提供?
- 默认 Host、Mini Profile 与 Consumer Solution 是否仍能在不引用行业包时构建?
- 可选包是否只通过 Contracts、Options、Policy、Store Port 或事件接入?
- 迁移是否在新旧列并存、旧列缺失和重复执行时给出确定结果?
- 行业预设是否只增加一个根包,并由独立消费测试调用公开 API?
- 删除可选包后,通用 Profile 是否没有残留注册、枚举或配置要求?
复核命令
# 中立迁移、模板预设和显式行业采用测试。rg -n "AssignedLawyerId|AssignedOwnerId|ProfileChoice|IndustryExtensionAdoptionTests" \ src scripts tests -g '*.cs' -g '*.json' -g '*.sql'
# 默认项目闭包不得意外包含四个行业根包。rg -n "IndustryExtensions|LegalCalculators" \ src/Tooling/BitzOrcas.Modern.Templates -g '*.csproj' -g '*.json'