场景推演:一次「新增订单」如何被本体模型驱动执行
本文用 SCENE_CREATE_ORDER 这一具体场景,逐步推演"架构引擎如何解释与执行模型",说明整体运行原理。
0. 一图看懂
models/*.yaml (唯一事实源)
┌───────────────────────────────────────────────────────────┐
│ M0 校验 M1 领域 M2 命令 ME 事件 M3 部署 M4 场景 │
│ M5 安全 M6 监控 M7 SLA MetaRule 规则 M8 前端模板 │
└───────────────────────────────────────────────────────────┘
│(启动/热重载加载) │(GET /api/meta/scene)
▼ ▼
┌───────────────────────┐ ┌──────────────────────────┐
│ 后端 解释引擎(Java) │◀──REST──│ 前端 渲染引擎(React) │
│ ModelRepository │ │ SceneForm/FieldControl │
│ CommandEngine (通用) │ │ 读 M8 动态生成「新增订单」│
└───────────────────────┘ └──────────────────────────┘
关键:没有 OrderController、没有 OrderService.createOrder() 这类专用代码。CommandEngine 对任何场景都走同一套解释流程;SceneForm 对任何模板都走同一套渲染流程。
1. 页面从哪来:M8 → 合成 Schema → React 渲染
前端请求 GET /api/meta/scene/SCENE_CREATE_ORDER。SceneSchemaService 做跨层合成:
- 取 M4 场景
SCENE_CREATE_ORDER→ 得flowSteps、permissionBind:[order_create]、绑定命令OrderAggregate.CreateOrder。 - 取 M8 中
bindSceneId == SCENE_CREATE_ORDER的模板template_order_master_detail,逐组件富化:- 用 M1 补
fieldType(如totalAmount → decimal、quantity → int)→ 前端据此选控件。 - 用 M5 补
masked(receiverPhone/contactPhone打脱敏标记)。 - 对绑定到某聚合根标识的
select(customerId→CustomerAggregate、productId→ProductAggregate)补optionsSource(下拉数据源端点)。
- 用 M1 补
- 附 M2 命令入参/校验、M3 命令 API 地址(
/api/v1/order/create)、MetaRule 绑定本场景的规则、以及溯源信息。
前端 SceneForm 拿到后按模板结构渲染:
-
card+aggregate_root→ 主信息卡片(订单主字段) -
table+aggregate_entity(OrderItem)→ 可增删行的明细表格 -
card+aggregate_entity(PaymentTerm/DeliveryAddressEntity)→ 单行子实体卡片 - 每个叶子组件按
compType经FieldControl注册表映射到 antd 控件。
因此:改 M8 的
compType/formLabel/span/childComponents→ 页面即变;这就是"仅维护模板、不改前端源码"。
2. 提交做什么:payload → 通用命令执行
用户填完点「提交」,前端按 M8 绑定把值组装为领域结构:
{
"master": { "customerId":"C002", "totalCurrency":"CNY", "totalAmount":7997, "createTime":"..." },
"details": {
"OrderItem": [ { "productId":"P001", "quantity":1, "itemPrice":6999, "itemCurrency":"CNY" } ],
"PaymentTerm": [ { "paymentType":"月结", "dueDays":30 } ],
"DeliveryAddressEntity": [ { "province":"广东", ... , "receiverPhone":"13800002222" } ]
}
}
POST 到 /api/scene/SCENE_CREATE_ORDER/execute。CommandEngine.execute 逐 M4 flowSteps 解释:
| flowStep(M4) | 引擎动作 | 依据层 |
|---|---|---|
permission(隐式) |
order_create 的 allowPrincipals 是否含当前主体 |
M5 |
readOnlyCheck: CustomerAggregate |
校验 master.customerId 存在于客户表 |
M4 + M1 + M3 |
bindCommand: CreateOrder |
见下方"命令执行" | M2 |
return |
汇总结果返回 | M4 |
命令执行(CreateOrder)内部:
-
校验:必填项来自 M8
required标记(与前端同源);收货手机号格式、dueDays>=0等来自 M2validations文本的解释。 -
风控(MetaRule / Aviator):
- 订单级:以
totalAmount+ 客户customerLevel(查客户表得)评估R_ORDER_AMOUNT_LIMIT:totalAmount > #{single_order_max_amount} && customerLevel != "VIP"→ 命中则 REJECT。 - 逐商品级:以每个商品
stockNum评估R_STOCK_LOW_WARN:stockNum < #{stock_warn_min}→ ALERT(不阻断)。
- 订单级:以
-
落库(M3):生成
orderId;据t_order_main的fieldMap写主单;据组合子实体(M1 relations)逐个写t_order_item / t_order_payment_term / t_order_address,并补父外键order_id。列名、表名全部来自 M3。 -
发事件(ME/Outbox):据 M2
emitEvents写OrderCreated / OrderItemAdded / ...到t_domain_outbox;topic 取自 ME(无则按命名派生)。 -
跨聚合最终一致(ME):匹配
crossAggConsistencyRules中OrderCreated → ProductAggregate.StockDeduct,逐商品扣减库存(库存列同样取自 M3)。
页面右侧「执行追踪」会显示真实的逐步日志,例如:
M5 permission — 主体 normal_user 通过权限校验 [order_create]
M4 readOnlyCheck — 校验 CustomerAggregate 引用存在:通过
M2/M8 validate — 命令 CreateOrder 入参校验通过
MetaRule metaRule — 风控规则评估完成,命中 0 条;REJECT=0,ALERT=0
M3 persist — 写入 Order(O3EQD8P2591) 及 3 条子实体,表结构源自 M3
ME emitEvents — 写入 Outbox 事件 4 条
ME crossConsistency — 触发 StockDeduct 扣减 1 个商品库存
M4 return — 场景执行完成,返回结果
3. 推演三条"改模型即改行为"
(a) 大额拦截:改 MetaRule
把 single_order_max_amount: 50000 → 100,热重载。同一笔 普通用户 7997 元订单:
- 改前:
7997 > 50000为假 → 通过; - 改后:
7997 > 100 && level!="VIP"为真 → REJECT,返回普通用户单笔订单不可超过50000元...。 > 规则只维护一份;前端「提交前预检」调用同一后端 MetaRule,实时给出同样结论。
(b) 表单改版:改 M8
把 input_currency 的 compType: input → select、改 formLabel,热重载。「新增订单」页的"结算币种"从文本框变为下拉、标签同步更新——未动任何 React 代码。
(c) 字段类型:改 M1
把某字段 type: string → int,热重载。合成 Schema 的 fieldType 变化 → 前端控件从 Input 变为 InputNumber;如需落库再在 M3 增列,SchemaInitializer 于重载时 ALTER TABLE ADD COLUMN 自动补列。
4. 为什么这套架构成立
- 单一事实源:字段口径(M1)被前端组件、后端校验、DDL、事件负载共同引用,天然对齐,杜绝前后端不一致。
- 解释而非生成:引擎在运行时解释模型,故"热重载即变",无需重新编译/发版。
- 通用而非专用:新增一个业务场景 = 新增一套 M1/M2/M3/M4/M8 描述 + 绑定 MetaRule,无需新增 Controller/Service/React 页面。
- 关注点分层:业务专家维护 M8 模板与 MetaRule 规则;架构师维护 M0–M7 底座;二者解耦。