feat: 简化技术描述语言(1~2.6)

This commit is contained in:
dayjoy
2025-08-19 18:51:41 +08:00
parent 10af7636a9
commit bd1cf2a5a4

View File

@@ -1,71 +1,72 @@
<TOCO知识库>
-----------------------------------------------------------------------------
### **1. TOCO 平台概览:**
- **1.1 平台简介:** TOCO是一款重视软件设计及核心代码自动生成的专业研发平台。基于DDD、分层设计、CQRS等经典研发理论,数据库到API全部覆盖,可显著提升开发设计和编程效率,帮助开发团队实现更高的质量和生产力
- **1.2 核心价值/目标用户:** 从软件工程理论出发,提供软件设计能力,设计结果可直接转换为标准格式的代码,提编码一致性效率
- **1.3 主要特性概览:** 可视化设计、模型关联、多人协作、代码生成器
- **1.1 平台简介:** TOCO是软件设计代码自动生成平台。基于DDD、分层架构、CQRS理论覆盖数据库到API的完整开发链路,提升开发效率和代码质量
- **1.2 核心价值/目标用户:** 提供软件设计能力,设计成果直接生成标准代码,提编码一致性效率
- **1.3 主要特性概览:** 可视化设计、模型关联、多人协作、代码生成器
### **2. TOCO 设计元素:**
#### **2.1 模块 (Module)**
- **定义与用途:** 在TOCO中我们将系统领域细分为具体的模块映射为Java工程中的module。这些模块代表了系统的叶子子域每个模块负责特定的功能。模块划分有助于系统的可维护性和可扩展性并能提高开发效率和代码质量
- **关键配置:** 名称(小写英文字母+下划线如meeting,user_detail,禁止加任何固定后缀,全局唯一),描述
- **与其他元素关系:** 下面的每种设计元素都属于个模块
- **代码产物** 每个Module会单独生成一个Java Module项目路径/modules/模块名,内部采用了entrance、service、manager、persist、common分层结构
- **关键配置:** 名称(小写英文+下划线如meeting,user_detail禁止后缀全局唯一),描述
- **与其他元素关系:** 下列所有设计元素都属于个模块
- **代码产物:** 生成独立Java Module项目路径/modules/模块名,包含entrance、service、manager、persist、common分层
#### **2.2 枚举 (Enum)**
- **定义与用途:** Enum用来表达一些常量值集合,可被其他模块使用,可被用来做为字段类型
- **关键属性/配置:** 名称(以_enum结尾,全局唯一),枚举值列表(全大写字母+下划线)
- **与其他元素关系:** 枚举可以作为其他对象(Entity、Dto、Vo、Bto、Qto、Eo)的字段类型使用。
- **定义与用途:** 表示常量值集合,可模块使用,可为字段类型
- **关键属性/配置:** 名称(以_enum结尾全局唯一),枚举值列表(全大写+下划线)
- **与其他元素关系:** 可作为Entity、Dto、Vo、Bto、Qto、Eo的字段类型
- **Enum设计元素的表达:**
- Json格式表达json schema 定义如下
- Json格式schema如下
```json
{
"type": "object",
"properties": {
"name": { "type": "string","description": "名称,使用英语表达,单词之间使用下划线分割,总长度不能超过32字符"},
"uuid": { "type": "string","description": "计元素Enum)在TOCO中的唯一标识符,创建枚举的时候,该字段为空; 在更新时,该字段不能为空"},
"description": { "type": "string", "description": "描述这个枚举的具体含义,介绍这个枚举的用途,控制在128字符以内"},
"moduleName": { "type": "string", "description": "指定该设计元素(Enum)所属模块,创建的时候该字段不能为空,在更新的时候,该字段可以为空"},
"name": { "type": "string","description": "名称,英语表达,下划线分割,超过32字符"},
"uuid": { "type": "string","description": "唯一标识符,创建时为空,更新时必填"},
"description": { "type": "string", "description": "枚举含义和用途,不超过128字符"},
"moduleName": { "type": "string", "description": "所属模块,创建时必填,更新时可空"},
"values": {
"type": "array","description": "枚举值列表",
"items": {"type": "string","description": "枚举值,使用英语表达, 单词之间使用下划线分割,总长度不能超过32字符"}
"items": {"type": "string","description": "枚举值,英语表达下划线分割,超过32字符"}
}
},
"required":["name","description"]
}
```
* **代码产物和修改建议**
* **生成产物**:在common模块中生成一个Java类
* **职责:** 表达Enum数据结构
* **命名规则**类名以Enum结尾
* **类路径** 位于 <code>**.common.enums</code> 包路径
* **唯一标识符位置** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Enum在TOCO中的uuid}|ENUM|DEFINITION
- **生成代码** Enum会在common层生成Enum文件如StatusEnum
- **修改建议** 不建议修改
* **生成产物:** common模块中生成Java类
* **职责:** 表达Enum数据结构
* **命名规则:** 类名以Enum结尾
* **类路径:** 位于 <code>**.common.enums</code> 包下
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${Enum的uuid}|ENUM|DEFINITION
- **生成代码:** 在common层生成Enum文件如StatusEnum
- **修改建议:** 不建议修改
#### **2.3 值对象 (EO)**
- **定义与用途:** EO为一种POJO对象结构,可被其他模块使用,可被用来做为实体字段类型。
- **关键属性/配置:** 名称(以_eo结尾,全局唯一)。EO的字段类型只能为基本类型(含List、EO、Enum,其他类型不允许
- **与其他元素关系:** 可作为其他对象(Entity、Dto、Vo、Bto、Qto)的字段类型使用同时一个EO中可以嵌套其他EO作为字段类型
- **EO设计元素的表达:**
- Json格式表达Json Schema定义如下
- **定义与用途** EO是可复用的POJO数据结构,可模块使用,可为实体字段类型。
- **关键属性/配置** 名称以_eo结尾全局唯一字段类型限制:基本类型List、EO、Enum。
- **与其他元素关系** 可作为Entity、Dto、Vo、Bto、Qto的字段类型支持EO嵌套
- **EO设计元素的表达**
- 使用Json格式表达Schema定义如下
```json
{
"type":"object",
"properties": {
"name":{ "type": "string", "description": "名称,使用英语表达,单词之间使用下划线分割,总长度不能超过32字符"},
"description": { "type": "string","description": "描述,描述这个数据结构的具体含义,介绍这个数据结构的用途,控制在128字符以内"},
"uuid":{ "type": "string", "description": "该设计在TOCO中的唯一标识符,创建EO的时候该字段为空; 在更新时,该字段不能为空"},
"moduleName":{ "type": "string", "description": "指定该设计元素所属模块,创建的时候该字段不能为空,在更新的时候,该字段可以为空"},
"name":{ "type": "string", "description": "名称,英文下划线分割,超过32字符"},
"description": { "type": "string","description": "描述EO含义和用途不超过128字符"},
"uuid":{ "type": "string", "description": "唯一标识符,创建时为空,更新时必填"},
"moduleName":{ "type": "string", "description": "所属模块,创建时必填,更新时可空"},
"fieldList":{
"type":"array", "description": "定义EO属性字段列表",
"type":"array", "description": "EO属性字段列表",
"items":{
"type": "object",
"properties":{
"name": { "type": "string","description": "属性字段名称,使用英语表达,单词之间使用下划线分割,总长度不能超过32字符"},
"uuid":{ "type": "string","description": "字段类型是枚举、值对象的时候,该字段不能为空,表示该字段对应的枚举或者值对象" } ,
"type":{ "type": "string","description": "字段类型,可以是String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum, BigDecimal,List" },
"innerUuid":{"type": "string", "description": "当innerTypeEo或Enum的时候,表示字段类型对应的枚举或者值对象"},
"innerType": { "type": "string", "description": "当type是List的时候表示List包含的元素类型, 类型可以是String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum, BigDecimal"}
"name": { "type": "string","description": "字段名称,英文下划线分割,超过32字符"},
"uuid":{ "type": "string","description": "字段类型为Enum或Eo时必填对应的Enum或Eo标识符" } ,
"type":{ "type": "string","description": "字段类型String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum,BigDecimal,List" },
"innerUuid":{"type": "string", "description": "当innerTypeEo或Enum时,对应的标识符"},
"innerType": { "type": "string", "description": "List元素类型String,Integer,Long,Float,Double,Boolean,Date,Eo,Enum,BigDecimal"}
},
"required":[ "name","type"]
}
@@ -75,125 +76,134 @@
}
```
* **代码产物和修改建议**
* **生成产物**:在persist层生成结构定义类如AddressEo
* **职责:** 表达POJO数据结构
* **命名规则**类名以Eo结尾
* **类路径:** 位于 <code>**.persist.eo</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid}|EO|DEFINITION
* **生成产物** persist层生成结构定义类如AddressEo
* **职责** 表达POJO数据结构
* **命名规则** 类名以Eo结尾
* **类路径:** 位于 `**.persist.eo` 包
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则${Eo的uuid}|EO|DEFINITION
- **修改建议:** 不建议修改
#### **2.4 实体关系 (ER / Entity)**
- **定义与用途:** 实体及其关系。一个实体一般对应一个数据库表,关系为实体间的外键依赖关系
- **关键属性/配置:** 实体中包含名称、字段、字段类型、主键、索引关系分为1:1和1:N关系
- **与其他元素关系:** 实体关系是聚合的基础也是DTO和VO的派生基础
- **定义与用途:** 实体对应数据库表,关系为实体间的外键依赖
- **关键属性/配置:** 包含名称、字段、字段类型、主键、索引关系分为1:1和1:N
- **与其他元素关系:** 实体是聚合的基础也是DTO和VO的派生基础
- **代码产物和修改建议**
- 结构定义
* **生成产物**Java类按照Mybatis-plus要求生成
* **职责:** 按照Mybatis-plus的要求生成结构定义类文件
* **类路径:** 位于 <code>**.persist.dos</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DEFINITION
* **生成产物**Mybatis-plus要求生成的Java类
* **职责:** 生成Mybatis-plus结构定义类文件
* **类路径:** 位于 `**.persist.dos` 包
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${Entity的uuid}|ENTITY|DEFINITION
- Mapper
* **生成产物**persist层生成Mybatis-plusMapper类
* **职责:** 提供Mapper给Mybatis-plus框架
* **生成产物**persist层Mybatis-plus Mapper类
* **职责:** Mybatis-plus框架提供Mapper
* **命名规则**类名以Mapper结尾(${entityName}Mapper)
* **类路径:** 位于 <code>**.persist.mapper.mybatis</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Eo在TOCO中的uuid}
* **类路径:** 位于 `**.persist.mapper.mybatis` 包
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${Entity的uuid}|ENTITY|MAPPER
- Dao接口
* **生成产物**persist层生成Dao接口
* **职责:** 提供Entity数据查询接口为service层提供数据访问入口
* **生成产物**persist层Dao接口
* **职责:** 提供Entity数据查询接口为service层提供数据访问入口
* **命名规则**类名以Dao结尾(${entityName}Dao)
* **类路径:** 位于<code>**.persist.mapper</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|IDAO
* **类路径:** 位于 `**.persist.mapper` 包
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${Entity的uuid}|ENTITY|IDAO
- Dao实现
* **生成产物**persist层生成Dao接口实现类文件
* **职责:** 通过调用Mapper实现实现Dao接口
* **生成产物**persist层Dao接口实现类
* **职责:** 通过调用Mapper实现Dao接口
* **命名规则**类名以DaoImpl结尾(${entityName}DaoImpl)
* **类路径:** 位于 <code>**.persist.mapper</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|ENTITY|DAO
* **类路径:** 位于 `**.persist.mapper` 包
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则: ${Entity的uuid}|ENTITY|DAO
- **修改建议:** 不建议修改
#### **2.5 聚合对象 (BO/业务对象)**
- **定义与用途:** 在TOCO中聚合对象是对一组密切关联实体的封装。聚合对象从单一实体开始,这个实体我们称为聚合根,通过实体间关系,不断的顺序将其他实体按层级关系组装进这个聚合对象。聚合对象可以按实体表达为树形结构。聚合对象提供了这组实体的内存一致性视图,提供数据操作入口。由于写操作的内聚性,聚合对象只能在单一模块中组合而且一个实体只能属于一个对象。同样如果有实体不在任何一个聚合对象中TOCO将无法提供与之相关的写方法
- **包含元素:** 聚合对象包括聚合根及其聚合下的其他子实体对象例如,商品聚合ProductBO中,商品基本信息实体是ProductBO的聚合根商品SKU实体、商品库存实体是ProductBO的子聚合对象。
- **关键配置:** 名称(${EntityName驼峰}BO结尾如StaffBO),聚合根实体,聚合子对象实体。每个聚合必须包含一个聚合根
- **与其他元素关系:** 聚合是写方案的基础
- **定义与用途:** 聚合对象封装一组关联实体。从聚合根实体开始,按层级关系组装其他实体,形成树形结构。提供内存一致性视图数据操作入口。一个实体只能属于一个聚合对象,聚合对象只能在单一模块中组合。
- **包含元素:** 聚合根实体 + 子实体对象例如ProductBO包含商品基本信息实体(聚合根)、商品SKU实体、商品库存实体(子对象)
- **关键配置:** 名称(${EntityName驼峰}BO如StaffBO),聚合根实体,子对象实体。每个聚合必须包含一个聚合根
- **与其他元素关系:** 聚合是写方案的基础
- **代码产物和修改建议**
- 综述
- 业务对象包含多个Entity通过业务对象的嵌套组合表达Entity之间的关系,如果一个业务对象包含了子对象,则会生成BO和BaseBO,BaseBO封装实体属性和关系子类留给业务扩展逻辑;
如果是叶子节点不存在子对象的BO则直接生成BO类文件不生成BaseBO类文件
- 业务对象包含多个Entity通过嵌套组合表达Entity关系。有子对象时生成BO和BaseBOBaseBO封装实体属性和关系BO用于业务扩展。无子对象时只生成BO类。
- BO
* **生成产物**Manager层生成聚合对象类文件符合Hibernate标准
* **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更监听数据变更,支持数据校验
* **命名规则**类名以BO结尾(${entityName}BO)
* **类路径:** 位于 <code>**.manager.bo</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION
- **修改建议:** 建议修改BO中的validateAggregate或valid方法。不建议修改验方法以外的其他代码,如果发现需求中有业务不变性校验,**注意** 上述的校验方法,在写方案内部由框架触发调用,而不是业务代码显式调用
* **生成产物**Manager层生成聚合对象类符合Hibernate标准
* **职责:** 定义聚合对象,组合成层级结构实现充血模型,支持写链路数据变更监听变更、数据校验
* **命名规则**${entityName}BO
* **类路径:** `**.manager.bo`包
* **唯一标识符位置:** 类注解@AutoGenerated中uuid规则: ${Entity的uuid}|BO|DEFINITION
- **修改建议:** 建议修改BO中的validateAggregate或valid方法进行业务不变性校验。不建议修改验方法以外的代码。校验方法由框架自动调用,不需要业务代码显式调用
- BaseBO
* **生成产物**对于存在子BO的聚合对象,封装不变代码部分
* **职责:** 定义聚合对象,多个聚合对象组合成层级结构实现充血模型,支持写链路上的数据变更监听数据变更,支持数据校验
* **命名规则**类名以BO结尾(${entityName}BO)
* **类路径:** 位于<code>**.manager.bo</code> 包路径
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定 ,uuid规则: ${Entity在TOCO中的uuid}|BO|DEFINITION
- **修改建议:** 建议修改BO中的validateAggregate或valid方法,如果发现需求中有业务不变性校验;不建议修改验方法以外的其他代码
* **生成产物**存在子BO时生成,封装不变代码部分
* **职责:** 定义聚合对象,组合成层级结构实现充血模型,支持写链路数据变更监听变更、数据校验
* **命名规则**${entityName}BO
* **类路径:** `**.manager.bo`包
* **唯一标识符位置:** 类注解@AutoGenerated中uuid规则: ${Entity的uuid}|BO|DEFINITION
- **修改建议:** 建议修改BO中的validateAggregate或valid方法进行业务不变性校验不建议修改验方法以外的代码
#### **2.6 数据传输对象 (DTO)**
- **定义与用途:** 在TOCO中DTO表达某个Entity为基本,通过外键关系不断关联多个Entity的数据结构。DTO还隐式表达了数据的取数拼装,这种拼装符合外键关系。DTO分为BaseDTO和普通DTOBaseDTO派生自Entity包含Entity所有字段每个Entity有且仅有一个BaseDTO普通DTO派生自BaseDTO包含BaseDTO所有字段,且可以增加扩展字段或自定义字段。注意DTO不能作为接口参数,不能作为HTTP API返回值
- **如何创建/生成:** 对于每个EntityTOCO会自动生成一个BaseDTO命名为${Entity名字}BaseDto如UserBaseDto该BaseDTO包含Entity全部字段。除了BaseDTO其他DTO均需要手动以BaseDTO为根来创建。在TOCO中必须要先判断需要的DTO是否为BaseDTO如果是BaseDTO则可通过Entity名称获取BaseDTO如果不是BaseDTO则需要通过DTO要表达的信息创建DTO,如会议及议程信息。
- **关键配置:** 名称(BaseDTO以BaseDto结尾其他DTO以Dto结尾,全局唯一)、根Entity、字段列表。DTO中的字段分三种a.继承Entity或BaseDTO的字段和Entity及BaseDTO的字段类型一样b.扩展字段含正向替换和反向注入字段类型为DTO或List<DTO>;c.自定义字段类型为基本类型、Eo、Enum、DTO类型。BaseDTO中一般包含Entity全部字段,DTO中一般包含BaseDTO中的全部字段,不进行字段裁剪,可以根据外键关系扩展其他Entity(详见**字段扩展方式**)在明确无法扩展外部Entity的情况下可增加对应的自定义字段。
- **字段扩展方式:**TOCO定义了一个DTO组装方法适用于DTO通过外键关系替换/注入对应Entity的信息对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件可扩展a.对于正向替换:当前实体存在指向其他实体的外键字段b.对于反向注入:其他实体存在指向当前实体的外键字段。
例如有两个Entity
<code>
- **定义与用途:** DTO基于某个Entity构建通过外键关系关联多个Entity的数据结构。DTO隐含数据组装逻辑,符合外键关系。分为BaseDTO和普通DTOBaseDTO直接派生自Entity包含Entity所有字段每个Entity有一个BaseDTO普通DTO基于BaseDTO创建包含BaseDTO所有字段增加扩展字段或自定义字段。DTO不能作为接口参数不能作为HTTP API返回值
- **创建方式:** 每个Entity自动生成一个BaseDTO命名为${Entity名字}BaseDto如UserBaseDto包含Entity全部字段。其他DTO需手动基于BaseDTO创建。判断是否为BaseDTO是则通过Entity名称获取否则通过DTO要表达的信息创建如会议及议程信息。
- **关键配置:** 名称BaseDTO以BaseDto结尾其他DTO以Dto结尾全局唯一、根Entity、字段列表。字段分三种a.继承Entity或BaseDTO的字段类型相同b.扩展字段,含正向替换和反向注入字段类型为DTO或List<DTO>c.自定义字段类型为基本类型、Eo、Enum、DTO。BaseDTO包含Entity全部字段DTO包含BaseDTO全部字段裁剪字段,可通过外键关系扩展其他Entity,无法扩展时可增加自定义字段。
- **字段扩展方式:** DTO通过外键关系替换/注入对应Entity信息。满足条件可扩展a.正向替换:当前实体指向其他实体的外键字段b.反向注入:其他实体指向当前实体的外键字段。
例如两个Entity
```
MeetingRoom{ //会议室
Long id;// 会议室id,主键
String name;// 会议室名称
}
Meeting { //会议
Long id;// 会议id,主键
Long roomid; //占用的会议室id到MeetingRoom的外键,n:1关系
Long backupRoomid; //备用会议室id到MeetingRoom的外键,n:1关系
Long roomid; //会议室id外键到MeetingRoom的n:1关系
Long backupRoomid; //备用会议室id外键到MeetingRoom的n:1关系
String title; //会议标题
DateTime startTime //会议开始时间
DateTime endTime //会议结束时间
DateTime startTime; //开始时间
DateTime endTime; //结束时间
}
</code>
其中Meeting和MeetingRoom是n:1关系。即多个会议室会占用同一个会议室。
当组装对象以某Entity为根时那么首先它将拥有和该Entity一样的数据结构并将通过下面的“正向替换”“反向注入”的行为递归的将多个互相之间有外键关系的Entity的信息组装到该组装对象中
定义“正向替换”这个行为,选定一个表,这张表存在到另外一张表的一个或多个外键。选择和需求相关的具体的外键属性,将该外键属性替换为另一张表为根的组装对象。这样就可以获取基于某些外键且包含另一张表更详细的属性数据
例如需要会议和其占用会议室时将Meeting表中的roomid外键替换为以MeetingRoom为根的组装对象而backupRoomid对应的候选会议室具体信息和本需求无关不做任何替换。如下
<code>
```
Meeting和MeetingRoom是n:1关系。多个会议占用同一个会议室
组装对象以某Entity为根时首先拥有该Entity相同数据结构通过"正向替换"、"反向注入"行为递归组装有外键关系的Entity信息
**正向替换:** 选定表存在到另一表的外键。选择相关的外键属性,将该外键替换为另一表为根的组装对象。获取基于外键且包含另一表详细属性的数据。
例如需要会议和占用会议室时将Meeting表中roomid外键替换为以MeetingRoom为根的组装对象backupRoomid不替换
```
MeetingWithRoomDto {
Long id;// 会议id
MeetingRoomDto room { //正向替换该会议用的会议室信息,是一个以会议室为根的对象
Long id; //会议室ID
MeetingRoomDto room { //正向替换会议室信息,以会议室为根的对象
Long id; //会议室ID
String name;// 会议室名称
}
Long backupRoomid; // 不变化
Long backupRoomid; // 不变化
String title; //会议标题
DateTime startTime //会议开始时间
DateTime endTime //会议结束时间
DateTime startTime; //开始时间
DateTime endTime; //结束时间
}
</code>
又例如需要会议和其候选会议室时将Meeting表中的backupRoomid进行正向替换。
又例如需要会议且即需要其占用会议室也需要候选会议室时将Meeting表中的roomidbackupRoomid都进行正向替换
同时TOCO还定义了“反向注入”这个行为选定一个表如果有另外的表到前表有外键选择和需求相关的具体的外键属性在选定表中增加一个以另外表为根的组合对象(当外键关系是1:1时)或者组合对象的列表(当外键关系是n:1时)。需求是:“获取会议室和占用它的会议信息”,需要选定MeetingRoom那么基于另外的表Meeting存在字段roomid为到MeetingRoom的n:1关系外键。可以将List<MeetingBaseDto>反向注入到MeetingRoom中最终生成一个以Meeting为根的组装对象生成
<code>
```
**反向注入:** 选定表如有其他表到该表有外键选择相关的外键属性在选定表中增加以另一表为根的组合对象1:1时或组合对象列表n:1时
需求:"获取会议室和占用它的会议信息"选定MeetingRoom基于Meeting存在roomid字段为到MeetingRoom的n:1外键。将List<MeetingBaseDto>反向注入到MeetingRoom中
```
MeetingRoomWithMeetingDto {
Long id;// 会议室id
String name;// 会议室名称
List<MeetingBaseDto> meetingList { //反向注入用该会议室的会议信息,是以会议为根的对象
List<MeetingBaseDto> meetingList { //反向注入使用该会议室的会议信息
Long id;// 会议id
String title; //会议标题
DateTime startTime //会议开始时间
DateTime endTime //会议结束时间
DateTime startTime; //开始时间
DateTime endTime; //结束时间
}
}
</code>
这种“正向替换”和“反向注入”可以按需递归调用去将多个互相之间有外键关系的对象组装成最终对象。例如还有另外一张表MeetingAgenda到Meeting有n:1的外键和另外一张表AgendaAttendance到MeetingAgenda有n:1外键。那么如果我要去组装以Meeting开始包含MeetingRoom, MeetingAgenda, AgendaAttendance的组装对象首先发现MeetingRoom是可以正向扩展到Meeting的反向注入MeetingAgenda而AgendaAttendance需要先反向注入到MeetingAgenda中。
- **TOCO中DTO的json结构描述:** 在TOCO中DTO使用一个json结构表示该结构可用于理解DTO的含义或作为创建、更新DTO工具的参数。部分字段的含义为dto的uuid为唯一标识如果需要创建DTO则设置为null如果需要复用则填入其uuid。expandList为正向替换reverseExpandList为反向注入customFieldList为自定义字段。expandListList中foreignKeyInThisEntity为正向替换对应的本表外键字段的名字dtoFieldName为正向替换之后给该字段的起的新名字reverseExpandList中foreignKeyInOtherEntity为反向注入对应的他表外键字段的名字dtoFieldName为反向注入之后给该字段的起的新名字customFieldList中uuid为自定义字段特有的UUID创建DTO的时候不需要填入因为TOCO会自动为其分配UUID更新DTO的时候需要传入用于定位需要更新的自定义字段typeUuid参数对应类结构的UUID当type为Enum、Eo时包含该字段innerType为List内部类型当type为List时包含该字段innerUuid为List内部类结构的UUID当type为List且innerType=Enum、Eo时包含该字段。示例如下
```
"正向替换"和"反向注入"可按需递归调用,组装多个有外键关系的对象。
- **JSON结构描述:** DTO用json结构表示用于理解含义或作为创建、更新DTO工具的参数。字段含义dto的uuid为唯一标识创建时设置为null复用时填入uuid。expandList为正向替换reverseExpandList为反向注入customFieldList为自定义字段。expandList中foreignKeyInThisEntity为本表外键字段名dtoFieldName为替换后字段名reverseExpandList中foreignKeyInOtherEntity为他表外键字段名dtoFieldName为注入后字段名customFieldList中uuid为自定义字段UUID创建时不填入更新时需传入用于定位typeUuid为类结构UUIDtype为Enum、Eo时包含innerType为List内部类型type为List时包含innerUuid为List内部类结构UUIDtype为List且innerType为Enum、Eo时包含。
示例:
- meeting_with_room_dto
<code>
```json
{
"dto": {
"uuid": null,
"name": "meeting_with_room_dto",
"description": "会议详情,包含会议室信息,以及其中的会议列表",
"description": "会议详情,包含会议室信息",
"fromEntity": "meeting",
"expandList": [
{
@@ -209,7 +219,7 @@ DateTime endTime //会议结束时间
],
"customFieldList":[
{
"uuid": "自定义字段唯一标识更新DTO的时候需要传入",
"uuid": "自定义字段唯一标识更新DTO时需传入",
"name": "status",
"type": "Enum",
"typeUuid": "对应Enum的uuid",
@@ -218,15 +228,16 @@ DateTime endTime //会议结束时间
]
}
}
</code>
```
- meeting_room_with_meetings_dto
<code>
```json
{
"dto": {
"uuid": "d05c7b3d-1c92-45a1-2113-a01b245813c1",
"name": "meeting_room_with_meetings_dto",
"description": "会议室详情,包含会议室信息,以及其中的会议信息",
"fromEntity": "meeting",
"description": "会议室详情,包含会议信息",
"fromEntity": "meeting_room",
"reverseExpandList": [
{
"foreignKeyInOtherEntity": "room_id",
@@ -241,49 +252,51 @@ DateTime endTime //会议结束时间
]
}
}
</code>
示例中meeting_with_room_dto没有uuid为待创建的DTO。meeting_base_dto和meeting_room_with_meetings_dto为已存在的DTO带有uuid。
```
- **预定义方法:** 对于每一个DTOTOCO会自动其根Entity的唯一索引生成一些预定义的RPC方法及实现预定义方法不仅获取了根Entity的数据还通过RPC调用的方式**自动**获取了**所有扩展字段**的数据并对数据进行了拼装处理。如实体user有unique index(username)则会针对UserDto生成UserDto UserDtoService.getByUserName(String userName)和List<UserDto> UserDtoService.getByUserNames(List<String> userNames)。预定义方法内部根据外键关系自动生成了复杂DTO数据的递归、Join拼装的能力会直接返回DTO内部的所有继承字段和扩展字段的数据。注意自定义字段的数据获取不会自动生成需要在对应的convert方法中编写代码
- **公开性:** DTO可以设置公开性如果DTO为公开则其生成的预定义RPC方法也为公开RPC可以被其他模块订阅并调用如果DTO为非公开则其生成的预定义RPC方法也为非公开RPC其他模块不可见
- **跨模块依赖:** 如果DTO内引用了其他模块的DTO则需要订阅其他模块的RPC(getBy${PrimaryKey},getBy${PrimaryKey}s,getBy${foreignKey}等)方法用来获取对应的DTO
- **复杂嵌套DTO获取流程:** 如果我们需要获取DTO通常有2种方式且两种方式都可以直接获取到复杂嵌套的DTO数据
- 第1种: 通过预定义方法获取DTO
- 第2种: 通过读方案获取DTO
meeting_with_room_dto无uuid为待创建DTO。meeting_base_dto和meeting_room_with_meetings_dto为已存在DTO带uuid
在TOCO中通常使用以下步骤判断使用哪种方式
- a. 如果查询DTO的条件为主键或唯一索引的值或列表则直接使用第1种方式
- b. 如果通过其他复杂查询条件则可以采用第2种方式
- **预定义方法:** 每个DTO基于根Entity的唯一索引自动生成预定义RPC方法及实现预定义方法获取根Entity数据通过RPC自动获取所有扩展字段数据并拼装。如实体user有唯一索引username则为UserDto生成UserDto UserDtoService.getByUserName(String userName)和List<UserDto> UserDtoService.getByUserNames(List<String> userNames)。预定义方法内部基于外键关系自动生成复杂DTO数据的递归、Join拼装能力直接返回DTO内部所有继承字段和扩展字段数据。自定义字段数据获取不自动生成需在对应convert方法中编写代码。
- **公开性:** DTO可设置公开性DTO为公开时生成的预定义RPC方法也为公开RPC可被其他模块订阅调用DTO为非公开时生成的预定义RPC方法也为非公开RPC其他模块不可见。
- **跨模块依赖:** 如DTO内引用其他模块DTO需订阅其他模块RPC方法getBy${PrimaryKey}、getBy${PrimaryKey}s、getBy${foreignKey}等用于获取对应DTO。
- **复杂嵌套DTO获取流程:** 获取DTO有2种方式都可直接获取复杂嵌套DTO数据
- 第1种通过预定义方法获取DTO
- 第2种通过读方案获取DTO
判断使用方式的步骤:
- a. 查询DTO条件为主键或唯一索引的值或列表使用第1种方式
- b. 通过其他复杂查询条件采用第2种方式
判断使用方式时只能根据查询条件判断是否使用读方案禁止使用返回值是否需要数据拼装来判断如现实代码和TOCO定义有冲突只能使用TOCO定义
注意判断使用哪种方式时,只能根据查询条件判断是否使用读方案,绝对禁止使用返回值是否需要数据拼装来判断!**如果现实中的代码和TOCO中的步骤有冲突时只能使用TOCO的定义**
- **代码产物和修改建议**
- 结构定义
* **生成产物**一个Java类
* **职责:** 表达DTO数据结构
* **生成产物**Java类
* **职责:** 表达DTO数据结构
* **命名规则**类名以Dto结尾
* **禁止** 修改该类
* **类路径:** 位于 <code>**.manager.dto</code> 包路径下
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|DEFINITION
* **类路径:** `**.manager.dto` 包路径下
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则${DTO在TOCO中的uuid}|DTO|DEFINITION
- **Manager**
* **生成产物:** Java接口及实现类
* **命名规则:** 接口类名以Manager结尾、实现类名以ManagerImpl结尾(${DtoName}Manager)、基类名ManagerBaseImpl结尾(${DtoName}ManagerImpl)
* **职责:** 提供DTO数据获取接口包括根据id单个、id列表批量获取、以及根据DTO对应实体的数据库索引获取
* **类路径:** 位于<code>**.manager</code> 包路径下
* **禁止** 删除该类中任何系统自动生成函数
* **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|MANAGER
* **生成产物:** Java接口及实现类
* **命名规则:** 接口类名以Manager结尾、实现类名以ManagerImpl结尾(${DtoName}Manager)、基类名ManagerBaseImpl结尾(${DtoName}ManagerImpl)
* **职责:** 提供DTO数据获取接口包括根据id单个、id列表批量获取、根据DTO对应实体的数据库索引获取
* **类路径:** `**.manager` 包路径下
* **禁止** 删除该类中任何系统自动生成函数
* **唯一标识符位置:** 类注解@AutoGenerated中指定uuid规则${DTO在TOCO中的uuid}|DTO|MANAGER
- **Converter**
* **生成产物:** Java实现类及基类
* **禁止** 删除该类中任何系统自动生成函数
* **生成产物:** Java实现类及基类
* **禁止** 删除该类中任何系统自动生成函数
* **命名规则:** 实现类名以Converter结尾(${DtoName}Converter)、基类名以BaseConverter结尾(${DtoName}BaseConverter)
* **职责:** Entity转换到BaseDTO或BaseDTO转为普通DTO从Entity转为BaseDTO的方法命名为convert${EntityName}To${DtoName}从BaseDTO转换为DTO的方法命名为convert${BaseDtoName}To${DtoName}
* **类路径:** 位于 <code>**.manager.converter</code> 包路径下
* **职责:** Entity转换到BaseDTO或BaseDTO转为普通DTO从Entity转为BaseDTO的方法命名为convert${EntityName}To${DtoName}从BaseDTO转换为DTO的方法命名为convert${BaseDtoName}To${DtoName}
* **类路径:** `**.manager.converter` 包路径下
* **唯一标识符位置:**
* 实现类Converter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|CONVERTER
* 基类BaseConverter 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER
* 实现类Converter类注解@AutoGenerated中指定uuid规则${DTO在TOCO中的uuid}|DTO|CONVERTER
* 基类BaseConverter类注解@AutoGenerated中指定uuid规则${DTO在TOCO中的uuid}|DTO|BASE_CONVERTER
- **例子:**
* 如UserDto、UserDtoManager、UserDtoConverter extends UserDtoBaseConverter、UserDtoService(名称为${DtoName}Service内部包含getBy${PrimaryKey},getBy${PrimaryKey}s等方法。如Dto为UserBaseDto则生成类名为UserBaseDtoService
* 如UserDto、UserDtoManager、UserDtoConverter extends UserDtoBaseConverter、UserDtoService名称为${DtoName}Service内部包含getBy${PrimaryKey}getBy${PrimaryKey}s等方法。如Dto为UserBaseDto则生成类名为UserBaseDtoService
- **修改建议:**
- 建议在Service与BaseConverter中进行代码扩展不建议修改结构定义文件和Manager文件。其中DTO的**自定义字段**由于不直接派生自Entity所以一般对应取数逻辑代码。通常如果涉及数据获取、计算和拼装,批量处理性能最好,所以代码位置**必须**放在BaseConverter中已自动生成的**列表**转换方法中批量取数组装如UserBaseDtoBaseConverter.convertUserToUserBaseDto(List<User>)或UserDtoBaseConverter.convertUserBaseDtoToUserDto(List<UserBaseDto>)
- 建议在Service与BaseConverter中扩展代码不建议修改结构定义文件和Manager文件。DTO的自定义字段不直接派生自Entity一般对应取数逻辑代码。涉及数据获取、计算和拼装批量处理性能最好代码位置必须放在BaseConverter中已自动生成的列表转换方法中批量取数组装如UserBaseDtoBaseConverter.convertUserToUserBaseDto(List<User>)或UserDtoBaseConverter.convertUserBaseDtoToUserDto(List<UserBaseDto>)
#### **2.7 视图对象 (VO)**
- **定义与用途:** 在TOCO中VO表达某个BaseDTO(如果用户指明派生源也可使用其他DTO)为派生源通过外键关系不断关联多个BaseDTO的数据结构。VO用于在视图层与前端之间进行数据传输往往被当做HTTP API的返回值、或读方案的返回值使用由服务端返回至前端。注意VO不能作为接口的参数也不能作为RPC的返回值。
@@ -1153,154 +1166,154 @@ DateTime endTime //会议结束时间
}
</code>
#### **2.12 业务变更传输对象(BTO)**
- **定义与用途:** 在TOCO中BTO为写方案自动生成的参数结构每个写方案会生成一个BTO。BTO为写方案选定的操作实体根据关系形成的树形集合最外层为聚合根。写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要操作的实体字段值完成对数据库的写操作
- **如何创建/生成:** BTO只能由写方案自动创建不能单独新建任何BTO。在创建写方案后TOCO会自动生成一个BTO作为该写方案传入的参数结构。
- **关键配置:** 名称(${WritePlanName}Bto驼峰展示嵌套的树形实体和字段列表BTO内部的字段全部都来自Entity。以下为一个示例
<code>
class CreateUserBto { //对应实体user
Long id; //来自于user.id
String name; //来自于user.name
List<PictureBto> pictureList;
class PictureBto { //对应实体picture
String url; //来自于picture.url
}
}
</code>
- **与API的关系:** BTO通常可作为API的参数API接收到参数后可直接透传给内部的RPC进行调用注意BTO只用作写操作的参数**禁止用作查询参数结构**
- **复杂场景处理:** 对于涉及多个写方案的复杂API可以
a. 如果接口有一个主要的写场景则创建一个主要的写方案让其自动生成的BTO作为API参数然后再增加其他基本类型的参数或EO
b. 使用基本类型参数或EO作为参数在接口执行流程中将基本类型参数转换为各个写方案自动生成的BTO再调用多个写方案
c. 如果流程过于复杂优先考虑使用基本类型参数或EO
#### **2.13 服务层方法 (RPC)**
- **定义与用途:** 在TOCO中RPC为服务层的方法。RPC按照可见性可以分为两种一种是公开RPC可以被其他模块订阅订阅后可以通过RPC适配器进行调用另一种是非公开RPC只能被当前模块调用。非公开RPC可以被公开从而被其他模块订阅并调用
- **如何创建/生成:** RPC有4种创建方式a.DTO创建后会自动创建RPCRPC的公开性与DTO的公开性保持一致b.返回DTO的读方案会根据分页情况、以及是否生成计数函数的配置自动生成非公开的RPC c.写方案创建后会自动生成非公开的RPC返回值为操作的聚合根实体记录的主键字段的值 d.如果上述三种RPC无法满足需求则可以通过TOCO创建自定义RPC完整指定功能需指定具体的参数和返回值以及公开性等。
- **优先复用:** 当用户需要创建一个RPC时如果用户有明确要求创建的方式则按照用户的要求来创建。如果没有明确要求则通常先判断是否可以通过创建读方案、写方案、DTO来使TOCO自动创建出对应的RPC最后再考虑通过TOCO创建自定义RPC
- **自定义RPC和代码中手写方法的关系** 二者都可以通过手动的方式实现一个服务层的方法应用场景的区别在于如果一个方法需要被其他模块订阅则通常使用自定义RPC如果一个方法只是某个API私有调用不需要给外部模块开放则可以使用代码手写方法
- **关键配置:** 类名(驼峰首字母大写以Service结尾)、是否公开、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个RPC是分页查询且参数为Qto类型则Qto中已经包含了分页所需的from、size、scrollId等属性无需额外为RPC增加类似参数。
- **TOCO中RPC的存储:** 注意RPC在TOCO中只存储了方法签名不存储内部的执行流程逻辑如果需要了解其内部的实现逻辑则需要通过阅读RPC对应的代码。
- **参数类型:** RPC的参数**只能**为QTO、BTO、Enum、基本类型可为单值或列表。注意如果是对象类型则优先使用QTO、BTO作为参数**禁止使用**VO和自定义结构如Object
- **返回值类型:** RPC的返回值**只能**为DTO、Enum、基本类型,可为单值或列表,**禁止使用**VO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型则优先使用DTO作为返回值
- **TOCO中json结构描述:** 在TOCO中DTO使用一个json结构表示示例如下
```json
{
"methodName": "saveUsers",
"className": "UserSaveService",
"requestParams":[
{
"name": "saveUserBtoList",
"description": "批量保存用户参数",
"type": "List",
"innerType": "Bto",
"innerUuid": "dbvvc4d4-0063-442f-abd7-vrfded656988"
}
],
"response": {
"type": "Boolean"
}
}
```
结构中一些关键字段描述如下:
requestParams为请求参数列表response为返回结构requestParams中每个参数和response的结构相同其中name为参数名;type为参数类型参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,Dto,Qto,Bto,List,PageResult,Void其中参数不能为Void和PageResult如果不需要返回值则type设置为Void如果返回值为分页查询的结果则type设置为PageResult且innerType必为Dto对应代码中的VSQueryResult<XxxDto>description为描述typeUuid为参数对应类结构的UUID当type为Enum、Eo、Dto时传入该对象的uuid当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuidinnerType为List内部类型当type为List或PageResult时包含该字段innerUuid为List内部类结构的UUID当type为List或PageResult且innerType为Enum、Eo、Dto时传入该对象的uuid当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。
- **生成代码:** RPC会在service层中生成类文件及实现函数包含DTO自动生成的RPC如UserDtoService.getById(主键为id)或StaffBaseDtoService.getByStaffId(主键为staff_id)、读写方案自动生成的RPC如UserDtoQueryService.queryByListQto、UserBOService.createUser、自定义RPC如UserCustomService.customMethod。特别注意公开的RPC才可被其他模块使用RPC被订阅后会生成RpcAdapter适配器其他模块通过RpcAdapter才可调用该方法。如Order模块订阅了User模块的UserDtoService.getById则会在Order模块中生成UserDtoServiceInOrderRpcAdapter.getById方法Order模块中的代码必须通过@Resource private UserDtoServiceInOrderRpcAdapter userDtoServiceInOrderRpcAdapter;注入适配器后才可进行方法调用。这里**必须要注意**:变量的命名必须是类名的首字母小写,禁止使用其他变量名。
- **修改建议:** 建议修改RPC方法不建议修改RPC方法签名、适配器中的内容
#### **2.14 应用程序接口 (API)**
- **定义与用途:** 在TOCO中API用于定义对外暴露的HTTP接口
- **如何创建/生成:** API一般为通过TOCO创建需指定具体的参数和返回值等。
- **TOCO中API的存储:** 注意API在TOCO中只存储了其URI和方法签名不存储内部的执行流程逻辑如果需要了解其内部的实现逻辑则需要通过阅读API对应的代码。
- **关键配置:** uri(加粗展示,一般为/api/${moduleName}/xxx如/api/user/create,全局唯一。如果用户有特殊命名规则的话以用户要求为准)、类名(以Controller结尾)、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个API是分页查询且参数为Qto类型则Qto中已经包含了分页所需的from、size、scrollId等属性无需额外为API增加类似参数。
- **参数类型:** API的参数**只能**为读方案自动生成的QTO、写方案自动生成的BTO、EO、Enum、基本类型可为单值或列表。注意如果是对象类型则优先使用读方案自动生成的QTO、写方案自动生成的BTO作为参数。**禁止使用**DTO和自定义结构如Object作为参数
- **返回值类型:** TOCO的API运行在自己的Java脚手架中脚手架会自动对API的返回值做一层对象包装code、message、data。所以在TOCO中API的返回值无需考虑返回码和错误信息只需考虑返回的数据本身。TOCO中API的返回值**只能**为VO、Enum、基本类型,可为单值或列表,**禁止使用**DTO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型则优先使用VO作为返回值。
- **TOCO中json结构描述:** 在TOCO中API使用一个json结构表示示例如下
```json
{
"methodName": "getUserMeetingList",
"className": "MeetingQueryController",
"usageScenario": "xxx",
"requestParams":[
{
"name": "getMeetingListByUserId74Qto",
"description": "查询参数",
"type": "Qto",
"uuid": "6351a4d4-0063-442f-abd7-a2df6d656988"
},
{
"name": "test",
"description": "是否为测试请求",
"type": "Boolean"
}
],
"response": {
"type": "List",
"innerType": "Vo",
"innerUuid": "1d58352b-5333-4509-aec2-1abc3fac9122"
}
}
```
结构中一些关键字段描述如下:
requestParams为请求参数列表response为返回结构requestParams中每个参数和response的结构相同其中name为参数名;type为参数类型参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,PageResult,Vo,Qto,Bto,Void其中参数不能为Void和PageResult如果不需要返回值则type设置为Void如果返回值为分页查询的结果则type设置为PageResult且innerType必为Vo对应代码中的VSQueryResult<XxxVo>description为描述typeUuid为参数对应类结构的UUID当type为Enum、Eo、Vo时传入该对象的uuid当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuidinnerType为List内部类型当type为List时包含该字段innerUuid为List或PageResult内部类结构的UUID当type为List或PageResult且innerType为Enum、Eo、Vo时传入该对象的uuid当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。
- **代码产物和修改建议**
- **生成代码:** API会在entrance层生成Controller以及对应的API方法
- **修改建议:** 建议修改API方法的实现内容禁止直接修改API方法签名、URI**注意**如需修改API定义名称、出入参数、URL需要通过修改API设计元素实现
#### **2.15 流程服务FunctionFlow)**
- **定义与用途:** TOCO针对复杂业务拆解定义了流程服务把一个复杂的业务过程根据业务逻辑的内聚性合并逻辑功能把流程分解成流程节点最终构造出一个类似工作流的逻辑流程最终实现复杂业务流程分解提升代码的可维护性。TOCO内嵌了流程引擎在Function_Flow生成代码后可以在流程引擎中执行
- **何时使用:**
- 如果一个API/RPC中涉及的写服务超过3个则**必须**使用流程服务
- 当用户要求使用流程服务
- **节点的封装:**
- 因为TOCO是一个面向数据处理的系统所以数据内聚为首要考虑因素一个节点通常围绕一个核心写服务包括取数的读服务为写服务的入参进行数据处理和转换以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。
- **关键配置:** 名称(小写字母+下划线),拆解复杂业务逻辑,如果业务流程比较简单,则不需要使用流程服务
- **如何创建/生成:**
- 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。
- 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递在写代码的时候按需修改Context文件添加所需的字段
- 定义了JSON结构用于表达逻辑流。流程节点分为 “顺序节点”、“条件节点”,“选择节点”,"开始节点“ ,节点之间通过有向边连接,表示逻辑的执行方向;从开始节点开始,可达各个节点。
- 节点的边的定义如下顺序节点可以有多条入边可以有多条出边一个出边表示下一个执行的节点多条出边表示几个分支并发执行条件节点可由多条入边可以有2条出边表示条件为True和False两种逻辑分支选择节点可以有多条入边可以有多条出边每条出边表示一种选择路径开始节点只能有一条出边不能有入边;
- ”条件节点“只封装条件判断逻辑返回TRUE或者FALSE”循环节点“只封装条件判断逻辑返回TRUE或者FALSE”选择节点“只封装分支选择逻辑返回下游分支节点的名称
- 终止流程:直接抛出异常
- 一般校验逻辑放在一个“顺序节点"中,如果校验失败,以异常的方式抛出
- 节点可以复用,同样的功能可以抽取出来封装到一个节点中
- **流程服务设计元素的表达:**
- 以Json表达Json Schema 定义如下:
```json
{
"type": "object","description": "流程服务设计元素", "required": [ "description","name","nodes","edges"],
"properties":
{
"name": { "type": "string", "description": "流程名称英语描述单词之间使用下划线分割总称不超过32个字符" },
"moduleName": {"type": "string", "description": "模块名称,创建流程时传入,指定流程服务所属的模块"},
"uuid": {"type": "string", "description": "流程服务设计元素的uuid创建流程不传入在更新的时候必须传入"},
"description": {"type": "string", "description": "流程服务描述, 总长度控制在200个字符内"},
"nodes": {
"type": "array", "description": "流程服务的节点列表",
"items": {
"type": "object","description": "流程服务节点对象", "required": ["name", "type", "description"],
"properties": {
"name": {"type": "string", "description": "节点名称, 英语描述单词之间使用下划线分割总称不超过32个字符"},
"type": {"type": "string", "description": "节点 类型,可以是 PROCESS_NODE(顺序节点、SWITCH_NODE(选择节点)、CONDITION_NODE(条件节点),START_NODE(开始节点)"},
"description": {"type": "string", "description": "描述节点的功能, 英语描述单词之间使用下划线分割总称不超过200个字符" }
#### **2.12 业务变更传输对象(BTO)**
- **定义与用途:** 在TOCO中BTO为写方案自动生成的参数结构每个写方案会生成一个BTO。BTO为写方案选定的操作实体根据关系形成的树形集合最外层为聚合根。写方案调用方按照BTO的结构向写方案生成的RPC方法传入需要操作的实体字段值完成对数据库的写操作
- **如何创建/生成:** BTO只能由写方案自动创建不能单独新建任何BTO。在创建写方案后TOCO会自动生成一个BTO作为该写方案传入的参数结构。
- **关键配置:** 名称(${WritePlanName}Bto驼峰展示嵌套的树形实体和字段列表BTO内部的字段全部都来自Entity。以下为一个示例
<code>
class CreateUserBto { //对应实体user
Long id; //来自于user.id
String name; //来自于user.name
List<PictureBto> pictureList;
class PictureBto { //对应实体picture
String url; //来自于picture.url
}
}
},
"edges": {
"type": "array", "description": "流程服务的边列表",
"items": {
"type": "object","description": "流程服务的边", "required": ["fromNode", "toNode"],
"properties": {
"fromNode": {"type": "string", "description": "边的开始节点名称"},
"toNode": {"type": "string", "description": "边的结束节点名称"},
"value": {"type": "boolean", "description": "可选作为条件节点的出边true值表示条件匹配的分支,false表示条件不匹配的分支; 作为循环节点的出边true值表示进入循环false值表示退出循环"}
}
}
}
}
}
```
- 例子:用户注册流程
<code>
{
"moduleName":"user",//该流程所属的模块
"name":"user_register",//定义该流程的功能
"description":"注册用户",//描述该流程的详细功能
</code>
- **与API的关系:** BTO通常可作为API的参数API接收到参数后可直接透传给内部的RPC进行调用注意BTO只用作写操作的参数**禁止用作查询参数结构**
- **复杂场景处理:** 对于涉及多个写方案的复杂API可以
a. 如果接口有一个主要的写场景则创建一个主要的写方案让其自动生成的BTO作为API参数然后再增加其他基本类型的参数或EO
b. 使用基本类型参数或EO作为参数在接口执行流程中将基本类型参数转换为各个写方案自动生成的BTO再调用多个写方案
c. 如果流程过于复杂优先考虑使用基本类型参数或EO
#### **2.13 服务层方法 (RPC)**
- **定义与用途:** 在TOCO中RPC为服务层的方法。RPC按照可见性可以分为两种一种是公开RPC可以被其他模块订阅订阅后可以通过RPC适配器进行调用另一种是非公开RPC只能被当前模块调用。非公开RPC可以被公开从而被其他模块订阅并调用
- **如何创建/生成:** RPC有4种创建方式a.DTO创建后会自动创建RPCRPC的公开性与DTO的公开性保持一致b.返回DTO的读方案会根据分页情况、以及是否生成计数函数的配置自动生成非公开的RPC c.写方案创建后会自动生成非公开的RPC返回值为操作的聚合根实体记录的主键字段的值 d.如果上述三种RPC无法满足需求则可以通过TOCO创建自定义RPC完整指定功能需指定具体的参数和返回值以及公开性等。
- **优先复用:** 当用户需要创建一个RPC时如果用户有明确要求创建的方式则按照用户的要求来创建。如果没有明确要求则通常先判断是否可以通过创建读方案、写方案、DTO来使TOCO自动创建出对应的RPC最后再考虑通过TOCO创建自定义RPC
- **自定义RPC和代码中手写方法的关系** 二者都可以通过手动的方式实现一个服务层的方法应用场景的区别在于如果一个方法需要被其他模块订阅则通常使用自定义RPC如果一个方法只是某个API私有调用不需要给外部模块开放则可以使用代码手写方法
- **关键配置:** 类名(驼峰首字母大写以Service结尾)、是否公开、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个RPC是分页查询且参数为Qto类型则Qto中已经包含了分页所需的from、size、scrollId等属性无需额外为RPC增加类似参数。
- **TOCO中RPC的存储:** 注意RPC在TOCO中只存储了方法签名不存储内部的执行流程逻辑如果需要了解其内部的实现逻辑则需要通过阅读RPC对应的代码。
- **参数类型:** RPC的参数**只能**为QTO、BTO、Enum、基本类型可为单值或列表。注意如果是对象类型则优先使用QTO、BTO作为参数**禁止使用**VO和自定义结构如Object
- **返回值类型:** RPC的返回值**只能**为DTO、Enum、基本类型,可为单值或列表,**禁止使用**VO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型则优先使用DTO作为返回值
- **TOCO中json结构描述:** 在TOCO中DTO使用一个json结构表示示例如下
```json
{
"methodName": "saveUsers",
"className": "UserSaveService",
"requestParams":[
{
"name": "saveUserBtoList",
"description": "批量保存用户参数",
"type": "List",
"innerType": "Bto",
"innerUuid": "dbvvc4d4-0063-442f-abd7-vrfded656988"
}
],
"response": {
"type": "Boolean"
}
}
```
结构中一些关键字段描述如下:
requestParams为请求参数列表response为返回结构requestParams中每个参数和response的结构相同其中name为参数名;type为参数类型参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,Dto,Qto,Bto,List,PageResult,Void其中参数不能为Void和PageResult如果不需要返回值则type设置为Void如果返回值为分页查询的结果则type设置为PageResult且innerType必为Dto对应代码中的VSQueryResult<XxxDto>description为描述typeUuid为参数对应类结构的UUID当type为Enum、Eo、Dto时传入该对象的uuid当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuidinnerType为List内部类型当type为List或PageResult时包含该字段innerUuid为List内部类结构的UUID当type为List或PageResult且innerType为Enum、Eo、Dto时传入该对象的uuid当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。
- **生成代码:** RPC会在service层中生成类文件及实现函数包含DTO自动生成的RPC如UserDtoService.getById(主键为id)或StaffBaseDtoService.getByStaffId(主键为staff_id)、读写方案自动生成的RPC如UserDtoQueryService.queryByListQto、UserBOService.createUser、自定义RPC如UserCustomService.customMethod。特别注意公开的RPC才可被其他模块使用RPC被订阅后会生成RpcAdapter适配器其他模块通过RpcAdapter才可调用该方法。如Order模块订阅了User模块的UserDtoService.getById则会在Order模块中生成UserDtoServiceInOrderRpcAdapter.getById方法Order模块中的代码必须通过@Resource private UserDtoServiceInOrderRpcAdapter userDtoServiceInOrderRpcAdapter;注入适配器后才可进行方法调用。这里**必须要注意**:变量的命名必须是类名的首字母小写,禁止使用其他变量名。
- **修改建议:** 建议修改RPC方法不建议修改RPC方法签名、适配器中的内容
#### **2.14 应用程序接口 (API)**
- **定义与用途:** 在TOCO中API用于定义对外暴露的HTTP接口
- **如何创建/生成:** API一般为通过TOCO创建需指定具体的参数和返回值等。
- **TOCO中API的存储:** 注意API在TOCO中只存储了其URI和方法签名不存储内部的执行流程逻辑如果需要了解其内部的实现逻辑则需要通过阅读API对应的代码。
- **关键配置:** uri(加粗展示,一般为/api/${moduleName}/xxx如/api/user/create,全局唯一。如果用户有特殊命名规则的话以用户要求为准)、类名(以Controller结尾)、方法名(驼峰,首字母小写)、请求参数、返回值。注意如果一个API是分页查询且参数为Qto类型则Qto中已经包含了分页所需的from、size、scrollId等属性无需额外为API增加类似参数。
- **参数类型:** API的参数**只能**为读方案自动生成的QTO、写方案自动生成的BTO、EO、Enum、基本类型可为单值或列表。注意如果是对象类型则优先使用读方案自动生成的QTO、写方案自动生成的BTO作为参数。**禁止使用**DTO和自定义结构如Object作为参数
- **返回值类型:** TOCO的API运行在自己的Java脚手架中脚手架会自动对API的返回值做一层对象包装code、message、data。所以在TOCO中API的返回值无需考虑返回码和错误信息只需考虑返回的数据本身。TOCO中API的返回值**只能**为VO、Enum、基本类型,可为单值或列表,**禁止使用**DTO、QTO、BTO、自定义结构如Object作为返回值。注意如果是对象类型则优先使用VO作为返回值。
- **TOCO中json结构描述:** 在TOCO中API使用一个json结构表示示例如下
```json
{
"methodName": "getUserMeetingList",
"className": "MeetingQueryController",
"usageScenario": "xxx",
"requestParams":[
{
"name": "getMeetingListByUserId74Qto",
"description": "查询参数",
"type": "Qto",
"uuid": "6351a4d4-0063-442f-abd7-a2df6d656988"
},
{
"name": "test",
"description": "是否为测试请求",
"type": "Boolean"
}
],
"response": {
"type": "List",
"innerType": "Vo",
"innerUuid": "1d58352b-5333-4509-aec2-1abc3fac9122"
}
}
```
结构中一些关键字段描述如下:
requestParams为请求参数列表response为返回结构requestParams中每个参数和response的结构相同其中name为参数名;type为参数类型参数类型取值范围为Boolean,String,Integer,Long,Float,Double,BigDecimal,Date,ByteArray,Enum,Eo,List,PageResult,Vo,Qto,Bto,Void其中参数不能为Void和PageResult如果不需要返回值则type设置为Void如果返回值为分页查询的结果则type设置为PageResult且innerType必为Vo对应代码中的VSQueryResult<XxxVo>description为描述typeUuid为参数对应类结构的UUID当type为Enum、Eo、Vo时传入该对象的uuid当type为Qto时传入对应读方案的uuid、当type为Bto时传入对应写方案的uuidinnerType为List内部类型当type为List时包含该字段innerUuid为List或PageResult内部类结构的UUID当type为List或PageResult且innerType为Enum、Eo、Vo时传入该对象的uuid当innerType为Qto时传入对应读方案的uuid、当innerType为Bto时传入对应写方案的uuid。
- **代码产物和修改建议**
- **生成代码:** API会在entrance层生成Controller以及对应的API方法
- **修改建议:** 建议修改API方法的实现内容禁止直接修改API方法签名、URI**注意**如需修改API定义名称、出入参数、URL需要通过修改API设计元素实现
#### **2.15 流程服务FunctionFlow)**
- **定义与用途:** TOCO针对复杂业务拆解定义了流程服务把一个复杂的业务过程根据业务逻辑的内聚性合并逻辑功能把流程分解成流程节点最终构造出一个类似工作流的逻辑流程最终实现复杂业务流程分解提升代码的可维护性。TOCO内嵌了流程引擎在Function_Flow生成代码后可以在流程引擎中执行
- **何时使用:**
- 如果一个API/RPC中涉及的写服务超过3个则**必须**使用流程服务
- 当用户要求使用流程服务
- **节点的封装:**
- 因为TOCO是一个面向数据处理的系统所以数据内聚为首要考虑因素一个节点通常围绕一个核心写服务包括取数的读服务为写服务的入参进行数据处理和转换以及该写服务完成后的一些附属功能, 除了条件节点,入参校验,最终数据返回节点,每个节点至少包括一个写服务。
- **关键配置:** 名称(小写字母+下划线),拆解复杂业务逻辑,如果业务流程比较简单,则不需要使用流程服务
- **如何创建/生成:**
- 流程不是逻辑的伪代码,不需要表达全部逻辑细节,相关有内聚性,相似性的功能逻辑需要被封装到一个流程节点内;例如用户注册:创建用户需要,需要创建账号信息,需要创建用户信息,并且发送一个通知消息,这几个功能都属于创建用户相关信息,而且没有逻辑分叉,因此可以内聚在一个逻辑节点内。
- 流程节点之间的参数和返回值的传递通过一个统一的上下文Context传递在写代码的时候按需修改Context文件添加所需的字段
- 定义了JSON结构用于表达逻辑流。流程节点分为 “顺序节点”、“条件节点”,“选择节点”,"开始节点“ ,节点之间通过有向边连接,表示逻辑的执行方向;从开始节点开始,可达各个节点。
- 节点的边的定义如下顺序节点可以有多条入边可以有多条出边一个出边表示下一个执行的节点多条出边表示几个分支并发执行条件节点可由多条入边可以有2条出边表示条件为True和False两种逻辑分支选择节点可以有多条入边可以有多条出边每条出边表示一种选择路径开始节点只能有一条出边不能有入边;
- ”条件节点“只封装条件判断逻辑返回TRUE或者FALSE”循环节点“只封装条件判断逻辑返回TRUE或者FALSE”选择节点“只封装分支选择逻辑返回下游分支节点的名称
- 终止流程:直接抛出异常
- 一般校验逻辑放在一个“顺序节点"中,如果校验失败,以异常的方式抛出
- 节点可以复用,同样的功能可以抽取出来封装到一个节点中
- **流程服务设计元素的表达:**
- 以Json表达Json Schema 定义如下:
```json
{
"type": "object","description": "流程服务设计元素", "required": [ "description","name","nodes","edges"],
"properties":
{
"name": { "type": "string", "description": "流程名称英语描述单词之间使用下划线分割总称不超过32个字符" },
"moduleName": {"type": "string", "description": "模块名称,创建流程时传入,指定流程服务所属的模块"},
"uuid": {"type": "string", "description": "流程服务设计元素的uuid创建流程不传入在更新的时候必须传入"},
"description": {"type": "string", "description": "流程服务描述, 总长度控制在200个字符内"},
"nodes": {
"type": "array", "description": "流程服务的节点列表",
"items": {
"type": "object","description": "流程服务节点对象", "required": ["name", "type", "description"],
"properties": {
"name": {"type": "string", "description": "节点名称, 英语描述单词之间使用下划线分割总称不超过32个字符"},
"type": {"type": "string", "description": "节点 类型,可以是 PROCESS_NODE(顺序节点、SWITCH_NODE(选择节点)、CONDITION_NODE(条件节点),START_NODE(开始节点)"},
"description": {"type": "string", "description": "描述节点的功能, 英语描述单词之间使用下划线分割总称不超过200个字符" }
}
}
},
"edges": {
"type": "array", "description": "流程服务的边列表",
"items": {
"type": "object","description": "流程服务的边", "required": ["fromNode", "toNode"],
"properties": {
"fromNode": {"type": "string", "description": "边的开始节点名称"},
"toNode": {"type": "string", "description": "边的结束节点名称"},
"value": {"type": "boolean", "description": "可选作为条件节点的出边true值表示条件匹配的分支,false表示条件不匹配的分支; 作为循环节点的出边true值表示进入循环false值表示退出循环"}
}
}
}
}
}
```
- 例子:用户注册流程
<code>
{
"moduleName":"user",//该流程所属的模块
"name":"user_register",//定义该流程的功能
"description":"注册用户",//描述该流程的详细功能
"nodes":[ // 定义该流程包含的节点
{
"name":"start",