Files
workspace/code/fms/FMS新系统核心架构设计.md
2026-10-07 22:01:17 +08:00

17 KiB
Raw Permalink Blame History

FMS 新系统核心架构设计 V2.0

文档版本:V2.0
编制日期:2026-09-30
文档状态:核心架构设计草案
适用范围:FMS/ERP 新系统第一阶段重构

1. 文档目的

本文档重新定义 FMS 新系统的核心架构,重点解决以下问题:

  • SQL 是系统的第一等公民;
  • 代码风格和模块实现方式统一;
  • 第一阶段保持简单,不建设通用低代码平台;
  • 真实业务表、业务 SQL 和业务代码拥有清晰边界;
  • 支持一个租户多个公司,但不提前建设复杂的平台能力。

本文档替代旧设计中以 s_module、通用 CRUD 和动态 JSON 为中心的架构思路。旧表结构中的业务数据、工作流和可复用 SQL 可以迁移,但运行时中心需要重新组织。

2. 根本改变

2.1 旧架构

旧设计的主链路是:

s_module
→ s_field
→ s_module_schema
→ 通用 SQL 拼接
→ 通用保存
→ 页面

它的优点是页面和简单模块创建较快,缺点是业务模块逐渐依赖元数据解释器,容易变成一个低代码平台。

2.2 新架构

新设计的主链路是:

业务域代码包
→ 真实 SQL 表和视图
→ 业务查询与业务动作
→ API / 页面

新系统的中心不再是全局模块元数据,而是业务域代码包。每个业务域自己拥有表结构、迁移、SQL、业务动作和页面入口。

客户域
├── 数据库迁移
├── 业务表和视图
├── 查询 SQL
├── 保存 SQL
├── 业务动作
├── 接口
├── 页面
└── 多语言资源

2.3 核心原则

业务域是组织中心
SQL 表和视图是事实来源
SQL 文件是查询和处理的主要实现
业务代码负责动作和事务
页面是业务域的使用界面
配置只解决少量表现和参数问题

3. 总体架构

租户注册配置
    │
    ▼
租户数据库路由
    │
    ▼
SQL Server 租户数据库
    ├── 系统内核
    │   ├── 用户、角色和业务上下文
    │   ├── 公司、组织、仓库和账簿
    │   ├── 多语言和审计
    │   └── 文件和运行记录
    ├── 业务域
    │   ├── 客户和供应商
    │   ├── 产品和基础资料
    │   ├── 销售
    │   ├── 采购
    │   ├── 库存
    │   └── 财务
    ├── 工作流
    └── SQL 视图和报表

运行时链路:

请求
→ 租户路由
→ 建立用户和公司上下文
→ 进入业务域
→ 执行查询或业务动作
→ SQL 事务
→ 审计记录
→ 返回结果

4. 核心内核

核心内核只提供所有业务域都需要的能力,不负责解释所有业务。

4.1 租户路由

第一阶段采用一个租户一个数据库:

  • 租户注册信息使用配置文件;
  • 一个租户对应一个 SQL Server 数据库;
  • 租户库内不重复使用 tenant_id 过滤业务数据;
  • 数据库连接由统一的 TenantRegistry 获取;
  • 每个租户数据库单独执行迁移并记录版本。

平台库、在线租户开通和集中运维不进入第一阶段。

4.2 业务上下文

每次请求建立统一上下文:

tenant_code
user_id
company_id
organization_id
warehouse_id
ledger_id
language
timezone
currency
request_id

上下文由运行时创建,业务域只能读取,不由页面自行拼接。SQL 和业务动作通过上下文得到当前公司、用户和账簿。

4.3 基础表

第一阶段的系统基础表:

s_user
s_role
s_user_role
s_company
s_organization
s_warehouse
s_ledger
s_user_company
s_user_organization
s_user_warehouse
s_user_ledger
s_i18n_type
s_i18n
s_audit_log
s_file

角色先采用简单模型,不实现角色继承和复杂拒绝规则。公司、组织、仓库和账簿是明确的独立对象,不使用一个 org_id 代替它们。

5. 业务域代码包

5.1 业务域定义

业务域是新系统的主要代码组织单位,例如:

customer
supplier
product
sales
purchase
inventory
finance
workflow

一个业务域负责自己的:

  • 业务表和表关系;
  • 数据库迁移;
  • 查询视图;
  • 查询 SQL;
  • 保存 SQL;
  • 业务动作;
  • 事务边界;
  • API;
  • 页面;
  • 多语言资源;
  • 审计事件。

