项目开发模板

Posted by Liber Sun on June 3, 2019

当我在从零开始搭建了多个项目之后,我发现了一些可以称为基础代码框架的代码,这些代码往往存在于多个系统中,细节或许不同,总体思路却是一致。

于是,我希望将其整理出一套较为公共性的项目模板出来,旨在之后的开发中更快的构建项目。

对于后端开发,使用了SpringBoot,对于前端开发,使用了Vue(Vue Template及是Vue官方推荐目录结构,然后结合SpringTemplate对axios进行封装)

但是这个模板并不是不刊之言,当然是可以结合自己的经验以及其他优秀的项目,在此之上继续总结,以飨开发者。

第一步:Readme.md

一份好的Readme可以给人以项目全景,无论是整体架构还是容易犯错的细枝末节。可以让新人更快的快速上手项目,降级沟通的成本。

建议包含如下的内容:

-项目简介:what?why?how? -技术选型:编程语言,技术栈以及中间件等 -构建与部署:如何进行本地开发以及远程部署,当然如果能提供脚本是最好的选择 -核心模型:对系统的核心领域模型进行描述 -测试策略:测试先行,确定必要的测试 -系统架构:网络架构、部署架构、技术架构等 -外部依赖:项目运行所依赖的外部服务提供方 -环境依赖:系统环境,数据库访问等 -编码实践:系统中需要对外指出的,公共的代码实践方式,比如全局异常如何处理,分页封装等 -项目原则:必要遵守的原则 -FAQ:开发过程中容易困惑点的解答

如何构建

如果一个项目需要花了多方的交流,长时间的尝试才能构建成功的话。那么请仔细的审视你的代码以及你的系统。

构建流程一般分为以下几个步骤:

  1. 拉取代码。
  2. 利用idea构建项目,主要涉及到配置环境。
  3. 编写代码,测试。
  4. 本地构建。
  5. 再次拉取代码,解决冲突,提交代码。

如何部署

看起来你需要一个自动化脚本,或者把代码交给运维吧。

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是实际的相对时间。就是经历过的时间差