LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)

2,493 阅读5分钟

背景:我之前写过一篇文章:《Liteflow逻辑编排可视化设计》,分享了我们对LiteFlow这个规则引擎的逻辑可视化设计,而我们这个可视化项目的目标不仅仅只是实现LiteFlow的逻辑可视化,而是实现LiteFlow的逻辑可视化编排。

接下来,我将通过系列文章的形式,进行LiteFlow逻辑可视化编排设计与实现的开发经验分享。

LiteFlow ContextPad.gif

《LiteFlow逻辑可视化编排设计与实现》会以系列文章的形式,有步骤、有重点地分享我们对LiteFlow逻辑可视化编排的实现,目前该系列文章有如下5篇:

  1. LiteFlow逻辑可视化编排设计与实现 01-先导篇
  2. LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)
  3. LiteFlow逻辑可视化编排设计与实现 03-视图呈现篇(View)
  4. LiteFlow逻辑可视化编排设计与实现 04-操作逻辑篇(Control)
  5. LiteFlow逻辑可视化编排设计与实现 05-后记篇(前端代码已开源,文末有仓库地址)

以下是《LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)》的文章正文。

02-数据模型篇(Model)

回顾一下我们在《先导篇》中提到过的内容,作为一名前端开发,我们需要特别关注的要素有三个——数据(Model)、视图(View)和逻辑(Control),即“MVC”——我接下来也是使用“MVC三要素”的知识框架来进行LiteFlow逻辑可视化编排系统的拆解、组合、设计和实现的:

LiteFlow逻辑可视化编排设计与实现.png

而“数据”,或者“模型”,或者“数据模型”,或者“概念模型”,是我们开发之前需要最先分析和设计的部分,而具体到对LiteFlow进行逻辑可视化编排的模型设计上,我们的目标是将EL表达式的操作符(Operator)进行建模,最终我们将EL表达式建模成了由ELNode组成的一棵树:

LiteFlow逻辑可视化编排设计与实现 1. 数据模型(Model).png

1、EL表达式

LiteFlow的逻辑编排,都是通过EL表达式来实现,比如一个EL表达式的例子:

<chain name="chain1">
    THEN(
        a,
        WHEN(b, c, d),
        e
    );
</chain>

LiteFlow逻辑可视化编排-EL表达式.png

其中“THEN”和“WHEN”是EL表达式的关键字,分别表示串行编排和并行编排,而“a”“b”“c”“d”“e”则是5个逻辑组件,由此组成了一个串行和并行编排的组合——即先执行“a”组件,然后并行执行“b”“c”“d”组件,最后执行“e”组件。

LiteFlow对可执行的逻辑流程进行建模,主要包括以下2个部分:

Liteflow流程建模:逻辑组件 + 逻辑编排.png

  • 1、逻辑组件(组件节点):逻辑组件类型包括:
    ① 顺序组件:用于THEN、WHEN;
    ② 分支组件:用于SWITCH、IF ...;
    ③ 循环组件:用于FOR、WHILE ...。

  • 2、逻辑编排:通过EL表达式进行组件编排:
    ① 串行编排:THEN;
    ② 并行编排:WHEN;
    ③ 选择编排:SWITCH;
    ④ 条件编排:IF;
    ⑤ 循环编排:FOR、WHILE等等。

在这个项目里,我们的首要任务就是将EL表达式的操作符(Operator)进行建模,最终我们将EL表达式建模成了由ELNode组成的一棵树。

2、树形结构:

LiteFlow的EL表达式进行文本拆解,我们其实能得到一个树形结构:

LiteFlow逻辑可视化编排-EL表达式 vs AST语法树.png

上面的树形结构来自于AST抽象语法树,使用AST explorer解析的完整的AST语法树如下所示:

{
  "type": "Program",
  "start": 0,
  "end": 34,
  "body": [
    {
      "type": "ExpressionStatement",
      "start": 0,
      "end": 34,
      "expression": {
        "type": "CallExpression",
        "start": 0,
        "end": 33,
        "callee": {
          "type": "Identifier",
          "start": 0,
          "end": 4,
          "name": "THEN"
        },
        "arguments": [
          {
            "type": "Identifier",
            "start": 8,
            "end": 9,
            "name": "a"
          },
          {
            "type": "CallExpression",
            "start": 13,
            "end": 26,
            "callee": {
              "type": "Identifier",
              "start": 13,
              "end": 17,
              "name": "WHEN"
            },
            "arguments": [
              {
                "type": "Identifier",
                "start": 18,
                "end": 19,
                "name": "b"
              },
              {
                "type": "Identifier",
                "start": 21,
                "end": 22,
                "name": "c"
              },
              {
                "type": "Identifier",
                "start": 24,
                "end": 25,
                "name": "d"
              }
            ],
            "optional": false
          },
          {
            "type": "Identifier",
            "start": 30,
            "end": 31,
            "name": "e"
          }
        ],
        "optional": false
      }
    }
  ],
  "sourceType": "module"
}