业务域之间通过明确的业务表关系、SQL 查询和服务接口协作,不通过全局 JSON 规则互相解释。

5.2 业务对象

业务对象仍然存在,但它属于业务域,而不是全局通用元数据引擎。

一个业务对象至少包含:

对象编码
主表或视图
查询 SQL
保存方式
业务动作

例如销售订单:

sales_order
├── b_sales_order
├── b_sales_order_line
├── sales_order_list.sql
├── sales_order_save.sql
├── submit_sales_order()
└── approve_sales_order()

5.3 业务域目录

建议使用以下代码组织方式:

fms/
├── core/
│   ├── tenant/
│   ├── context/
│   ├── auth/
│   ├── audit/
│   └── i18n/
├── domains/
│   ├── customer/
│   │   ├── migration/
│   │   ├── sql/
│   │   ├── repository/
│   │   ├── service/
│   │   ├── api/
│   │   └── page/
│   ├── product/
│   ├── sales/
│   ├── purchase/
│   ├── inventory/
│   └── finance/
├── workflow/
└── shared/

每个业务域使用同一种目录结构,不允许某个模块把 SQL 放数据库字段、另一个模块把 SQL 放页面代码、第三个模块再放配置 JSON。

6. SQL 设计

6.1 SQL 的职责

SQL 负责:

  • 查询;
  • 关联;
  • 分组和统计;
  • 排序和分页;
  • 批量更新;
  • 事务内的数据处理;
  • 报表和视图。

业务代码负责:

  • 业务动作入口;
  • 事务编排;
  • 状态变化;
  • 幂等处理;
  • 跨模块规则;
  • 库存和财务业务约束。

6.2 SQL 文件规范

SQL 统一放在业务域目录下:

domains/sales/sql/
├── order_list.sql
├── order_detail.sql
├── order_save.sql
├── order_submit.sql
└── order_report.sql

SQL 命名统一使用:

对象_动作.sql

例如:

customer_list.sql
customer_save.sql
customer_disable.sql
sales_order_submit.sql
inventory_lock.sql

6.3 通用 CRUD 的边界

新架构不移除通用 CRUD,但不把通用 CRUD 作为所有业务的默认核心。它可以用于简单基础资料,核心业务必须由业务域自己的 SQL 和业务动作实现。

以下内容不再作为系统核心:

  • 数据库中的 b_query_sql 作为所有模块的默认查询入口;
  • b_save_table 驱动所有业务模块的通用保存;
  • b_order_sql 在运行时任意拼接排序;
  • 全局 data / virtual 模块解释器;
  • 页面 JSON 中的业务公式和业务写入规则;
  • 一张通用配置表承载所有业务行为。

内部开发人员可以自由编写 SQL,但 SQL 由对应业务域维护,并与代码和迁移一起发布。

6.4 通用保存边界

通用保存仅用于简单基础资料。以下内容必须使用业务域专用服务和 SQL:

  • 库存扣减、锁定和释放;
  • 采购和销售状态推进;
  • 财务过账和反过账;
  • 公司间交易;
  • 审批提交和审批动作;
  • 任何需要多个业务表一致提交的操作。

7. 数据库模型

7.1 表分层

s_*    系统内核和控制数据
b_*    业务主表和业务明细表
wf_*   工作流定义和运行数据

核心业务表使用真实列和真实关系,不使用全量 EAV。

7.2 公司字段

公司级业务表根据业务实际需要包含:

company_id
organization_id
warehouse_id
ledger_id

跨公司业务另外保存:

source_company_id
target_company_id
settlement_company_id

共享基础资料不复制多份,使用共享范围或公司关联表表达。

7.3 动态字段

第一阶段不支持普通用户通过页面配置直接创建数据库字段。新增业务字段使用:

迁移脚本增加物理列或扩展表
→ 更新查询 SQL 和视图
→ 更新页面字段定义
→ 增加多语言资源

字段分为:

  1. 核心字段:真实列,参与业务逻辑和统计;
  2. 扩展字段:通过迁移增加的可选列或扩展表字段;
  3. 低频扩展:JSON,仅用于非核心展示。

需要筛选、排序、统计或导出的字段不能只放在 JSON 中。

