当我在从零开始搭建了多个项目之后,我发现了一些可以称为基础代码框架的代码,这些代码往往存在于多个系统中,细节或许不同,总体思路却是一致。
于是,我希望将其整理出一套较为公共性的项目模板出来,旨在之后的开发中更快的构建项目。
对于后端开发,使用了SpringBoot,对于前端开发,使用了Vue(Vue Template及是Vue官方推荐目录结构,然后结合SpringTemplate对axios进行封装)
但是这个模板并不是不刊之言
,当然是可以结合自己的经验以及其他优秀的项目,在此之上继续总结,以飨开发者。
第一步:Readme.md
一份好的Readme可以给人以项目全景,无论是整体架构还是容易犯错的细枝末节。可以让新人更快的快速上手项目,降级沟通的成本。
建议包含如下的内容:
-项目简介:what?why?how? -技术选型:编程语言,技术栈以及中间件等 -构建与部署:如何进行本地开发以及远程部署,当然如果能提供脚本是最好的选择 -核心模型:对系统的核心领域模型进行描述 -测试策略:测试先行,确定必要的测试 -系统架构:网络架构、部署架构、技术架构等 -外部依赖:项目运行所依赖的外部服务提供方 -环境依赖:系统环境,数据库访问等 -编码实践:系统中需要对外指出的,公共的代码实践方式,比如全局异常如何处理,分页封装等 -项目原则:必要遵守的原则 -FAQ:开发过程中容易困惑点的解答
如何构建
如果一个项目需要花了多方的交流,长时间的尝试才能构建成功的话。那么请仔细的审视你的代码以及你的系统。
构建流程一般分为以下几个步骤:
- 拉取代码。
- 利用idea构建项目,主要涉及到配置环境。
- 编写代码,测试。
- 本地构建。
- 再次拉取代码,解决冲突,提交代码。
如何部署
看起来你需要一个自动化脚本,或者把代码交给运维吧。
Detail
一致性的标准带来的好处不言而喻。
目录结构
首当其次的,我们需要统一我们的目录结构。
基于技术的目录结构
基于技术的分包,及基于controller、service、repository、domain等技术的分包方式。这种方式将我们的代码按照代码逻辑分为了几个模块部分,每个部分之间层层递进。 但是,随着业务的增多,这种分层中的代码会逐渐增多,最终一片混乱无法管理;另外,你也无法保证每一种业务都存在对应的模块部分。
├── src
│ ├── controller
│ │ ├── AController.java
│ │ └── BController.java
│ │ └── ...
│ ├── service
│ │ └── ...
│ ├── repository
│ │ └── ...
│ ├── domain\entity\po
│ │ └── ...
基于业务的目录结构
领域驱动设计(Domain-Driven Design,DDD)是面向领域模型的设计思想,它以领域模型为聚合根,将业务围绕他们展开。
├── src
│ ├── A模块
│ │ ├── AController.java
│ │ └── AService.java
│ │ └── ARepository.java
│ │ └── Amodel.java
│ │ └── ...
│ ├── B模块
│ │ └── ...
总结
在编码实践中,我们总是基于业务来实现代码的(当然如果将业务拆分,以微服务的方式来发布,我们更需要这种分包方式),在技术分包的情况下,我们需要在分散的各个包中来回的切换,增加了代码导航的成本。,另外在代码提交时,也只能发现零星的代码提交历史,无法直观的发现该次的提交是关于什么业务的。另外一个好处是,当我们需要将某个业务迁移到别的项目时,我们直接整体迁移业务包即可。
当然,我们无法把所有的代码都归属到对应的业务当中,而这种与业务无关的代码,往往是我们最需要的代码,及COMMON
包,这其中往往包含
├── src
│ ├── Common模块
│ │ ├── 配置
│ │ └── 异常
│ │ └── 日志
│ │ └── 工具类
│ │ └── 通用库
代码风格
统一即可,并没有金玉良言。 统一的返回,统一的前缀、后缀,统一的命名方式,统一的请求处理流程、统一的异常返回、统一的分页结构等。
代码检查
为了维护统一的代码风格,推荐使用idea插件Alibaba Java Coding Guidelines
对代码进行检查,不管是编写代码时候,或者push代码时都需要。
特别需要注意的是严格检查,任何的一次妥协都会带来永恒的灾难。
异常处理
异常处理框架必须考虑,统一的格式以处理,足够的上下文信息和不同类型的异常唯一标识以定位。
多环境问题
不同的环境,往往需要注意不同的配置。
- local 本地开发
- dev 前端联合调试
- prod 生产环境
如何测试
我们将代码的测试大致分为三类,及普通单元测试,组件测试以及API测试。
-
单元测试:核心的领域模型,包括领域对象,Factory类,服务类等;
-
组件测试:不适合写单元测试但是又必须测试的类,比如Repository类;
-
API测试:模拟客户端测试各个API接口,需要启动程序。
如何回溯
在回溯过程中,我们常用的是两个方法,一个是断点模拟,另一个是日志文件。
断点模拟,是已知条件,一步一步推到,直到发现问题;而日志文件是不知道条件并发生错误的情况。
因此我们在日志文件的生成中,必须考虑两个要点:
1.针对所有的api请求,考虑日志的请求标识,便于链路追踪。 2.日志的管理,是单纯的打印还是文件存储,以及数据库存储。
Async和Scheduling机制
异步处理(@EnableAsync
)和定时任务(@EnableScheduling
)在系统中是非常常见的,因此我们借用SpringBoot来完成这种业务。
API文档
@EnableSwagger2
,管理你的API。
@Configuration
@EnableSwagger2
@Profile(value = {"local", "dev"})
public class SwaggerConfiguration {
@Bean
public Docket api() {
return new Docket(SWAGGER_2)
.select()
.apis(basePackage("njnu.opengms"))
.paths(any())
.build();
}
}
第三方类库
大量的第三方类库,一方面能提高工作效率,另一方面也提高了Code的复杂度。 因此我们需要在引入第三方类库时,对系统使用到的第三方类库进行一定的说明阐述。
Guava:来自Google的常用类库
Apache Commons:来自Apache的常用类库
Mockito:主要用于单元测试的mock
DBUnit:测试中管理数据库测试数据
Rest Assured:用于Rest API测试
Jackson 2:Json数据的序列化和反序列化
jjwt:Jwt token认证
Lombok:自动生成常见Java代码,比如equals()方法,getter和setter等;
Feign:声明式Rest客户端
Tika:用于准确检测文件类型
itext:生成Pdf文件等
zxing:生成二维码
Xstream:比Jaxb更轻量级的XML处理库
Date的处理
首先我们要认识到,时间戳是一个和时区无关的东西,在哪里都是一致的!但是转化为可见字符串之后,就要考虑时区。
对于一个Web项目,是必然存在多时区问题的。由此我们可以考虑用long
解决。
-
数据库节省空间
-
好比较
-
无时区和夏令时冬令时问题
-
解决闰秒问题
-
解决历史遗留问题和国家特殊性问题 (教皇格列高里十三世于1582年宣布改历。先是一步到位把儒略历1582年10月4日的下一天定为格列历10月15日,中间跳过10天。1582年10月5-14号,这几天在历史上不存在。)
-
long是实际的相对时间。就是经历过的时间差