后端开发
后端工程规范实践
依赖引入
-
为确保依赖版本的统一管理和维护,所有依赖包均在根工程
innospots-root的 POM 文件中进行集中定义。具体而言,依赖包的版本号统一在 properties 变量中声明,其他子模块在引入依赖时不允许直接指定版本号。 -
项目中所需的所有第三方 jar 包依赖都必须在 dependencyManagement 中进行统一声明和管理。这样可以有效控制依赖版本,避免版本冲突。
-
对于包含子模块的父模块,其 packaging 类型需设置为 pom。在父模块的 dependencyManagement 中统一定义子模块所需的依赖包。如果某个依赖被所有子模块共用,则可以直接在父模块的 dependencies 中声明,子模块将自动继承。
命名规范
模块命名规范
- 工程模块全部以
innospots-开头 - 工程模块名称使用
kebab-case命名法,单词间以中划线分隔,单词小写 - 工程模块名称使用2-4个单词命名,最多不超过5个
- 命名方式上体现模块间的上下级关系,例如:
innospots-extension模块下的所有子模块全部以innospots-extension-*开头
包命名规范
- 包的命名方式采用两种方式: 一种为基于MVC架构的包命名方式,按数据库层,服务层,接口层,模型层,实体层命名,常用于做管理平台类工程或模块使用。一种为基于业务领域,功能模块的命名方式,按包中的业务功能或支撑功能划分包结构。
- 包名尽量使用完整单词或行业通用缩写词,不建议使用两个单词定义包名
- 在语义明确的前提下,尽量精简,做到见名知意
- 包名称命名全部小写
以下为通用功能下的常用包名定义
configuration, Spring Configuration,自定义Propertiescontroller, 管理平台端Restful API接口constant, 常量值类entity, 数据库表的实体POJOmodel, 业务类模型POJO,与实体POJO有对应关系dao, 数据库操作,返回实体类operator, 在管理端对数据库的CURD的操作,对controller层提供实际数据操作实现,包含参数校验,简单的数据层面的逻辑处理mapper, mapstruct的POJO转换Mapper,entity的POJO和业务模型的POJO的转换service, 复杂业务逻辑,多模块的operator的聚合调用enums, 枚举类型定义exception, 业务异常类utils, 工具类定义,日期,字符串转换,国际化,Bean拷贝等endpoint, 服务类APIfilter, Servlet Filter的自定义实现类starter, Spring Boot 启动后调用的ApplicationRunner实现类server, 应用服务启动主类,或嵌入服务event, 事件定义,基于ApplicationEvent,或自定义Event的实现interceptor, Spring MVC 的interceptor的自定义实现类aspect, 基于Spring AOP的拦截定义listener, event的监听器loader, 资源加载,数据加载
类命名规范
- 符合Java类基本命名规范
- 采用驼峰式命名
- 类名称通常采用主语词+核心功能后缀词的方式定义,采用名词作为后缀词,表明当前类的核心用途,可体现出层级
- 除了业务POJO外,类名需要使用两个以上单词定义,避免使用单个通用性单词定义类名,例如:Config,Status等,更好方式应该定义为DataConfig,TaskStatus等更加语义明确的名称定义
- 类注释上必须包含作者,版本号,时间日期,以及类的用途说明
常用类名词参考
**Entity, 数据库表实体POJO**Dao, 数据库ORM框架中的实体操作类,集成Mybatis Plus的BaseMapper的数据访问类**Controller, 管理平台Restful API接口**Filter, 管理平台端Servlet javax.servlet.Filter的实现类**Interceptor, 管理平台端 Spring Web中的HandlerInterceptor的实现类**Operator, CURD数据的操作类**Service, 跨模块,多Operator的业务服务实现**Mapper, mapstruct的Mapper,实体POJO与业务模型POJO的转换**Handler, 在业务责任链模式中的输入数据的接收者和业务处理**Encoder, 对数据的编码或加密**Decoder, 对数据的解码或解密**Utils, 工具类,静态类**Loader, 数据加载,资源加载**Operator, 数据的业务模型层的CRUD操作**Endpoint, 服务类型的API接口**Exception, 业务异常,继承RuntimeException**Configuration, Spring Configuration 定义**Properties, Spring Properties的定义**Watcher, 观察者,定时执行,监听执行**Manager, 业务资源管理类,用于资源加载到Manager中,由Manager负责业务资源的处理和缓存**Builder, 构造者模式的构造器,用于创建新的对象和数据**Listener, Spring Application Event事件的监听器**Perparer, 资源预备器,做业务逻辑预先准备使用**Starter, Spring ApplicationRunner接口实现,用于启动加载资源,初始化使用**Matcher, 匹配器,用于执行匹配,例如:UrlPattern的疲累**Generator, 生成器,随机数,主键,字符串等生成**Converter, 数据转换处理**Server, 服务启动主入口**Store, 数据存储**Reader, 数据只读功能**Receiver, 消息队列接收者**Sender, 消息队列发送者**Engine, 执行引擎**Factory, 工厂模式功能类**Facade, 门面模式功能类**Executor, 执行器**Event, Spring ApplicationEvent 自定义事件**Status, 枚举类,业务状态的定义**Type, 枚举类,业务类型定义**Context, 执行上下文,数据上下文**Execution, 执行记录,任务执行,流程执行**Base, 业务模型基础类,用于做扩展集成**Exporter, 数据导出,资源导出**Importer, 基于注解的 Spring Configuration bean的集成导入定义**Container, 业务执行容器**Job, 调度任务定义Base**, 抽象类,基础公共类定义
方法命名规范
- 方法命名主要采用动词+名词的命名规则,对业务性明确的类中可以直接使用动词做方法名称
以下为常用动词方案
fetch,接口获取数据load, 页面加载build,模型构造,对象构建change,状态变更cancel,动作撤销send, 监听模式的发送事件receive,接收事件动作create,新建操作update,modify,更新操作delete,删除操作read,读取数据操作,包含接口获取,read方法主要用于只读功能类中的操作listen,监听事件find,query,select,查询list或数组中的数据,查询数据库数据,可有多种形式的查询条件,着重不同参数条件的查询filter,对某个数据,按条件过滤extract,抽取操作format,将某对象格式化为固定格式字符串parse,将字符串解析为对象list,返回列表数据,无条件或者条件固定,着重返回的数据,弱化查询条件get,获取模型字段值,获取单一模型数据set,设置模型字段值publish,发布动作save,create或者updaterecord,插入数据,用于日志数据,记录型数据refresh,刷新show,展示显示操作,无翻页clean,清除数据,清除状态remove,移除动作,非物理删除generate,创建生成对象,生成随机数等check,检查值,检查合法性start,开始启动,模块功能启动stop,停止服务,停止线程open,打开配置,打开流,打开资源close,关闭资源prepare,预处理准备compile,编译动作run,execute,执行动作initialize,初始化copy,拷贝对象,拷贝数组fill,填充数据,基于参数数据填充add,添加数据到列表或mapcontain,是否包含数据match,匹配判断
接口规范
- 管理平台类Controller接口主要采用Restful风格规范定义
- 使用Spring MVC的PostMapping,DeleteMapping,PutMapping,GetMapping定义对资源的增删改查
- Controller使用OpenAPI 3.0定义接口描述和参数信息
- 对查询条件类字段使用Param类参数
- 对业务操作类的API需要在URI中增加操作词说明,例如:online,offline,execute等
- 使用javax.validation来验证输入参数的有效性和合法性,在参数的输入model中包含验证规则注解
- 在
io.innospots.base.constant.PathConstant定义ROOT_PATH为管理端API的contextPath,所有涉及管理平台端权限管控的API都需要以此常量作为contextPath路径设置 - API Path路径命名使用
kebab-case命名法,单词间以中划线分隔,单词小写 - 服务类接口定义在endpoint包中,服务类接口只使用POST方法和GET方法定义接口操作
配置规范
application.yaml, 服务的基础配置,不跟随环境进行变更application-xxx.yml, 按不同的环境的配置,主要包含数据库的配置,系统变量的配置application-security.yml, 权限拦截,授权key配置
开发实践
管理模块
Innospots提供通用管理平台Libra,包含最小化的管理平台功能支撑包括:工作台,通知消息,权限设置,菜单设置,国际化,系统配置管理以及数据看板
后端模块结构采用MVC结构模式,每个模块都有独立的MVC结构。管理平台的功能扩展通过扩展应用的形式增加平台功能扩展
controller, 模块API接口,以资源为粒度定义api接口的controllerdao, 物理数据表entity的CRUD功能,对数据表关联较强的场景,可在dao中单独定义连表查询方法,以注释的方式扩展连表查询entity, 数据库实体表,按物理表对应,每个物理表对应一个entity的POJOmapper, 业务模型和实体POJO的转换mapper,这里使用mapstruct做POJO的相互转换model, 业务模型的POJOoperator, 数据资源的CRUD功能,对数据字段的有效性进行验证,在无表关联时,通常一个operator引用一个dao,对数据表业务关联关系较强的operator中可引用使用多个daoservice, 在包含负责的业务逻辑操作,需要在一个模块中跨多个operator完成时,可单独定义业务类的service,此service将引入多个operator处理复杂的业务逻辑,包含业务逻辑类的数据验证及判断
模块间交互
- 【service层集成】当有业务场景需要多个模块的功能时可在上层
service上引入不同模块的operator或service实现多模块的功能操作 - 【数据事件通知】通过Spring Event实现在跨模块间的数据交互,当一个模块中的业务逻辑涉及到跨模块的业务交互时,使用event机制实现数据交互。例如:菜单操作关闭,对应的菜单权限的设置关闭
菜单、权限与操作日志
在管理端的API接口的Controller上定义接口的权限和菜单定义,每一个API接口对应Controller的一个方法,在Controller类和方法上增加注解项,明确定义API接口的菜单项,应页面端的操作以及操作日志
- 在Controller类上定义菜单项【ModuleMenu】,key值为
innospots-application-meta.json中modules的itemKey值,表明当前controller对应的模块操作项 - 多个Controller对应一个菜单Module时,ModuleMenu设置相同的key值即可
- 在Controller的方法上添加【ResourceItemOperation】的操作注解,标识菜单的操作接口,用于在菜单权限中设置操作权限配置,操作项不需要设置只读的接口
- 在Controller的方法上添加【OperationLog】注解用于操作日志的记录,增加
OperationLog的注解方法将在操作日志中记录操作日志
POJO
- 管理平台端定义了两类POJO,一类为Entity,一类为业务Model
- Entity为数据表结构映射,使用常量
TABLE_NAME定义对应的表名称 - 在Entity的成员中使用了
javax.persistence.Column注解标注数据表字段的类型,长度,主键信息,但系统的ORM并不使用Hibernate或JPA做ORM使用 - 业务POJO用于接口返回数据结构
- POJO中使用
Lombok的@Getter和@Setter定义属性的操作,POJO中,不使用Lombok的其他注解
异常以及异常码
innospots-base的exception中定义系统常用异常类io.innospots.base.exception.BaseException为系统定义的基础异常类,所有其他系统异常继承此异常类- 业务自定义异常的方式使用
build静态方法创建异常类实例,可参见业务异常类中的build构建实例 ErrorResponse作为异常异常的时的数据返回,code,message,detail包含当前的异常信息GlobalExceptionHandler作为全局异常处理类,将拦截所有接口层面抛出的异常io.innospots.base.model.response.ResponseCode中定义了系统状态码code的定义
数据操作
- 本系统使用
Mybatis Plus作为基础dao模块的ORM操作类 - 业务dao接口继承
com.baomidou.mybatisplus.core.mapper.BaseMapper, 将继承对公共资源CRUD以及列表查询操作 - 业务操作定义在
operator中,继承com.baomidou.mybatisplus.extension.service.impl.ServiceImpl,将继承常用业务资源的CRUD功能
模块依赖引入
- 系统模块采用主动引入的方式将不同模块的'Spring Bean'引入到当前工程模块
- 在每个模块的基础包定义模块的
**Importer注解,此Importer将引入当前模块中所定义配置的'Spring Bean'引入到当前服务中 - 在
Importer中将明确的定义,Spring ComponentScan的生效的包范围,以及使用的Spring Configuration,在Configuration中明确定义模块中所使用的Spring Bean定义
事件总线
- 事件总线用于跨模块,跨系统,异步等场景使用,解耦和提升系统处理性能
io.innospots.base.events中为系统自定义使用的事件处理模块- 对管理平台中,系统使用自定义的EventBus和对应的EventListener作为事件处理体系
观察者线程
- 在
innospots-base模块watcher包中定义观察者线程 io.innospots.base.watcher.WatcherSupervisor观察者线程池, 系统中定义的观察者线程通过registry方法添加观察者线程的注册,在模块中定义ApplicationRunner中将此模块中的观察者线程添加到- 观察者线程由观察者线程池和观察者线程组成,观察者线程将以定时轮询的方式常驻内存执行,用于检查,定时更新等操作
- 系统中定义了了多种功能的观察者,包括:服务注册观察者,Workflow上线状态观察者,等待定时执行观察者等