transflow:quake是如何构建以dsl为核心的低代码系统?(代码片段)

Phodal Phodal     2022-10-22     119

关键词:

自我们发布了 Quake 项目之后,我们一直在做各种 Meta 功能。在上次 release 时,可以进行自定义的数据编辑。在这个新的版本里,将会包含一个新的数据可视化特性:Transflow。

从类型流(TypeFlow)说起

Transflow 的设计起源需要从我司的大佬 @魔头 提出了类型流的理念,并在那篇《类型流(TypeFlow)——世俗化的函数式编程和改进的过程式设计》中进行了详细的介绍。Typeflow 有几个主要的规则:

  1. 从可视化模型上就可以看出:共存在4个待实现的函数,其中两个纯函数,参数校验和返回结果包装;一个副作用函数,保存代办事项;还有一个输入端口,即把这个几个函数编排起来完成业务的程序入口。

  2. 每个函数有明确的输入输出类型

  3. 函数之间通过匹配的输入输出类型连接起来。

  4. 输入输出类型使用业务人员能够理解的业务概念,从而符合 DDD 的要求。

  5. 可视化

简单来说,在有了设计之后,我们可以为功能生成对应的有输入和输出的函数,并可以通过规则将它们匹配起来。原理,就是这么简单。

Transflow 初识:DSL 生成代码

回到 Quake 的场景里,我们有固定的数据源,即不同的 entry。与此同时我们还有不同的用于展示这些数据的组件。我们所需要做的便是,提供一个从数据流向组件的规则,即 Transflow DSL。

Transflow 示例

举个例子,当我们想有一个 calendar 来展示所有的 todo 和 blog 时,我们就需要从数据源中取得 todo 和 blog,对数据进行转换然后传输给 calendar 组件。用一句话来表达便是:

from('todo','blog').to(<quake-calendar>);

这个 Transflow 的 DSL,最简模式下(即没有函数名、不添加数据映射(mapping)和过滤器(filter)、组件定义的情况下),它可以生成以下的 JavaScript 代码:

function from_todo_blog_to_quake_calendar(todos, blogs) 
  let results = [];
  results = results.concat(todos);
  results = results.concat(blogs);
return results;

const tl_temp_1 = async (context, commands) => 
  const el = document.createElement('quake-calendar');
  let todos = await Quake.query('todo');
  let blogs = await Quake.query('blog');
  let data = from_todo_blog_to_quake_calendar(todos, blogs);
  el.setAttribute('data', JSON.stringify(data));
  return el;

Quake.router.addRoutes(path: '/transflow/show_temp_1', action: tl_temp_1 ,)

代码逻辑上是:

  1. 创建一个新的 Quake Calendar 组件(Web Component)

  2. 获取 todo、blog 相关的数据

  3. 执行对应的数据转换规则

  4. 将数据传递给组件

  5. 添加可访问的路由

这样一来,只需要跳转到相应的路由即可。如果需要的话,也可以直接生成临时的组件。另外一部分,则是由 Web Components 所构建的组件体系,我们将会在另外一篇文章中展开介绍。

在生成了代码之后,开发人员可以基于生成的代码,来进行业务逻辑填空。

多条 Transflow

(PS:虽然尚未进行测试,但是我相信它当前是 work 的)

如果有多条 Transflow 规则时:

transflow show_calendar 
  from('todo','blog').to('record'),
  from('record').to(<quake-calendar>);

就会生成多个 Transflow 函数(部分):

function from_todo_blog_to_record(todos, blogs) 
  let results = [];
  results = results.concat(todos);
  results = results.concat(blogs);
  return results;

function from_record_to_quake_calendar(records) 
  let results = [];
  results = results.concat(records);
  return results;

Transflow 生成的代码,面临的最大问题是数据量大时的性能问题,但是 Quake 的场景下,不会有这样的问题。

目标函数式的 Transflow

既然,我们是对数据流进行操作,那么理想情况下,Transflow 的 DSL 就可以设计为向函数式靠齐。不过,当前,我们还没有理由实现这么复杂的功能,可以在后续展开。

一个实现一点点的 map

上述的 from('todo','blog').to(<quake-calendar>); 会在转化时生成特定的数据结构。因此,也可以直接从数据结构中读取对应的 Transflow,对它们进行存储:

