微服务项目规范(一)

以下规范仅代表个人观点以及部分公司的规范,不一定适用于所有项目,具体如何准守以实际项目而定。

 

一.模型篇

请求入参

所有Controller入参,一律使用 DTO结尾进行交互

所有 DTO 命名,前面采用驼峰命名,后面DTO大写

所有DTO内参数,如果是一组,请用List<类型>进行接口,尽量不用分隔符隔开,然后字符串接收,

所有DTO放置在子模块Model下,根据业务进行分组,例如:

com.wang.order.center.order.DTO,com.wang.order.center.pay.DTO

 

请求出参

所有Controller出参,一律使用Result进行包装返回

所有Controller出参,Result泛型包装对象,一律用VO结尾进行命名

所有 VO 命名,前面采用驼峰命名,后面VO大写

所有VO放置在子模块Model下,根据业务进行分组,例如

com.wang.order.center.order.VO

com.wang.order.center.order.VO 

 

数据库交互对象

跟数据库打交道的对象,一律使用PO进行结尾

跟数据库交互的对象,全部通过逆向生成工具进行生成

所有PO对象,放置在子模块dao下

所有映射生成的xml文件,在子模块dao的resources/mapper下

 

二.命名篇

类命名

Controller必须根据业务进行命名,驼峰,Controller结尾

Service必须根据业务进行命名,驼峰,Service结尾

implement必须根据业务进行命名,驼峰,业务+service+impl结尾

辅助类的命名,根据功能进行命名,如果是工具类,请使用final修饰,并且以utils或者util进行结尾

 

函数命名

函数命名使用驼峰命名

辅助函数,例如构建对象,请用build开头,表示只用于构建对象,并且返回

辅助函数,请使用 private修饰

DAO层函数命名

查询单个尽量使用queryXX或者selectXX命名 

查询多个尽量使用queryXXs或者selectXXs复数形式进行命名

更新单个尽量使用modifyXX或者updateXX命名

更新多个尽量使用modifyXXs或者updateXX复数形式进行命名 

插入单个尽量使用insert或者add命名

插入多个尽量使用insertXXs或者addXXs命名

DAO所有传参如果是单个参数,请使用@param注解进行注明

所有XXMapper.xml获取参数,请使用@param("XX") 命名, #{XX}获取,${XX}会有SQL注入的风险,具体细节请参考Mysql手册`跟'的区别

变量命名

  • 使用驼峰

  • 状态类的变量,请使用int类型

  • boolean类型的变量,请使用has开头,请勿使用is开头,在某些序列化框架下,可能会导致反序列化失败

枚举/常量命名

  • 枚举类型使用全大写,_进行分割

  • 枚举命名请使用业务+Enum进行命名

  • 接口常量命名请使用业务+Constant进行命名(尽量使用枚举来定义常量)

 

三.函数参数篇

接口参数

  • 尽量使用对象进行传递,严禁使用MAP进行传参

  • 如果要进行单个参数传递,最好不要操作5个参数,参数太多,看起来太混乱

辅助函数参数

  • 尽量使用对象进行传参,严禁使用Map进行传参

  • 如果要进行单个参数传递,最好不要操作5个参数,参数太多,看起来太混乱

 

四.注释篇

类注释

/**
 * 项目名:wang-order-center
 * 创建人: XX
 * 创建时间:2018/8/13 15:45
 * 类名:OrderController
 * 类描述: 订单控制器
 */

函数注释

函数参数,一定要加上 @param 这样,别人在调用的时候,ide会进行提示,如果涉及到枚举 加上注解 @see 可以跳转到枚举

    /**
     * 创建人:XXX
     * 创建时间:2018-08-20 20:46:04
     * 描述: 购物车创建订单
     *  @param userId 用户ID
     * @param subUserId 用户ID
     * @param addrId 地址ID
     * @param cartProductIds 购物车ID集合
     * @param fahuoTimeLimitedMode    截单模式 0 立即截单, 1 根据活动时间截单
     */

 

代码注释

代码注释,进行别使用/**/的形式,注释不需要像写作文那样详细,只需要介绍是做什么的就好

// 构建订单对象

 

五.其它篇

代码是逻辑的表现,一段代码是否是好代码,主要是要看逻辑是否清晰,因为业务代码是给人看的,并不是框架或者开源代码,写的太高大上,各种设计模式,并非好事,有过度设计的嫌疑,尽量做到面向接口编程,提高内聚,降低耦合

