低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计

本文主要是介绍低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

写最后一篇文章的时候,我本人其实犹豫了半年,在想是否发布出这篇文章,因为可能会动了很多人的利益。所以这篇文章既是整个低代码信创开发的高度总结,也是最为精华的一部分,它点明了低代码中最为核心的技术。虽然你在读这篇文章的时候会有犹抱琵琶半遮面的感觉,但当你领悟之后,会发现原来低代码开发平台的建设是如此的简单。低代码前端设计模型,而设计出来的模型以元数据的方式又能驱动整个系统运行,读完你会发现实在是非常巧妙的设计。一旦拥有了这个设计思想下建立的系统,一个程序员一天做出一套管理系统不再是梦,而且运维和调整将变得极为简单、实时,管理系统将变得极为廉价,所以恕我不能详述具体实现的代码。信息化对于实现中华民族伟大复兴的梦想尤为重要,所以此文章送给有缘人,为祖国的各行各业完成赋能的使命。

前两篇文章

低代码信创开发核心技术(一):基于Vue.js的描述依赖渲染DDR实现模型驱动的组件
低代码信创开发核心技术(二):手撕灵活好用的Vue拖拉拽布局系统

数据库层设计

自顶向下出发来判定设计,我们已经有了界面和组件,接下来就是需要通过组件来访问元数据层,元数据层再调用数据库层完成与数据库的交互。

1、从API角度入手

实现链式增删改查API编程,关键点是在每一次函数执行后返回当前的对象,而不是返回结果。争取让程序员使用的时候只需一个.就能通过IDE提示出来。大大降低二次开发的难度。
链式编程的封装,是指将API设计成一个Builder,直到最后query或update时才会产生结果,这样程序员的记忆负担最少,所有的功能都是一路“点”出来的。

2、SQL的产生

一个平台需要能支持多种数据源,所以需要建立方言机制,简单的做法是写定一个IDialect接口,用工厂模式根据不同传入的JDBC Driver类名判断使用哪个方言。
有了自动化的方言工具,我们就可以实时生成SQL了,其实说来也比较简单,常用SQL的生成无非是Where子句、OrderBy子句、LeftJoin的组合,用于检索、排序、带出关联表名称等等。
尽管可以实时生成SQL,但也会有非常复杂的SQL等待我们去手写,所以API仍要留有query和update相关方法,而且为了安全性也要强制让传递进来的参数进行预编译处理。
增删改其实非常容易实现,但查询是最难的,为了解决复杂度,则需要构建一个查询上下文,因此我们在各个环节传递这个上下文对象并补充它的各种条件,即可无视顺序、无视复杂度、无视前置各种函数的调用关系,在最终进行查询的环节才按顺序逻辑构建SQL,这样最为简单,调整起来也只需修改查询上下文的类即可,所以为了方便,建议把增删改的相关条件也融入到上下文中,还有分页条件等等,需要抽象的东西并不多,但这样做可以做到统一。

3、SQL的执行

SQL被创造出来之后,即可进入执行环节,需要注意的是如果插入的数据较多,则需要用values后面跟多个数据的语法来实现批量插入、update则需要用case when语句来实现批量更新。这样可以大大节约执行的时间。
而且SQL执行层应当直接作用于JDBC之上,因为我们假定项目里并没有Hibernate、JPA、MyBatis,纯手动造一个轮子。这样带来的好处是我们可以灵活的控制分布式事务、设置保存点、增加切面,而无需受制于其他框架的规则。

4、关于数据格式

查询的结果集一定是先转换成List<Map<String,Object>>格式,但是该类型的结果作为返回值无法进行方便的后续运算,所以需要在它基础上封装一层查询结果对象,包括我们可以把它转换成ValueObject的List(实际上低代码或无代码平台基本不需要用户自己定义ValueObject)、直接拿到唯一数据、抽取所有Map里同一个属性形成新的List,或者为了构建缓存、加速运算从而抽取出主键和Map之间对应的关系等算法。
同理,增删改操作也是操作同样的数据结构。

5、插件

我们可以定义一个统一的事件处理接口和事件处理对象,事件处理对象包含事件名称、查询上下文、是否打断执行即可。然后建立一个全局事件处理器,把事件处理的实现类可以动态注册或移除。这样我们就可以做到包括给数据自动创建主键、自动填写审计信息(创建人、创建时间、修改人、修改时间)等功能。

6、查询条件容错机制

一个好的系统一定是会允许容错的,比如我们传入的列名是驼峰式,它可以自动转换成小写字母加下划线。所以写一个过滤器,当查询上下文真正要执行前,对所传入的条件进行自动的修整。

7、数据库元数据