运行时字段分为两种情况:

  • 表现字段:标签、显隐、只读、排序和布局可以运行时调整;
  • 物理字段:需要存储、查询、排序、统计或导出的字段,必须通过 SQL Migration 增加物理列或扩展表。

后续如果确实需要管理员在线增加物理字段,可以增加一个受控的字段变更服务,由它生成并执行 SQL Migration,同时更新视图、字段定义和页面配置。这个过程仍然是数据库结构变更,不是普通 JSON 配置。

7.4 数据库约束

数据库负责结构完整性:

  • 主键;
  • 外键;
  • 唯一索引;
  • 非空约束;
  • 必要 CHECK;
  • 查询和关联索引。

不使用触发器替代库存、财务和审批业务流程。业务规则放在业务域服务和事务 SQL 中。

8. 页面和配置

8.1 页面不是元数据生成的

核心业务页面由业务域代码实现。页面可以复用通用列表、表单和查询组件,但页面的字段、动作和业务行为由业务域明确声明。

页面配置只保存:

  • 列显示顺序;
  • 列宽;
  • 表单分组;
  • 查询默认值;
  • 用户个人偏好;
  • 显示格式。

不使用 JSON 描述完整业务页面和业务处理器。

8.2 模块表的定位

如果保留 s_module,它只用于:

  • 菜单和路由登记;
  • 权限对象登记;
  • 模块名称和多语言资源键;
  • 业务域对象的索引。

它不负责:

  • 生成所有 SQL;
  • 推断所有保存逻辑;
  • 承载业务状态;
  • 解释任意 JSON 规则。

8.3 公司差异

第一阶段不建设通用的租户、公司、部门、角色多层配置继承。

确实存在公司差异时,在具体业务表中直接使用 company_id,例如:

s_number_rule(company_id, domain_code, field_code)
wf_binding(company_id, domain_code, event_code)
s_print_template(company_id, template_code)

不把所有差异都抽象为一个通用覆盖引擎。

9. 权限

权限采用简单的角色模型:

s_role
s_user_role
s_role_permission
s_role_data_scope
s_user_company

权限对象绑定业务域和业务动作:

customer.read
customer.create
sales_order.submit
sales_order.approve
inventory.adjust
finance.post

第一阶段支持:

  • 用户可用公司;
  • 用户可用组织;
  • 用户可用仓库;
  • 模块访问权限;
  • 业务动作权限;
  • 简单数据范围。

不实现角色继承、拒绝权限和通用权限表达式。复杂范围由对应业务域的 SQL 实现。

10. 工作流

工作流是业务域的流程编排器,不是业务规则中心。

工作流绑定业务域动作:

sales_order.submit
sales_order.approve
purchase_order.submit
expense.approve

工作流可以决定:

  • 下一节点;
  • 审批人;
  • 当前流程状态;
  • 需要调用哪个业务动作。

业务域负责真正的动作:

  • 销售订单批准后是否允许出库;
  • 采购收货如何增加库存;
  • 财务单据如何过账;
  • 审批失败如何补偿。

工作流实例固定流程版本,历史审批记录只追加不修改。

11. 多语言

第一阶段保持简单:

s_i18n_type
s_i18n

系统文案使用稳定资源键:

sales.order.name
sales.order.customer
action.submit

业务主数据使用业务域自己的翻译表:

b_product
b_product_translation
b_customer
b_customer_translation

系统资源可以放在代码资源文件或租户库中,但必须统一一种来源。第一阶段不实现三层 overlay、翻译变更集和运行时语言管理。

历史单据保存产品名称、客户名称、税率、地址、币种和金额等快照。

12. 配置和版本

第一阶段不建设覆盖所有对象的通用配置版本引擎。

配置分为三类:

类型 实现方式
数据库结构 SQL Migration
业务逻辑 代码和业务 SQL
页面表现 页面代码和少量布局配置
工作流 独立流程版本表
业务参数 明确的参数表

只有工作流、编号规则、打印模板等确实需要历史追溯的内容,才单独设计版本。

12.1 SQL Migration

SQL Migration 是按顺序执行、可追踪的数据库结构变更脚本。例如:

migration/
├── V001__create_company.sql
├── V002__create_customer.sql
└── V003__add_customer_tax_no.sql

迁移工具在每个租户数据库中记录已执行的版本。发布新代码时,先执行缺少的迁移,再启动依赖新结构的代码。

