场景推演.md 7.59 KB

场景推演:一次「新增订单」如何被本体模型驱动执行

本文用 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_ORDERSceneSchemaService跨层合成

  1. 取 M4 场景 SCENE_CREATE_ORDER → 得 flowStepspermissionBind:[order_create]、绑定命令 OrderAggregate.CreateOrder
  2. 取 M8 中 bindSceneId == SCENE_CREATE_ORDER 的模板 template_order_master_detail,逐组件富化
    • M1fieldType(如 totalAmount → decimalquantity → int)→ 前端据此选控件。
    • M5maskedreceiverPhone/contactPhone 打脱敏标记)。
    • 对绑定到某聚合根标识的 selectcustomerId→CustomerAggregateproductId→ProductAggregate)补 optionsSource(下拉数据源端点)。
  3. M2 命令入参/校验、M3 命令 API 地址(/api/v1/order/create)、MetaRule 绑定本场景的规则、以及溯源信息。

前端 SceneForm 拿到后按模板结构渲染:

  • card + aggregate_root主信息卡片(订单主字段)
  • table + aggregate_entity(OrderItem)可增删行的明细表格
  • card + aggregate_entity(PaymentTerm/DeliveryAddressEntity)单行子实体卡片
  • 每个叶子组件按 compTypeFieldControl 注册表映射到 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/executeCommandEngine.execute 逐 M4 flowSteps 解释

flowStep(M4) 引擎动作 依据层
permission(隐式) order_createallowPrincipals 是否含当前主体 M5
readOnlyCheck: CustomerAggregate 校验 master.customerId 存在于客户表 M4 + M1 + M3
bindCommand: CreateOrder 见下方"命令执行" M2
return 汇总结果返回 M4

命令执行(CreateOrder)内部

  1. 校验:必填项来自 M8 required 标记(与前端同源);收货手机号格式dueDays>=0 等来自 M2 validations 文本的解释。
  2. 风控(MetaRule / Aviator)
    • 订单级:以 totalAmount + 客户 customerLevel(查客户表得)评估 R_ORDER_AMOUNT_LIMITtotalAmount > #{single_order_max_amount} && customerLevel != "VIP" → 命中则 REJECT
    • 逐商品级:以每个商品 stockNum 评估 R_STOCK_LOW_WARNstockNum < #{stock_warn_min}ALERT(不阻断)。
  3. 落库(M3):生成 orderId;据 t_order_mainfieldMap 写主单;据组合子实体(M1 relations)逐个写 t_order_item / t_order_payment_term / t_order_address,并补父外键 order_id列名、表名全部来自 M3
  4. 发事件(ME/Outbox):据 M2 emitEventsOrderCreated / OrderItemAdded / ...t_domain_outbox;topic 取自 ME(无则按命名派生)。
  5. 跨聚合最终一致(ME):匹配 crossAggConsistencyRulesOrderCreated → 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_currencycompType: 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 底座;二者解耦。