数据库元数据可以通过Connection对象获取,包括了有哪些表、每个表有哪些字段、什么类型的、是否是主键等信息,一旦获取到了,我们可以把它用Caffeine之类的框架放到内存中缓存起来,这样便可以动态获知是否是主键。

8、事务处理

最终事务处理我们要写一个单独的项目和Spring进行结合,比较方便的做法是定义一个Bean,返回PlatformTransactionManager类型,而我们的实现类则继承AbstractPlatformTransactionManager即可。
还需要注意,把LocalDateTime、BigDecimal等格式的前后端转换问题一并解决了,同样也是定义一个Bean,返回Jackson2ObjectMapperBuilderCustomizer对象即可。

9、动态物理表的修改

我们通过第7条已经创建了数据库的元数据,但它的setter一定是protected的,不允许其他包的类对其进行任何的修改,所以我们要创建一个副本类,可以允许修改的那种。然后写一个数据表管理器,当我们对这个副本类的实例进行修改后,把整个定义传送给数据表管理器,应当能实现自动创建表、增加字段、增加索引的功能,同样的这些SQL也应当由方言插件负责完成拼装。

至此我们数据库层就完成了建设。

元数据层

我们常常在应用系统中要让系统能对自己的结构进行认知,并且要把表与表之间的外键关系、主子关系放到一个地方,在需要使用的时候从这个地方拿出关系既可自动完成一些操作,这个地方就叫做元数据层,非常类似于Java的反射机制,如果你了解了Java的反射,那么元数据层的作用就不言而喻。说到底,元数据是一种数据驱动的架构设计,实际使用时可以用一些通用的算法来处理不同的数据结构。当我们有了数据库层,就可以建立元数据层了。

1、结构定义

实体或者聚合实体只需要自身带编码、数据、子实体集合列表即可,那么我们在数据库中建立的元数据表应当也是id、code、name、pid、物理表名的格式,它是一种树状结构。随之还需要建立元数据字段表,id,code,name,物理字段名
聚合实体我们可以想象成是一个实体下面还挂有若干种实体的样子。比如入职申请表,个人基本信息完全可以放在主实体表中,而学习经历、工作经历因为和主表是1对多关系,所以学习经历、工作经历单独各是一张表,他们与主表“入职申请表”的关系就应当记录在元数据表中,当查询的时候把数据放入子实体集合列表里。
也因此,子实体集合列表实际上只需携带编码、List<数据>、分页信息即可。说是子实体集合列表,但实际上多个主实体也可以打包在一个子实体集合列表对象中。这样我们就可以形成一个无限嵌套的数据结构,做出超级复杂的业务数据结构,保障了主表和子表之间的数据一致性。
我们在实际操作元数据的时候永远操作的都是编码,读取的时候是多表按顺序自动读取并拼装,写入的时候也是视作整体。

2、执行层和事件动作

因为元数据层需要事件驱动,所以我们可以把事件简单的划分为保存、删除、查询。事件驱动实现原理非常简单,本文不做赘述。讲一下执行层究竟要做哪些事情。第一,要把数据库层的API抽离处理,比如做个QueryBuilder之类的,它可以完成对主实体(主表)的各种复杂查询。第二,需要通过数据库层的插件机制来完成主键生成、查询融入(因为数据库层有上下文,所以很容易剥离上下文并融入数据权限功能,比如未来按组织权限划分能看到的数据,实现组织 IN (组织主键清单)这类操作)。
另外在执行更新的时候还需特意注意审计信息中的最后修改时间是否有动过,为了保证多人同时修改时数据一致性,则需要将之前的数据读入,并对比时间戳,确保可以修改并给予新的时间戳,否则就应当提示用户数据已被他人修改。带入旧数据也会带来一些好处,比如我们可以在前端对子实体进行删除时,我们可以准确的得知哪些数据被删除了,从而执行删除校验,避免关键数据被删除。

3、关联定义

如果只有基本的结构定义,没有关联定义,这个系统就只能做简单的增删改查了。关联定义我们可以遵循2NF范式,将表与表之间的一对多关系存储在数据表里,到使用的时候(比如订单明细的商品ID自动翻译成商品名称)就非常有用了。
因此在查询完成后在事件驱动的查询After切面到达时,我们可以遍历主实体的树形结构,把关联到其他表的主键或者编码暂存到一起,然后批量发起查询,以字段名$name字段名$code为键保存到返回的聚合实体中。

4、实体服务和控制器基类的封装