Migration 可以包含:

  • 建表、改表和删表;
  • 增加或停用字段;
  • 创建视图、索引和约束;
  • 初始化字典和系统数据;
  • 必要的数据回填。

Migration 不等于运行时页面配置。页面配置可以立即保存,数据库结构变化则必须经过迁移记录。

数据库迁移记录:

schema_version
migration_name
checksum
executed_at

13. 代码风格和目录规范

新系统的代码按业务域组织,不按页面组织,也不按一组通用配置表组织。

domains/
  sales/
    migration/
    sql/
    repository/
    service/
    api/
    page/
    i18n/

统一要求:

  • 一个业务动作只有一个主要入口;
  • SQL 统一放业务域的 sql 目录;
  • 页面只调用业务域 API,不直接写业务 SQL;
  • 业务域不通过另一个域的内部表直接修改数据;
  • 数据库表、SQL、服务、接口和页面使用同一业务名称;
  • 新增抽象必须来自至少两个真实业务域的重复需求。

数据库命名在新系统中一次确定:

  • 表名使用小写 snake_case;
  • 系统表使用 s_;
  • 业务表使用 b_ 或明确业务域前缀;
  • 工作流表使用 wf_;
  • 字段命名统一使用一套规则,不再混用 b_、mx_ 和裸字段名。

14. 第一阶段建设范围

第一阶段只建设架构内核和一条完整业务链路。

14.1 必须完成

  1. SQL Server 迁移工具和数据库版本表;
  2. 租户数据库连接和路由;
  3. 用户、角色、公司、组织、仓库和账簿;
  4. 统一业务上下文;
  5. 业务域目录和 SQL 调用约定;
  6. 基础权限和公司范围;
  7. 审计日志;
  8. 基础多语言;
  9. 一个业务域的表、SQL、服务、接口和页面完整闭环。

14.2 建议示例

建议先做客户或产品域:

SQL Migration
→ 真实业务表
→ 查询 SQL
→ 保存 SQL
→ 业务域服务
→ API
→ 页面
→ 公司权限
→ 审计

这个示例必须验证“业务域代码包是中心”,不只是验证通用页面是否能显示。

14.3 第一阶段不做

  • 以全局 s_module 驱动的通用 CRUD 作为核心业务默认实现;
  • 全局 s_field 驱动的运行时字段创建;
  • 通用页面设计器;
  • 通用公式和规则解释器;
  • 全对象配置版本和变更集;
  • 多层公司配置覆盖;
  • 复杂多语言发布体系;
  • 集团分析库和跨租户业务查询。

15. 第一阶段验收标准

15.1 架构中心

  • 一个业务域拥有自己的迁移、SQL、服务和页面;
  • 业务域可以不依赖通用模块解释器运行;
  • 页面配置不能替代业务代码;
  • 工作流只能调用明确登记的业务动作。

15.2 SQL 驱动

  • 业务查询可以直接在 SQL Server 中验证;
  • 复杂查询使用业务域 SQL 文件或视图;
  • 业务表和关联关系是真实 SQL 结构;
  • 新增可统计字段需要物理列或索引扩展;
  • 不为了页面功能复制业务表。

15.3 简单和一致

  • 同类业务域使用相同目录和调用流程;
  • 不存在同一类配置的多种格式;
  • 不存在同一业务动作的多个通用入口;
  • 数据库迁移和代码可以一起发布;
  • 第一阶段不依赖通用低代码运行时。

16. 旧设计迁移原则

旧设计不是全部废弃,而是按职责迁移:

旧内容 新系统处理方式
真实业务表 保留并按业务域归属整理
业务 SQL 移入对应业务域 sql 目录
wf_* 工作流表 保留,改为绑定业务动作
s_module 降为菜单、权限和对象索引
s_field 降为字段说明,不负责运行时造字段
s_module_schema 页面局部配置,不作为核心运行时
通用保存接口 仅保留简单基础资料使用
JSON 公式和动态规则 迁移到业务代码或 SQL
b_query_sql 等全局 SQL 字段 逐步迁移到业务域 SQL 文件

17. 最终架构关系

业务域代码包是组织中心
SQL 表和视图是事实来源
SQL 文件是查询和处理的主要实现
业务代码负责动作和事务
页面负责交互
工作流负责流程编排
少量配置负责表现和参数

新系统不是旧系统增加一层配置,也不是把旧模块表重新命名。它的核心变化是:

从元数据驱动的通用模块,改成业务域驱动的 SQL 应用。