代码

  • Controller尽量只做转发,不做具体逻辑实现,所有逻辑实现交由service进行

  • service的接口函数,尽量功能单一,职责越小,越便于维护跟扩展

  • 如果涉及到写等动作,请在函数上加上@transaction注解/或者自己用事务块包裹

  • service如果不需要捕获异常,可以往上层抛,订单中心已经在全局做了异常处理

  • 尽量做到所有的check在入参就应该进行判断

  • 如果遇到需要构建对象,大量 set函数不应该放置主流程,抽离出来一个辅助函数,直接调用辅助函数即可,辅助函数注释写好,主流程调用辅助函数加上功能注释

  • 代码内部尽量不使用多层for循环调用,在不注意的情况,多层for循环嵌套构建对象,很容易写出内存泄漏的代码

  • 尽量不使用匿名内部类,使用lambda,因为现在jdk已经是1.8版本,支持Java8的特性

  • 如果一段代码,超过两个地方调用,说明需要进行抽象,然后对抽象进行实现

  • 函数跳转层级尽量不要过深,因为是业务代码,跳转太深,很容易把人绕晕了

  • 如果一个函数出现特别多的if else 考虑把if else建成枚举,然后枚举内进行判断返回

 

日志

  • 所有service层可以直接抛出异常,不需要捕获异常处理,已经配置好全局异常处理,只需要抛出错误信息就好

  • 业务错误打印日志,尽量别打印error打印warning级别日志就好,所有抛出异常上面,尽量打印错误日志的参数

  • 日志打印,尽量使用一下示例打印 // 正确打印 LOGGER.info("订单ID错误,订单ID:{}",order.getId()); // 错误打印 LOGGER.info("订单ID错误,订单ID:"+order.getId());

  • 日志是常量,所以请使用以下定义 private static finale Logger LOGGER = LoggerFactory.get(XX.class);

  • 日志引用,禁止引用具体实现,请引用 slf4j接口

 

请求路径

  • 尽量使用Restful风格

  • /liveId/productId URL参数,请使用 @PathVariable("liveId")String liveId 指定名称绑定

  • 查询尽量使用Get请求方式,如果参数在URL传递过多,可以不使用Get请求,使用POST请求,请把参数写进请求体内

  • 所有写的操作一律使用POST请求,参数放入请求体内,公共参数,以后会让客户端放入请求头内

 

幂等

  • 所有的核心写接口,例如,创建订单,支付,取消订单等,一定一定要做幂等,幂等可以在所有的check后也可以在函数调用就进行判断,但是一定是在逻辑进行前

  • 幂等接口,一定要跟调用方统一状态,达成共识

  • 往往幂等会伴随着并发同时进行,因为网络传输,机器性能,请求处理,事务提交带有太多的不确定性,所以要加以控制,所以redis尽量只做并发控制跟短时间内的幂等,请勿太依赖redis来进行做幂等,在redis锁后,插入前请进行DB查询操作,查看是否已经写入过,如果已经写入,拒绝请求

单机锁,如果没有竞争资源,不要使用锁

尽量使用 Lock接口作为锁,别使用Synchronize作为锁,后者作为重量级锁,太影响性能

对锁的使用不清楚,严禁私自使用,尽量多请教会的人,避免写出死锁或者活锁

分布式锁,目前只是用了redis的setnx操作作为分布式锁的实现,使用分布式锁,一定一定要设置过期时间或者在锁代码块执行完毕,手动移除锁,否则出现锁不能释放,你就悲剧了……...

目前setnx操作,使用的是jedis连接池来实现的,没有使用spring-data提供的redisTemplate是因为,后者的setnx操作并非一个原子操作,运气不好的情况,极有可能出现setnx操作了,setexprie失败,导致锁不过期,但是jedis使用,切勿忘记,使用完毕,归还连接,jedis是不会再你调用完毕主动归还连接的

所有锁,请在check完毕或者check之前进行锁定资源,请勿在最后锁定,进行无用的写

锁定的资源,必须是唯一的,例如,订单ID,订单编号等

posted @ 2019-04-19 22:29  薛定谔病态猫  阅读(3246)  评论(0编辑  收藏  举报