在当今快速发展的信息技术领域,动态元数据系统扮演着至关重要的角色。它不仅能够提供数据的描述信息,还能动态地适应业务需求的变化,从而提高系统的灵活性和可扩展性。构建一个动态元数据系统意味着我们可以在不重启系统的情况下,对数据模型进行实时更新和维护,而且通过与前端同步传递元数据,可以在不动用开发资源的情况下建立新的界面。
动态元数据系统的核心意义在于其能够提供一种机制,允许系统在运行时自动调整其数据结构,以适应不断变化的数据需求。这种系统对于需要快速迭代和频繁更新数据模型的业务场景尤为重要。结合大模型技术,可以实现自生成底层操作系统、应用界面,是不是更像《流浪地球2》里面550W的设定呢?
为了确保系统的互操作性和可维护性,动态元数据系统的设计和实现应遵循以下标准和规范:
在设计元数据系统时,我们需要构建几个关键的表来存储元数据信息:
基于您提供的表结构,以下是每个表的数据字典:
当然,以下是整理好的表格格式的数据字典:
字段名 | 描述 |
---|---|
主键 | 实体的唯一标识 |
编码 | 实体的编码 |
名称 | 实体的名称 |
物理表 | 实体对应的物理表名 |
上级实体 | 上级实体的标识 |
主键字段编码 | 主键字段的编码 |
字段名 | 描述 |
---|---|
主键 | 字段的唯一标识 |
编码 | 字段的编码 |
名称 | 字段的名称 |
物理字段 | 字段对应的物理字段名 |
所属实体 | 关联的实体标识 |
排序 | 字段的排序 |
必填 | 是否必填 |
长度 | 字段的长度 |
精度 | 字段的精度 |
是否主键 | 是否为主键 |
是否显示 | 是否在界面上显示 |
默认值 | 字段的默认值 |
字段类型 | 关联的字段类型标识 |
引用字典 | 字段关联的字典标识 |
字段名 | 描述 |
---|---|
主键 | 动态字典的唯一标识 |
编码 | 字典的编码 |
实体编码 | 关联的实体编码 |
值字段 | 字典的值字段 |
显示字段 | 字典的显示字段 |
弹窗组件 | 弹窗组件的编码 |
字段名 | 描述 |
---|---|
主键 | 字段类型的唯一标识 |
编码 | 字段类型的编码 |
名称 | 字段类型的名称 |
前端组件编码 | 对应的前端组件编码 |
字段名 | 描述 |
---|---|
主键 | 静态枚举的唯一标识 |
编码 | 枚举的编码 |
名称 | 枚举的名称 |
排序 | 枚举的排序 |
字段名 | 描述 |
---|---|
主键 | 枚举值的唯一标识 |
枚举键 | 关联的静态枚举标识 |
枚举值 | 枚举的具体值 |
为了使系统用户能够方便地操作元数据,我们需要设计一个用户界面,它应该包含以下元素:
const modules = import.meta.glob('/src/components/**/**.vue', {eager: true})
for (const path in modules) {
const componentConfig = modules[path]
const componentName = path
.split("/")
.pop()
.replace(/\.\w+$/, "");
app.component(componentName, componentConfig.default || componentConfig);
}
聚合实体
的伪代码
,用来示意如何做增删改查,为了更方便的管理动态元数据,这里仅做示意,省略了一些步骤,比如字典和枚举值的翻译等等。public 动态VO 封装动态VO(String 实体编码,Map<String,Object> 数据){
动态VO vo= new 动态VO();
vo.set实体编码(实体编码);
vo.set数据(数据);
vo.set子实例清单(new Map<String,List<动态VO>>);
return vo;
}
public List<动态VO> 查询(String 实体编码,Map<String,Object> 查询条件){
// 探明当前层结构
Map<String,Object> 当前实体 = 读一行("SELECT * FROM 实体 WHERE 编码=? AND 上级实体 IS NULL",实体编码);
List<Map<String,Object>> 子实体= 读多行("SELECT * FROM 实体 WHERE 上级实体=?",当前实体.get("主键"));
List<Map<String,Object>> 实体字段 = 读多行("SELECT * FROM 字段 WHERE 所属实体=?",当前实体.get("主键"));
String 当前实体主键字段=读值("SELECT 物理字段 FROM 字段 WHERE 所属实体=? AND 是否主键='是'",当前实体.get("编码"));
// 读取当前实体
String 查询语句="SELECT * FROM "+当前实体.get("物理表")+" WHERE TRUE ";
if(查询条件!=null){
for(Map<String,Object> 字段:实体字段){
if(查询条件.containsKey(字段.get("编码"))){
查询语句+="AND " + 字段.get("物理字段") + "=" + 查询条件.get(字段.get("编码"));
}
}
}
// 读取查询结果
List<Map<String,Object>> 查询结果 = 读多行(查询语句);
// 封装动态VO
List<动态VO> 返回结果=new ArrayList<>();
for (Map<String, Object> 数据行 : 查询结果) {
动态VO vo = 封装动态VO(数据行);
// 处理子实体
for (Map<String, Object> 子实体 : 子实体) {
String 子实体外键字段 = 读值("SELECT 物理字段 FROM 字段 WHERE 所属实体=? AND 字段类型='外键' AND 引用类型=?",子实体.get("编码"),当前实体.get("编码"));
Map<String,Object> 子实体查询条件=new HashMap<>();
子实体查询条件.put(子实体外键字段,数据行.get(当前实体主键字段));
vo.get子实例清单().add(子实体.get("编码"), 查询(子实体.get("编码"), 子实体查询条件));
}
返回结果.add(vo);
}
return 返回结果;
}
为了实现元数据的动态管理,我们需要规划以下机制:
最后一步是将用户通过界面操作的元数据信息保存回元数据表,这一步骤是实现自举元数据闭环的关键:
就像是C语言编译器其实是用C语言写的
一样,我们也要在手动创建元数据体系之后,使元数据体系能自我迭代和自我解释,实现可以用当前版本元数据系统造下一版本元数据系统
,包括实体、字段、动态字典这些表的结构也可以实时修改、增加特性,从你能够用元数据系统来修改元数据系统的功能开始,后续你再加的所有需要编码的功能其实都是元数据系统的插件而已,本质上元数据系统是可以无需修改即可自行拼装SQL进行CURD并且能自动指挥前端组装界面的。
这个是本人所做的一个某大型社区物业报修系统的前端报文格式,运用了动态元数据技术,开发时仅需在低代码平台拖拉拽,配置一下字段的属性,像使用原型设计软件一样,即可在30秒内完成需求修改、5分钟内完成功能设计、最快1小时全套管理系统的初版上线。别的公司销售人员和客户见面还没喝完茶,我们即可完成订单。
构建一个动态元数据系统是一个复杂但值得追求的目标。它能够极大地提升系统的灵活性和适应性,使得数据管理更加高效和自动化。通过遵循设计原则,我们可以创建一个既强大又易于维护的元数据系统,以支持不断变化的业务需求。而且未来在面对大数据应用、CI/CD场景、ETL自动化的落地时,动态元数据系统、自举元数据是每一位高级架构师无法绕过的一个门槛,即使是最富有经验的架构师,仍需几个月时间才能独立完成一套可用的元数据系统。只有有了动态元数据系统,才有可能有后续的开放平台、数据交换中心,BPM、OA、ERP等等灵活的系统形态。
本篇文章的出现是因为有些朋友私信想了解低代码开发如何实现,普遍反馈上一篇讲的太粗浅了,所以这一篇相当于保姆级教程了,至少可以支撑各位架构们迈出第一步,原理其实并不难,只是每天面对硬编码的环境很少有人能想到系统可以这么设计,即使想到了也并不确定能不能达到想要的效果而从未开始,即使开始了也因为周期超出预期太多,从而中途放弃了。所以我这里可以肯定的说:一个全栈架构师全职去做(设计模式/数据结构熟练、算法水平能达到轻松解CSP-J试题,前后端双修,10年以上码龄即可,这个技能水平要求其实并不高),48小时内可跑通最基本的动态元数据体系,用时3个月可以出雏形,用时6个月可做出1.0版本商用级产品。但要注意的是这种东西最初版本的研发一定是一个人独立完成的,哪怕只加了半个人这事都很难成(包括老板,除非这位全栈架构自己就是老板)。