在《架构设计》https://blog.csdn.net/hguisu/category_5905793.html系列里面主要谈的是架构相关方法论,没有具体到代码层面。最近抽点时间来总结架构师如何站在巨人的肩膀上眺望远方。
管理的精髓就是“制度管人,流程管事”。而所谓流程,就是对一些日常工作环节、方式方法、次序等进行标准化、规范化。且不论精不精髓,在技术团队中,对一些通用场景,统一规范是必要的,只有步调一致,才能高效向前。
这个在《架构设计(1)-谈谈架构》 已经明确:
框架是组件实现的规范,提供一定的约束、规范、配置,具备通用业务的基础开发能力,提供一定程度的组件、工具等公共封装。例如:MVC、MVP、MVVM等,是提供基础功能的半成品,例如开源框架:Spring、spring boot、Django等,这是可以拿来直接使用或者在此基础上二次开发。框架是规范,架构是结构。
1)、框架是提供基础功能的半成品:已经对基础的代码(例如文件上传,数据库查询)进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的api可以省去很多代码编写,从而提高工作效率和开发速度。
2)、框架是规范:框架规范应用的体系结构。它定义了整体结构,模块的划分、模块的主要职责,模块之间的协作。
1)、提高开发效率:
使用成熟的框架,基础工作已经完成,应用开发人员只需要集中精力完成系统的业务逻辑设计。框架一般已经处理好基础功能和细节问题,比如事务处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。
2)、提高团队人员协同效率。
通过框架规范,规定大层次的约束与规范,较小层次的设计在这些约束与规范下进行的话,能最大限度地满足某些方面的特性,如可读性、可靠性、可扩展性、安全性。同时有使得代码更为清晰及更易维护。最终目的是提高团队协作效率,降低工程维护成本。
组件化就是基于可重用的目的,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,已较少耦合。把重复的代码提取出来合并成为一个个组件,组件最重要的就是重用(复用),位于框架最底层,其他功能都依赖于组件,可供不同功能使用,独立性强。
大部分来说,组件主要分三层:业务组件,基础业务组件以及基础组件,组件之间只能通过接口耦合,也就是依赖倒置原则,每个组件都提供对外的接口文档以描述该组件提供的功能。
组件化的好处:解耦,平台化,职责单一,复用性,编译集成。
组件化到组件的粒度到底多大,如何区分业务组件以及基础业务组件?这个需要根据具体项目具体分析。
脚手架:基于开源框架或者组件的整合,只是一个空架子,把项目的基础环境配置和maven相关依赖都搭建好,封装程度较低,偏向于一套技术最佳实践。做前端node的都知道,经常去npm install 一个脚手架或者vue-cli脚手架搭建工具,然后用脚手架命令去创建一个空架子的项目。
基础应用框架:除了整合应用使用的开源框架,同时还封装项目应用到的基本工具和基础功能:日志,接口协议、异常处理、安全处理、单元测试、参数校验等。即在脚手架的基础。
注明:以上仅个人理解脚手架和项目基础技术框架。
在微服务里面或者生命周期比较长的项目,一般是需要对使用不同原始框架拼装项目的基础框架,目的是封装提供项目使用的基础功能和规范项目技术体系,不要重复造轮子,提高团队的协同效率,降低维护成本。
例如:
1、封装基础组件:如spring boot的版本统一规范和依赖规范。
2、日志组件统一规范。
3、restFull接口规范:比如rest接口返回格式。
4、异常处理统一规范
5、参数校验
6、安全处理机制。
7、api文档
8、基础应用配置。
9、模型统一规范格式。
10、一些基础工具类:比如项目使用获取ip,邮箱、手机号等校验。
11、单元测试规范。
下面是一一具体描述。
主要spring boot的版本统一规范和依赖规范。主要的措施:
1)、通过maven的parent工程统一管理项目所有的jar的版本。
2)管理jar包依赖,在parent工程中配置依赖通用性jar包,然后引用该parent的项目就会自动继承。
3)parent工程通过<dependencyManagement>管理了可选的jar依赖,具体项目是按需继承。
如:<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
具体项目只要按需去依赖自己所需的jar包,并且version 和 scope 都继承pom。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
Java应用项目经常遇到的一个棘手的问题就是:依赖的包使用了不同的日志组件,常用的有log4j, log4j2, logback, common-logging, JUL等,导致日志输出异常混乱。因此日志的输出有必要进行统一配置,而不是针对不同的日志组件分别配置。
因此需要做统一规范:
因此为了统一规范日志的记录,及方便日后扩展,通过抽象接口对日志记录进行了封装。
然后在具体项目统一使用:
import com.xxx.framework.logs.Logger;
import com.xxx.framework.logs.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(xxController.class);
采用的微服务框架,前后端分离,前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。
为了提高团队间接口对接的效率,需要做前后端交互的格式规范。一般统一的规范如下:
1、请求格式规范:建议使用json body格式,传入参数包含公共请求头的要求(如:app_version,api_version,device等)。
2、返回格式规范:
JSON体方式,定义如下:
{
code:integer,#返回状态码
message:string,#返回信息描述,直接展示友好用户提示信息
error:string,#错误描述信息,方便开发直接定位
data:object #返回值
}
code状态码:我们可以参考http状态设计:
HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
分类的好处就把错误类型归类到某个区间内,如果区间不够,可以设计成4位数。
#900~999 区间表示系统异常错误
#1000~1999 区间表示用户模块1错误
#2000~2999 区间表示用户模块2错误
这样前端开发人员在得到返回值后,根据状态码就可以知道,大概什么错误,再根据message相关的信息描述,可以快速定位。
简单实现,这里我是为兼容旧项目,不得使用status代替code,这个无关紧要,只要规范统一旧可以。
@Data
public class RestVO<T> {
private int status = 0;
private String msg = "success";
//前端不展示,方便开发检查。
private String error;
private T data;
public RestVO() {
}
public RestVO(T data) {
this.data = data;
}
public RestVO(int code, String msg) {
this.status = code;
this.msg = msg;
}
public RestVO(int code, String msg, T data) {
this.status = code;
this.msg = msg;
this.data = data;
}
public RestVO(RestCode restCode) {
this.status = restCode.getCode();
this.msg = restCode.getMsg();
}
public RestVO(RestCode restCode, String error) {
this.status = restCode.getCode();
this.msg = restCode.getMsg();
this.error = error;
}
}
统一异常处理需要依赖rest接口格式规范。
基础框架组件需要对异常进行统一处理,规范异常处理的行为。这样各个具体项目基于这个框架组建开,减少重复造轮子。
具体实现:
Spring学习笔记(9)一springMVC全局异常处理_黄规速博客:学如逆水行舟,不进则退-CSDN博客_springmvc全局异常处理
参数校验主要是定义一些注解实现请求参数校验和格式化
1、示例1:@RegionFormat(地区格式化):
被@RegionFormat注解的属性,会接受一个地区id转换为地区对象。
@RegionFormat
private Region region;
上述注解假设如下请求:
xxx?region=3
则会将id为1的地区及其父的信息读取并形成Region对象
例如region=3的地区为长安街,父辈为为东城区-->北京,刚形成的Region对象数据如下:
{
"cityId": 2,
"townId": 0,
"countyId": 3,
"provinceId": 1,
"province": "长安街",
"county": "东城区",
"city": "北京市",
"town": ""
}
2、@Mobile (手机格式校验)
使用@Mobile注解可以校验手机号码的正确性。
使用:在声明的对象手机号码属性上面加@Mobile
@Mobile
private String mobile;
如果校验失败会抛出如下异常:
{
"code": "004",
"message": "手机号码格式不正确"
}
主要是防xss攻击的一些措施。例如防xss攻击过滤。
使用Swagger2做api文档,同时封装Swagger配置基类:
public abstract class AbstractSwagger2 {
/**
* 构建认证token参数
*
* @return token参数
*/
protected List<Parameter> buildParameter() {
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
// tokenPar.name("sign").description("sign").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
// pars.add(tokenPar.build());
return pars;
}
}
待续... ...
8、基础应用配置。
9、模型统一规范格式。
10、一些基础工具类:比如项目使用获取ip,邮箱、手机号等校验。
11、单元测试规范。