- name: "from_todo_blog_to_quake_calendar_timeline"
from: [ "todo", "blog" ]
to: "<quake-calendar-timeline>"
map:
- entry: "todo"
source: ["title", "content", "start_time", "updated_date"]
target: ["title", "content", "created_date", "updated_date"]
- entry: "blog"
source: ["title", "content", "start_time", "updated_date"]
target: ["title", "content", "created_date", "updated_date"]

这里的 map 是一个尚未在 DSL 设计的功能,也需要进一步验证是否真的需要。除此,这个 YAML 的设计也是有问题的。

还有,一个刚可用的 filter

在 filter 方面,我做了一些简化设计(~~偷懒~~),因为需要的是搜索引擎,可以可以直接使用搜索引擎的 fliter 功能。在评估了多个 filter-parser 的库之后,我发现没有理由在当前做这么复杂的设计。所以,针对于一些特别的过滤条件做了一些特别的处理。

如下是一个过滤时间的表达式:

from('todo','blog').to(<quake-calendar>).filter('created_date > 2021.01.01 AND created_date < 2021.12.31')

由于搜索引擎并不支持各种各样的时间处理,所以我们可以替换对应的字符器,然后:

created_date > 1609459200 AND created_date < 1640908800

等时机成熟,再完成整体的 filter 规则设计。

下一步:更简单的 Transflow

还在设计中,预期可能会有组件中的编排等。不过,首先我们得需要有足够的 Web Components 组件,才能完成基本的功能开发,并收集这些数据场景。诸如于:

    • Todo 应用

    • Kanban 应用

    • Typeform 编辑器

    • 白板

    • ……

双向绑定的中间组件:ComponentFlow

理想的情况下,我们应该在 Transflow 中生成的是一个新的 WebComponents 组件,以提供数据到组件的通道。只是呢,当前受限于当前的场景有限,所以提供的是简单的代码生成。等组件库进一步完善之后,便可以尝试引入这个新的设计。

面向专业人士的 Transflow

在 Quake 现有的设计里,专业人士可以自由自在的对 Quake 进行定制,所以并不需要高级的 Transflow 存在。如果需要的话,我们可以引入流编辑器。不过,我相信这一天还早着,除非有人愿意来帮填坑。

其它:体验 Transflow

由于当前精力有限,所以暂时没有开发 Quake 的 GUI 版本(如果你有兴趣,欢迎入坑),所以需要按照 https://github.com/phodal/quake 的安装指南进行。

简单的方式是:从 release 页面下载 quake 及 web 部分,然后创建一个新的项目进行体验。

本文使用 Quake 0.2.0 编写。

transflow:quake是如何构建以dsl为核心的低代码系统?(代码片段)

...的版本里,将会包含一个新的数据可视化特性:Transflow。从类型流(TypeFlow)说起Transflow的设计起源需要从我司的大佬@魔头提出了类型流的理念,并在那篇《类型流(TypeFlow)——世俗化的函数式编... 查看详情

如何将 gradle 中的 groovy 任务转换为 Gradle Kotlin DSL 以生成 pom.xml?

】如何将gradle中的groovy任务转换为GradleKotlinDSL以生成pom.xml?【英文标题】:HowcanIconvertagroovytaskingradleintoGradleKotlinDSLtogenerateapom.xml?【发布时间】:2018-07-0821:48:32【问题描述】:以下Gradle脚本的build.gradle.kts版本是什么?applyplugin:\'... 查看详情

dsl的本质:领域构建的半成品

DSL的本质是使用通用和专用语言构建领域的半成品;实际上是构建了一个世界观、小宇宙的半成品;这个半成品包含领域的基本要素、联系方式和基本运行规律;开发者使用这个半成品平台进行开发能达到事半功倍、开发效率大... 查看详情

将 SQL 查询转换为 Elasticsearch dsl 以进行数据可视化

...数据问题想将它们转换为ElasticsearchDSL。我当前的sql查询是"query":"select\\"A\\",\\ 查看详情

androidgradle开发指南

Gradle简介Gradle是一个优秀的构建系统和构建工具,它允许通过插件创建自定义的构建逻辑。它具有如下一些特点:采用了DomainSpecificLanguage(DSL语言)来描述和控制构建逻辑。构建文件基于Groovy,并且允许通过混合声明DSL元素和使用... 查看详情

如何构建一个在线绘图工具:feakin是如何设计与构建的?(代码片段)

高中,读过几本3D图形编程相关的书。怎么说呢,自那以后,图形学相关的东西,都不在我的兴趣范围里了。直到最近,我重新燃起了一点兴趣:架构治理工具ArchGuard依赖于「图即代码」,用于生成架... 查看详情