我们有了以上内容即可完成Service和Controller的封装,善于运用@PathVariable和@RequestBody可以让你事半功倍。需要注意的是我们这里要且仅要一个主入口,后续要根据操作来分发到不同的处理类上进行处理,这样我们可以将平台框架封装起来并用spring.factories机制来实现自动注入,这样在程序员眼里看来项目就一点都不复杂了,依赖你的项目就可以获得全部元数据能力。

5、联动结构

接下来需要有一个联动结构,从前台传递元数据的定义,然后联动把元数据表、元数据字段表的内容填充进去。然后再根据元数据的定义和表和表之间关联的结构,把数据表建立起来,这就需要调用数据库层的物理表修改功能了。

初步实现低代码开发

接下来我们根据前两篇文章讲述前端的技术基础,把项目面板、属性面板搭建出来,然后在属性面板里我们可以做一个弹窗,针对某一个组件创建元数据或选择已有的元数据,弹窗界面里一开始有一个输入框,询问这里需要有哪些字段(只需要给中文名),然后预先写好prompt调用AI,把字段英文名、类型返回回来让用户确认,如果用户觉得不对,可以修改成想要的,点击保存即可自动联动创建表(如果表中没有数据,想删也是可以删的),这种程度的业务逻辑相比于非常抽象的元数据还是非常容易实现的。
后续使用已经有的元数据就没这么麻烦了,连输入框都不需要,只需要把已经有的元数据选中,勾选要展示的字段,点确定即可
最后运行时环境把设计器代码隐藏掉,只需要渲染即可。至此低代码平台的雏形就有了。

这篇关于低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/390625

相关文章

使用Java实现Navicat密码的加密与解密的代码解析

《使用Java实现Navicat密码的加密与解密的代码解析》:本文主要介绍使用Java实现Navicat密码的加密与解密,通过本文,我们了解了如何利用Java语言实现对Navicat保存的数据库密... 目录一、背景介绍二、环境准备三、代码解析四、核心代码展示五、总结在日常开发过程中,我们有时需要处理各种软

Java List排序实例代码详解

《JavaList排序实例代码详解》:本文主要介绍JavaList排序的相关资料,Java排序方法包括自然排序、自定义排序、Lambda简化及多条件排序,实现灵活且代码简洁,文中通过代码介绍的... 目录一、自然排序二、自定义排序规则三、使用 Lambda 表达式简化 Comparator四、多条件排序五、

Java 压缩包解压实现代码

《Java压缩包解压实现代码》Java标准库(JavaSE)提供了对ZIP格式的原生支持,通过java.util.zip包中的类来实现压缩和解压功能,本文将重点介绍如何使用Java来解压ZIP或RA... 目录一、解压压缩包1.zip解压代码实现:2.rar解压代码实现:3.调用解压方法:二、注意事项三、总

Linux实现简易版Shell的代码详解

《Linux实现简易版Shell的代码详解》本篇文章,我们将一起踏上一段有趣的旅程,仿照CentOS–Bash的工作流程,实现一个功能虽然简单,但足以让你深刻理解Shell工作原理的迷你Sh... 目录一、程序流程分析二、代码实现1. 打印命令行提示符2. 获取用户输入的命令行3. 命令行解析4. 执行命令

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

利用Python实现可回滚方案的示例代码

《利用Python实现可回滚方案的示例代码》很多项目翻车不是因为不会做,而是走错了方向却没法回头,技术选型失败的风险我们都清楚,但真正能提前规划“回滚方案”的人不多,本文从实际项目出发,教你如何用Py... 目录描述题解答案(核心思路)题解代码分析第一步:抽象缓存接口第二步:实现两个版本第三步:根据 Fea

Java计算经纬度距离的示例代码

《Java计算经纬度距离的示例代码》在Java中计算两个经纬度之间的距离,可以使用多种方法(代码示例均返回米为单位),文中整理了常用的5种方法,感兴趣的小伙伴可以了解一下... 目录1. Haversine公式(中等精度,推荐通用场景)2. 球面余弦定理(简单但精度较低)3. Vincenty公式(高精度,

QT6中绘制UI的两种方法详解与示例代码

《QT6中绘制UI的两种方法详解与示例代码》Qt6提供了两种主要的UI绘制技术:​​QML(QtMeta-ObjectLanguage)​​和​​C++Widgets​​,这两种技术各有优势,适用于不... 目录一、QML 技术详解1.1 QML 简介1.2 QML 的核心概念1.3 QML 示例:简单按钮

Java进行日期解析与格式化的实现代码

《Java进行日期解析与格式化的实现代码》使用Java搭配ApacheCommonsLang3和Natty库,可以实现灵活高效的日期解析与格式化,本文将通过相关示例为大家讲讲具体的实践操作,需要的可以... 目录一、背景二、依赖介绍1. Apache Commons Lang32. Natty三、核心实现代