3、JSON表示

我们可以把这棵AST抽象语法树进行简化,得到一个简化版的JSON表示:

LiteFlow逻辑可视化编排-AST语法树 vs JSON表示.png

{
  type: "THEN",
  children: [
    { type: "NodeComponent", id: "a" },
    {
      type: "WHEN",
      children: [
        { type: "NodeComponent", id: "b" },
        { type: "NodeComponent", id: "c" },
        { type: "NodeComponent", id: "d" },
      ],
    },
    { type: "NodeComponent", id: "e" },
  ]
}

上面这个JSON数据表示,就是我们的目标格式,也是我们打算前后端进行数据交换的标准格式。

4、建立模型

LiteFlow逻辑可视化编排设计与实现 1. 数据模型(Model).png

经过以上步骤的分析,我们可以建立这么一个ELNode模型:

/**
 * EL表达式的模型表示:数据结构本质上是一个树形结构。
 * 例如一个串行编排(THEN):
 * (1) EL表达式形式:THEN(a, b, c, d)
 * (2) JSON表示形式:
 * {
    type: ConditionTypeEnum.THEN,
    children: [
      { type: NodeTypeEnum.COMMON, id: 'a' },
      { type: NodeTypeEnum.COMMON, id: 'b' },
      { type: NodeTypeEnum.COMMON, id: 'c' },
      { type: NodeTypeEnum.COMMON, id: 'd' },
    ],
  }
 * (3) 通过ELNode节点模型表示为:
                                          ┌─────────────────┐
                                      ┌──▶│  NodeOperator   │
                                      │   └─────────────────┘
                                      │   ┌─────────────────┐
                                      ├──▶│  NodeOperator   │
  ┌─────────┐    ┌─────────────────┐  │   └─────────────────┘
  │  Chain  │───▶│  ThenOperator   │──┤   ┌─────────────────┐
  └─────────┘    └─────────────────┘  ├──▶│  NodeOperator   │
                                      │   └─────────────────┘
                                      │   ┌─────────────────┐
                                      └──▶│  NodeOperator   │
                                          └─────────────────┘
 */
export default abstract class ELNode {
  // 节点类型:可以是编排类型,也可以是组件类型
  public type: ConditionTypeEnum | NodeTypeEnum;
  // 当前节点的子节点:编排类型有子节点,组件类型没有子节点
  public children?: ELNode[];
  // 当前节点的父节点
  public parent?: ELNode;
  // 判断类节点类型:主要用于SWITCH/IF/FOR/WHILE等编排类型
  public condition?: ELNode;
  // 组件节点的id
  public id?: string;
  // 编排节点的属性:可以设置id/tag等等
  public properties?: Properties;
}

LiteFlow逻辑可视化编排-ELNode模型-组合关系vs继承关系.png

对于我们建立的ELNode模型,关键点有以下2个:

  1. 组合关系:一个EL表达式,最终是由ELNode组成的一棵树;
  • 这棵树的根节点被我们定义为Chain,其他EL操作符(比如THEN/WHEN/SWITCH等关键字,也包括逻辑组件)都是树上的子节点;
  • 需要注意的是,逻辑组件(NodeComponent)也被当做一种操作符(Operator),而且是这棵树的叶子结点。
  1. 继承关系:所有EL操作符(比如THEN/WHEN/SWITCH等),包括逻辑组件(NodeComponent),都继承自ELNode,同时有自己的特有属性和方法实现。
/**
 * EL表达式各个操作符模型,继承关系为:
                      ┌─────────────────┐
                  ┌──▶│  ThenOperator   │
                  │   └─────────────────┘
                  │   ┌─────────────────┐
                  ├──▶│  WhenOperator   │
                  │   └─────────────────┘
                  │   ┌─────────────────┐
                  ├──▶│  SwitchOperator │
                  │   └─────────────────┘
  ┌──────────┐    │   ┌─────────────────┐
  │  ELNode  │────┼──▶│  IfOperator     │
  └──────────┘    │   └─────────────────┘
                  │   ┌─────────────────┐
                  ├──▶│  ForOperator    │
                  │   └─────────────────┘
                  │   ┌─────────────────┐
                  ├──▶│  WhileOperator  │
                  │   └─────────────────┘
                  │   ┌─────────────────┐
                  └──▶│  NodeOperator   │
                      └─────────────────┘
 */
// 1. 顺序类
export { default as ThenOperator } from './then-operator';
export { default as WhenOperator } from './when-operator';
// 2. 分支类
export { default as SwitchOperator } from './switch-operator';
export { default as IfOperator } from './if-operator';
// 3. 循环类
export { default as ForOperator } from './for-operator';
export { default as WhileOperator } from './while-operator';
// 4. 节点类
export { default as NodeOperator } from './node-operator';

以上是我们《LiteFlow逻辑可视化编排设计与实现 02-数据模型篇(Model)》的内容,我们完成了EL表达式的操作符(Operator)的建模,最终我们将EL表达式建模成了由ELNode组成的一棵树。