平台设计
- 文章作者: flytreeleft - flytreeleft@crazydan.org
- 文章链接: https://nop.crazydan.io/implement/design
- 版权声明: 本文章采用许可协议《署名 4.0 国际 (CC BY 4.0)》,转载或商用请注明文章来源及作者信息。
模型层级结构
实际进行低代码开发时,可以围绕
XMeta
进行设计,再通过其定义推导XORM
、XView
。
DSL 文件类型
注意,以下划线开头的以下后缀文件均为自动生成,不能手工修改以避免被覆盖,
手工修改的部分应该放在 src/resources/_vfs/_delta/<xxx>/
下的同名文件中(可在不同的子工程中,通过依赖引入即可),其中,<xxx>
表示 Delta 分层标识,
默认分层为 default
,可以在定制化需求场景中增加不同的分层来管理不同场景下的 Delta。
分层的顺序由
nop.core.vfs.delta-layer-ids
控制, 如,nop.core.vfs.delta-layer-ids=base,hunan
表示先应用base
再应用hunan
。
*.xdef
:DSL 的 Schema 定义*.xlib
:Xpl 模板语言的函数库,用于将公共的可执行逻辑抽取并定义为一个 DSL 标签,以便于在 DSL 中复用执行逻辑*.xgen
:按照 Nop 的_vfs
路径生成模板代码- 在使用 maven 打包功能时,会自动执行工程的
precompile
和postcompile
目录下的*.xgen
代码, 其中precompile
在 compile 阶段之前执行,执行环境可以访问所有依赖库, 但是不能访问当前工程的类目录,而postcompile
在 compile 阶段之后执行, 可以访问已编译的类和资源文件
- 在使用 maven 打包功能时,会自动执行工程的
*.xrun
:nop-codegen
专用 DSL,用于按照模板生成代码和 Delta 文件*.xbiz
:对无代码开发模式的支持,可以在不修改 Java 源代码的情况下, 在线增加、修改后台 GraphQL 模型中的Query/Mutation/DataLoader
, 其内置了有限自动机模型,一些简单的状态迁移逻辑无需在 Java 中编程,通过配置即可完成*.xmeta
:用于定义模型的描述信息,据此可以自动实现对数据增删改查的全部逻辑_module
:空白文件,放在src/resources/_vfs/xxx/yyy/
(appName
为xxx-yyy
) 中用于标识当前包是否为一个 Nop 模块,若为模块, 则会自动加载其/_vfs/xxx/yyy/beans/
目录下的app-*.beans.xml
文件
DSL 引用关系
以 nop-demo 项目中的 Region
管理页面为例:
在 application.yaml
中指定了前端页面的定义文件为 /auth/app.action-auth.xml
,
并在其中定义了应用前端的各个模块资源 resource
(即,导航菜单)的组织结构和样式:
<auth ...>
<site id="xxx" ...>
<resource id="xxx" displayName="测试nop-demo" resourceType="TOPM"
routePath="/test-orm-nop-demo"
component="layouts/default/index" ...>
<children>
<resource id="xxx" displayName="地区" resourceType="SUBM"
url="/nop/demo/pages/Region/main.page.yaml"
component="AMIS" ...>
</resource>
...
</children>
</resource>
...
</site>
</auth>
在用户点击导航菜单以访问其页面资源时,会向后端传递 resource#url
上所设置的页面路径,
后端对该文件 /pages/Region/main.page.yaml
进行解析并得到页面结构后,
将该结构返回给前端,再由前端根据该页面结构进行渲染:
// 请求数据
{
"query": "query PageProvider__getPage($path:String){\nPageProvider__getPage(path:$path)\n}",
"variables": {
"path": "/nop/demo/pages/Region/main.page.yaml"
}
}
// 响应数据
{
"data": {
"PageProvider__getPage": {
"type": "page",
"name": "main",
"body": { ... }
}
}
}
默认生成页面 /pages/Region/main.page.yaml
的内容如下:
x:gen-extends: |
<web:GenPage view="Region.view.xml" page="main" xpl:lib="/nop/web/xlib/web.xlib" />
其表示在编译阶段(x:gen-extends
)调用 Xpl 库 /nop/web/xlib/web.xlib
中的 GenPage
(调用时的标签前缀与库文件名相同)函数解析页面视图
Region.view.xml
并获取其 name="main"
的页面的结构:
<view ...>
...
<pages>
<crud name="main" .../>
...
</pages>
</view>
可以查看
_dump
目录中编译后的页面文件/pages/Region/main.page.yaml
内容, 以了解完整的页面结构。
页面视图会与一个业务模型绑定,以便于在编译页面时,向页面组件填充与业务模型相关的配置数据:
<view ...>
<objMeta>/nop/demo/model/Region/Region.xmeta</objMeta>
<controlLib>/nop/web/xlib/control.xlib</controlLib>
...
</view>
而页面组件在 controlLib
指向的 Xpl 库中进行定义:
<lib ...>
<tags>
<edit-tree-parent outputMode="xjson">
...
<attr name="objMeta" mandatory="true"/>
<attr name="bizObjName" mandatory="true"/>
<source>
<c:script><![CDATA[
function filter_cond(){
if(objMeta.tree.levelProp)
return 'filter_' + objMeta.tree.levelProp + '=' + (objMeta.tree.rootLevelValue ?? '__null');
return 'filter_' + objMeta.tree.parentProp + '=__null';
}
]]></c:script>
<tree-select clearable="@:true">
<source>
<url>
@query:${bizObjName}__findList/value:id,label:${objMeta.displayProp || 'id'},
${objMeta.tree.childrenProp} @TreeChildren(max:5)?${filter_cond()}
</url>
</source>
</tree-select>
</source>
</edit-tree-parent>
...
</tags>
</lib>
其中,objMeta
便是在页面视图中通过 <objMeta/>
所绑定的业务模型。
该业务模型的结构则会在其对应的 *.xmeta
文件中进行定义:
<meta ...>
<entityName>io.nop.demo.dao.entity.Region</entityName>
<primaryKey>id</primaryKey>
<displayProp>name</displayProp>
<x:gen-extends>
<!-- 标签前缀与库文件名相同 -->
<meta-gen:DefaultMetaGenExtends xpl:lib="/nop/core/xlib/meta-gen.xlib"/>
</x:gen-extends>
<x:post-extends>
<!-- 标签前缀与库文件名相同 -->
<meta-gen:DefaultMetaPostExtends xpl:lib="/nop/core/xlib/meta-gen.xlib"/>
</x:post-extends>
<props>s
<prop name="id" displayName="地区ID" ...>
<schema type="java.lang.String" precision="32"/>
</prop>
<prop name="name" displayName="地区名称" ...>
<schema type="java.lang.String" precision="500"/>
</prop>
...
</props>
</meta>
默认在该业务模型的结构中还将调用 Xpl 库 /nop/core/xlib/meta-gen.xlib
中的函数 DefaultMetaGenExtends
和 DefaultMetaPostExtends
对业务模型结构进行自动化调整,比如,在当前结构中附加必要的 GraphQL 相关的配置数据。
注意事项
xpl:lib
必须为 Delta 层的绝对路径,不能是相对路径- 使用
xpl:lib
的标签前缀需与库文件名相同