讲解视频:www.bilibili.com/video/BV12K...
Nop平台的服务框架是下一代RPC服务框架NopGraphQL。NopGraphQL与普通的REST框架不同,它采用了最小化信息表达的架构设计,将GraphQL的任务分解组合能力推广到了所有RPC服务调用场景。一个POJO的服务函数(只需增加少量注解)可以同时支持GraphQL、REST和gRPC等多种调用方式。
依托于NopGraphQL,采用Nop平台实现的后端服务拥有远超普通Web服务的可扩展性。本文将介绍在不修改DTO类也不修改服务函数的情况下,如何通过配置来扩展服务函数的返回值。
关于NopGraphQL的设计详解,参见为什么在数学的意义上GraphQL严格的优于REST?,业务开发自由之路:如何打破框架束缚,实现真正的框架中立性
使用NopGraphQL实现服务函数非常简单,它使用的注解个数和隐含约定远少于SpringMVC。比如,在下面的示例中,我们定义了一个查询操作
java
@BizModel("Demo")
public class DemoBizModel {
@BizQuery
public CustomObj testCustomObj(@Name("name") String name) {
CustomObj obj = new CustomObj();
obj.setName(name);
obj.setStatus(1);
return obj;
}
}
@BizModel
注解用于定义服务对象名。多个不同的BizModel类可以对应于同一个服务对象,它们的服务函数会聚合到一起对外暴露。@BizQuery
注解表示这是一个查询操作,如果是@BizMutation
则表示是修改操作。修改操作会自动打开事务环境,一般不需要手工标注@Transactional
。@Name
注解用于指定服务函数的参数名。与SpringMVC不同,NopGraphQL并没有假定它在Web环境中调用,因此所有概念都与Web无关。具体通过REST服务调用时,可以通过url参数传递参数,也可以通过HTTP JSON Body来传递参数。- 服务函数上无需指定REST链接。NopGraphQL在REST调用模式下固定使用
/r/{bizObjName}__{bizAction}
这一链接模式。返回值会被自动包装为ApiResponse<T>
对象。
在上面的示例中,CustomObj具有两个属性name和status。现在要求返回一个status_label
字段,它对应于status属性的中文描述。比如,如果status为1,则status_label返回"已启用",如果status为0,则返回"已禁用"。
要实现这一功能,对于传统的RPC框架或者Web框架,肯定是需要修改服务函数的源码,在其中加载字典表然后实现文本翻译。返回的结果对象CustomObj上也需要主动增加status_label
字段。 但是,在NopGraphQL中,我们无需修改testCustomObj函数,也不需要修改CustomObj类。生成status_label
的逻辑是一个按照确定性的规则进行衍生计算的逻辑,所以它本质上与服务函数中如何构建CustomObj的逻辑是解耦的。 服务函数只需要知道如何加载、构建CustomObj对象就好了,并不需要将所有返回属性都计算出来。在NopGraphQL引擎中,服务函数的返回值并不是是直接返回给前端的DTO数据。服务函数返回的结果对象还需要进行GraphQL引擎的后处理,执行各种各样的DataLoader,最终才会将DataLoader的返回结果合成为一个JSON数据。
如果要扩展返回值对象,增加status_label属性,我们只需要标记CustomObj是一个BizObject,然后在model目录下增加一个Meta元数据定义。
java
@BizObjName("CustomObj")
public class CustomObj {
private String name;
private int status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public boolean isActive(){
return status == 1;
}
}
xml
<!-- resources/_vfs/nop/demo/model/CustomObj/CustomObj.xmeta -->
<meta x:schema="/nop/schema/xmeta.xdef" xmlns:x="/nop/schema/xdsl.xdef">
<props>
<prop name="name" displayName="名称" queryable="true" insertable="true" updatable="true">
<schema type="String"/>
</prop>
<prop name="status" displayName="状态" queryable="true" insertable="true" updatable="true">
<schema type="Integer"/>
</prop>
<prop name="status_label" displayName="状态文本">
<schema type="String"/>
<getter>
<c:script><![CDATA[
if(entity.status == 1)
return "已启用";
return "已禁用";
]]></c:script>
</getter>
</prop>
</props>
</meta>
- 在XMeta文件中可以增加一个自定义属性status_label,通过getter段来编写它的计算逻辑。
- 在XMeta文件中没有定义的字段,即使在CustomObj上存在,也不会返回到前台。比如CustomObj对象上具有isActive函数,但是
CustomObj.xmeta
文件中并没有定义active字段,返回结果中也就不会包含这个属性。
一旦启用了XMeta这个DSL模型,那么就可以进一步利用Nop平台提供的元编程能力,通过x:gen-extends
和x:post-extends
等内置的编译期代码生成器来动态生成扩展逻辑。
xml
<meta x:schema="/nop/schema/xmeta.xdef" xmlns:x="/nop/schema/xdsl.xdef"
xmlns:xpl="xpl" xmlns:meta-gen="meta-gen" xmlns:c="c">
<x:post-extends>
<meta-gen:DefaultMetaPostExtends xpl:lib="/nop/core/xlib/meta-gen.xlib"/>
</x:post-extends>
<props>
<prop name="name" displayName="名称" queryable="true" insertable="true" updatable="true">
<schema type="String"/>
</prop>
<prop name="status" displayName="状态" queryable="true" insertable="true" updatable="true">
<schema type="Integer" dict="core/active-status"/>
</prop>
</props>
</meta>
在上面的示例中,我们没有显式添加status_label
字段,而是为status字段指定了字典表core/active-status
。x:post-extends
段调用了<meta-gen:DefaultMetaPostExtends>
标签,这个标签引入了一系列的编译期生成规则,会自动查找所有配置了dict的字段,逐个为它们生成对应的label字段。
完整的示例可以参见 nop-spring-simple-demo
这个示例项目
Nop平台中所有自动生成数据库实体对象都已经标记为BizObject,并且自动生成了对应的XMeta元数据描述文件。因此所有的增删改查操作的输入输出对象都是可以通过XMeta进行扩展的。
基于可逆计算理论设计的低代码平台NopPlatform已开源:
- gitee: gitee.com/canonical-e...
- github: github.com/entropy-clo...
- gitcode:gitcode.com/canonical-e...
- 开发示例:gitee.com/canonical-e...
- 可逆计算原理和Nop平台介绍及答疑:www.bilibili.com/video/BV14u...
- 官网国际站: nop-platform.github.io/
- 网友Crazydan Studio建立的Nop开发实践分享网站: nop.crazydan.io/