后端开发
后端工程规范实践
依赖引入
-
为确保依赖版本的统一管理和维护,所有依赖包均在根工程
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上线状态观察者,等待定时执行观察者等