Dubbo的接口设计&编写规范
Dubbo的接口设计&编写规范
action->facade->biz->dao
好的Dubbo服务接口设计,并非只是纯粹的接口服务化
接口类型
同步&异步
- 简单的数据查询接口:action -> facade -> dao(例根据Id查询记录)
- 带业务逻辑的数据查询接口:action -> facade -> biz -> dao(复杂的查询,带业务逻辑)
- 简单的数据写入接口:action -> facade -> dao(简单数据插入)
- 带业务逻辑的数据写入接口:action -> facade ->biz -> dao(有业务逻辑的数据处理)
设计原则
-
服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将地面临分布式事务问题
-
Dubbo暂未提供分布式事务支持,同时可以减少系统间的网络交互
-
服务接口建议以业务场景为单位划分,并对相近的业务做抽象,防止接口数量爆增(爆炸)。
-
不建议使用过于抽象的通用接口,如Map query(Map),这样的接口没有明确语义,会给后期维护带来不便
-
给每个接口默认分配一个版本, 方便升级维护
接口兼容性
-
服务接口增加方法,或服务模型增加字段,可向后兼容
-
删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需要通过变更版本号升级
异常处理
-
建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多的信息,以及语义更友好。
-
如果担心性能问题,可以通过override掉异常类的finlllnStackTrace()方法为空方法,使其不拷贝栈信息。
-
查询方法不建议抛出checked异常, 或者说让Consumer尽量减少接受异常的种类
-
Provider上尽量多配置Consumer端的属性, 让Provider实现者一开始就思考Provider服务特点、服务质量的问题
简单聊下
Specification 模式解决接口爆炸问题
public interface UserMainProvider {
UserMainDTO findByUserId(Long userId);
UserMainDTO findByUsername(String username);
List<UserMainDTO> listByUsername(Long username);
List<UserMainDTO> listByNickname(Long nickname);
}
先不要纠结我有没有写注释, 像这样的接口书写方式,后期业务复杂的话肯定会出现接口爆炸的情况
我们可以这样修改:
public interface UserMainProvider {
UserMainDTO findByUsername(UserMainSpec spec);
List<UserMainDTO> listByUsername(UserMainSpec spec);
PageInfo<UserMainDTO> page(UserMainPageSpec spec);
}
如果你了解领域驱动设计,会发现这里借鉴了其中 Specification 模式的思想。不清楚自行google吧
单参数易于管理
我们可以定义个抽象父类,方便做一些统一的做一些东西, 例如公用参数, 切面对象等等
@Data
public abstract class RpcSpecification<T> implements Serializable {
private static final long serialVersionUID = 1L;
/** 追溯ID */
private String traceId;
/** 操作类型*/
private OperationTypeEnum operationType;
/** client IP */
private String clientIP;
/** client name */
private String clientName;
}
接口异常设计
如果是dubbo 这种裸体rpc的话我还是建议通过抛异常的形式处理, 当然很多人在REST风格的影响下,也会封装一个对象, 这太麻烦, 每次都要解析对象处理各种code的场景,
我们可以这样做:
- Provider 尽量处理异常
- 定义一个全局异常放在公用包中.
- Consumer 也不要傻傻的一个一个处理了, 建议用@ExceptionHandler 统一处理全局异常.
可参考<Dubbo的异常处理>这篇文章
好了就这些, 欢迎关注, 转发, 评论, 点赞~