中台技术标准
一、工程结构
统一使用sofaboot的sofa分层方式,SOFA 分层是沿袭自蚂蚁 SOFA 工程的传统分层,蚂蚁传统 SOFA 应用通常基于模块化形式进行开发,解决了模块化开发下各模块相互影响的问题。
工程结构如下:
+appname
|—— app (应用目录)
|———— common (基础结构层)
|—————— common-util (公共常量,枚举,异常,日志,Log以及工具类等)
|—————— common-dal (ZDAL配置,SqlMap,DO,DAO接口以及实现)
|—————— common-service-facade (对外暴露的API,DTO,Request,Result等)
|—————— common-service-integration (调用外部服务的Wrapper或者Client等)
|———— core (核心/原子服务层)
|—————— core-model (领域模型,支持校验,追踪,环境上下文等)
|—————— core-service (核心/原子服务,领域模型的仓储,缓存,转换器)
|———— biz (业务逻辑层)
|—————— biz-shared (业务逻辑,入参xxxReq,返回WelfareResult包裹DTO)
|—————— biz-service (对外暴露的API实现类,入参xxxReq,返回DTO)
|———— test (测试层,单元测试的基类,Test Case和Starter)
|—— conf (SOFA 相关配置存放目录,包括sofa, log4j等配置)
|—— pom.xml (总POM文件)
SOFA 分层对应的模块依赖如下图所示:
test 层(测试层)
该层是 SOFABoot 项目中测试模块,提供了单元测试的基类,支持继承或扩展。由于要对所有模块进行测试,因此该层位于 SOFABoot 系统最顶端,通过直接和间接依赖,可以访问到每个模块的代码,即所有模块对测试层都是可见的。
bootstrap 层(启动层)
该层是 SOFABoot 项目中的启动模块,该模块中会包含 SOFABoot 应用的启动类、同时配置 SOFABoot 打包等。该层通过直接和间接依赖,可以访问到除了测试模块外每个模块的代码。
biz 层(业务应用层)
biz-service-impl
biz-service-impl是facade层的实现,是接口的入口,该层要尽量的薄,不要掺杂太多的业务逻辑,对应的业务逻辑放在shared层进行组装,任何需要在入口做的非业务逻辑都应该放在biz-service-impl用统一的模版进行处理
- 参数校验
- 数据库本地事务控制
- 服务调用,调用shared层对应服务
- 组装返回结果,将shared返回结果通过WelfareResult进行统一包装(返回结果=VO1,VO2+返回码+描述)
- 捕获异常
- 日志出入参打印
- 数据埋点
- 其他
biz-shared
biz-shared 层相当于领域驱动设计中的应用层,位于领域层之上,调用的是领域服务,使用的是领域模型,自己则专注于具体应用所需要的逻辑处理,而不包含核心业务规则,更多的是给领域层需要协作的各个领域服务协调任务、委派工作。
- 该层业务服务类以 Manager 结尾
- 业务/数据 权限控制在此严格控制,考虑每一次访问是否真的具有对该数据的查看或操作权限
- 业务写在对应的业务bundle中
- 实现相对复杂的业务,复用性较低的业务
例如:复权因子计算、业绩计算、行情排行等对多个数据进行聚合或计算;拼装多个业务逻辑
- 相对完整、独立的,且有可能被复用的模块业务,或者一些通用的业务工具类,可以抽象到core-service层
例如:查询人员、审核模块、日期处理类、规则引擎 - 入参是基本类型或者xxxreqDTO对象、输出结果是没有被WelfareResult包裹的DTO数据
代码示例
/** * 文档manager * * @author yutao create on 2021/12/9 11:27 */ public interface AlFileManager { /** * 批量查询接口 */ List<FileInfoDTO> fileListByBizId(AlFileQueryByBizIdReqDTO request); /** * 单个查询接口 */ <FileInfoDTO> fileGetById(AlFileGetByIdReqDTO request); /** * 文档保存 * @return 文档id */ String fileSave(AlFileSaveReqDTO req); /** * 批量保存 * @param req * @return */ List<String> fileBatchSave(AlFileBatchSaveReqDTO req); /** * 修改文档 * @param request * @return */ Void fileModify(AlFileModifyReqDTO request); }
core 层(核心领域层)
core-model
core-model 模块包含领域层各个模型对象,模型命名以Model结尾,一个实体可以有多个Model
core-service
core-service模块封装核心业务,该层主要有三种类
- xxxService:领域服务类,要求原子化程度高,通用性强,服务较稳定,改动频率较低,根据是否需要缓存确认继承cacheableBaseService,对数据进行操作时(增、改、删)时应更新缓存,保障缓存一致性。
- xxxConverter:模型转化类,负责model->DO, DO->model, model->DTO/VO, DTO/VO->model的转化
- xxxRepository:数据仓库类(可选),复杂数据源场景,数据库操作与服务分离,区分服务与仓库。当一个模型需要通过多个数据源组装,应该将多个数据源的聚合工作放到Repository中,如果只是单纯操作单一数据源(如DB)可以不用添加Repository
common 层(基础结构层)
在 Common 层中包含了为系统提供基础服务的各个模块:
common-service-facade (外观层)
facade 层是应用对外提供的接口层,门面模式在sofa项目中的具体体现。facade 层用于提供接口描述文件(xxxxFacade,DTO),不包含任何业务逻辑(只有接口信息,比如接口定义,接口请求模型和接口返回模型,接口涉及到的enum枚举),该模块使用单独的版本号,需要单独发布打包。
- 请求类命名格式为:com.alipay.{项目名称}.common.service.facade.{模块名称}.request.xxxxxreq,命名须明确清晰的解释出此接口的作用及用途(格式:操作对象+操作动作+Req)请求都以req结尾,强制要求继承ToString,必须要有serialVersionUID字段,如果请求里面只有一个字段也需要遵守此规范
- 禁止facade接口含有多个参数(所有的请求都由一个复合对象包裹,理论上来说一个接口一个请求对象)
- 返回类命名格式为:由统一的WelfareResult包装,对外展示的对象都以DTO结尾,必须继承ToString,必须要有serialVersionUID字段
- 所有tr接口类都以facade结尾,查询类接口与操作类接口分离,方便后期分zong发布路由,文件查询类与文件操作类的接口分开为FileOperateFacade和FileQueryFacade,考虑到query流量可能较大,后期可能发布czone
common-dal
使用mybaties-generator
- sofa项目初始化地址,sofa boot的项目初始化的时候可以选择Zdal+MyBatis Framework
-
- 📎sofaboot+zdal+mybatiesGenerator.zip,单表+分表,连接dds应用数据源的代码在ZdalConfiguration.java
- generatorConfig.xml是插件生成mapper和do的配置,用来连接dev的库
sofaboot 推荐的开源组件,使用maven插件生成对应的mapper和DO,使用上比dalgen方便
- dev库的账号密码要明文写在代码里
common-integration
- 接口和实现类均使用 client 命名
- 外部服务依赖均不注册为sofa服务,在实现类中使用 @Autowired 引入服务bean
common-util
- util 模块则提供了基础的公用的工具服务,工具类命名都以Util结尾。
此处为语雀内容卡片,点击链接查看:https://yuque.antfin.com/middleware/sofaboot
模块间调用规范
理论上各除了本模块的服务,manager层只调用manager层的服务,service层只调用service层的服务
二、命名规范
- ✅单个对象查询方法命名get*
- ✅多个对象查询方法命名list*
- ✅模型命名,视图模型-DTO/VO、领域模型-Model
- ✅方法命名,*Facade,*FacadeImpl,*Manager,*Service,*Repository
- ✅接口层透出对象统一以DTO/VO结尾命名
- ✅接口字段里如果有枚举code或者name,注释上标记 {@link 枚举类}
- ✅Facade入参,必须包装对象
- ✅Service根据是否需要缓存确认继承cacheableBaseService
- ✅facadeImpl必须继承BaseFacadeImpl
三、日志规范
- ✅各层的请求和返回日志利用AOP通过统一日志拦截器进行打印(包括数据库层)
- ✅方法独有的业务监控日志通过工具类打印,方便后续自定义业务监控
- ✅禁止打印堆栈
- ✅严格根据业务需要,打印error日志,会配置相应监控告警,保障告警有效性。
四、开关配置规范
- ✅DRM与后台开关默认打开,走正常发布流程
五、其他注意事项
- ✅尽量使用Integer,不要使用int,int默认为0,不排名和排名为0是两码事
- ✅根据业务抽出基本对象,减少重复写相同字段
- ✅在明确的场景下,为集合指定初始容量
- ✅代码开发时,考虑判空,严防NPE
- ✅在编码时,也要考虑测试用例的行覆盖率和分支覆盖率,本着从facade入口处调用,从上至下贯穿到dao层,尽量覆盖完全,遇到分支或异常等特殊情况,使用多个case来进行覆盖
- ✅方法尽量抽象,没有用到或非必须的方法及时删除