如何构建一个在线绘图工具:feakin是如何设计与构建的?(代码片段)

高中,读过几本3D图形编程相关的书。怎么说呢,自那以后,图形学相关的东西,都不在我的兴趣范围里了。直到最近,我重新燃起了一点兴趣:架构治理工具ArchGuard依赖于「图即代码」,用于生成架... 查看详情

如何构建一个在线绘图工具:feakin是如何设计与构建的?(代码片段)

高中,读过几本3D图形编程相关的书。怎么说呢,自那以后,图形学相关的东西,都不在我的兴趣范围里了。直到最近,我重新燃起了一点兴趣:架构治理工具ArchGuard依赖于「图即代码」,用于生成架... 查看详情

dsl语法(代码片段)

一:DSL语法FROM(指定基础image)构建指令,必须指定且需要在Dockerfile其他指令的前面。后续的指令都依赖于该指令指定的image。FROM指令指定的基础image可以是官方远程仓库中的,也可以位于本地仓库。FROM命令... 查看详情

如何使用 spring 集成 dsl 解组 xml

】如何使用spring集成dsl解组xml【英文标题】:Howtounmarshallxmlusingspringintegrationdsl【发布时间】:2018-09-1822:37:00【问题描述】:我正在研究弹簧集成dsl。要求是从队列中读取xml消息,基于消息头值,我需要调用不同的服务。我能够... 查看详情

Kotlin DSL 中的 gradle 额外属性是如何设置的?

】KotlinDSL中的gradle额外属性是如何设置的?【英文标题】:HowaregradleextrapropertiessetintheKotlinDSL?【发布时间】:2017-11-1905:24:48【问题描述】:我正在尝试像在groovy中那样组织我的构建文件,方法是将值放在一个单独的文件中以供重... 查看详情

Jenkins job DSL - 每当构建 Ivy 依赖项时构建

...用于创建Ivy工作的DSL脚本。它几乎完成了,除了我找不到如何禁用工作中的BuildwheneveranIvydependencyisbuilt。我到处查看ivyJob元素。有人可以请教吗?【问题讨论 查看详情

在Java中查询类似DSL的机制以进行运行时sql绑定

...L提供类似QueryDSL的功能,但仅适用于运行时SQL绑定。特别是,我只需要一个SQL生成器,这样我就不必为连接、窗口子句等建模......我们需要查询的架构仅在运行时可用 查看详情

如何构建图形数据库以支持三向关系?

】如何构建图形数据库以支持三向关系?【英文标题】:Howtostructureagraphdatabasetosupport3-wayrelationships?【发布时间】:2014-01-1514:52:20【问题描述】:我一直在尝试为我一直在开发的个人web应用程序找出数据库后端。由于我在数据中... 查看详情

quake一个开源的知识管理元框架(代码片段)

...容的功能。这个简单粗糙的页面,让我想起了多年前构建Phodit的场景, itworks 作为开始就足够了。来,先上链接GitHub: https://github.com/phodal/quake缘由半个月前,我在准备一个材料,好不容易从我的博客、Todo、N... 查看详情

如何以安全的方式为其他开发人员构建框架或库? [关闭]

】如何以安全的方式为其他开发人员构建框架或库?[关闭]【英文标题】:Howtobuildaframeworkorlibraryforotherdevelopers,thesecureway?[closed]【发布时间】:2011-05-0304:16:13【问题描述】:我们有一个对任何iOS开发人员都非常有帮助的框架或库... 查看详情

如何使用 Jenkins job dsl 为 gitlab 插件设置秘密令牌?

】如何使用Jenkinsjobdsl为gitlab插件设置秘密令牌?【英文标题】:HowcanIsetthesecrettokenforgitlabpluginwithJenkinsjobdsl?【发布时间】:2018-08-1511:41:14【问题描述】:插件站点的文档似乎有误:https://github.com/jenkinsci/gitlab-plugin来自工作dsl文... 查看详情

如何使用 ivy api 以编程方式为缓存中的模块构建路径?

】如何使用ivyapi以编程方式为缓存中的模块构建路径?【英文标题】:Howtoprogrammaticallybuildapathforamoduleinthecacheusingivyapi?【发布时间】:2015-09-1021:05:01【问题描述】:我想按照ivyofficialdocumentation中的描述以编程方式执行从缓存构建... 查看详情