# 代码规范

作者:于佳鑫

# 阿里巴巴规约

我们主要采用的是阿里巴巴规约,请熟读并理解每条的确切含义。

# 采用的工程架构模型

# 1、如何分层

p3c规范中的分层模型

本系统采用的分层结构与 p3c 规范中的基本相同。

# Dao 层

数据访问对象(Data Access Object),用于对数据库进行访问,负责数据的 CURD。

当然 Dao 不仅限于与数据库进行交互,假如日后系统引入的 ElasticSearch、Mongo 甚至 Redis,我认为都可以定义一个 Dao 对其进行访问。

这样的定义,可以将数据的 CURD 和业务逻辑进行分离。

# Manager 层

p3c 规范中对其定义如下:

  1. 对第三方平台封装的层,预处理返回结果及转化异常信息
  2. 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理
  3. 与 DAO 层交互,对多个 DAO 的组合复用

由这个定义并结合我们的系统,可以得出 Manager 层主要作用为:

  1. 可以通过 Manager 层调用第三方服务(指标系统、全文检索服务,因为他们是基础服务,所以不要在 web 层调用他们),对返回结果进行简单的处理之后返回 Service 层。
  2. 如果需要对基础服务进行调用,并将其结果处理后入库(例如:档案管理系统、模型系统)这种用法时,直接通过 Dao 层进行保存。
  3. 可以将 Manager 理解为对通用逻辑的封装,避免 Service 与 Service 进行相互调用,以及对通用逻辑的管理。

    在开发中,我们经常会遇到 AService 中的某个业务可以提供给 BService 调用,从而让 BService 调用 AService 的方法,认为是 Service 之间具有共同的业务。其实 Service 之间没有共同的业务,而是具备通用的逻辑,这时应该将其抽离出来放在 Manager 中。无论何种工程架构都好,我都不赞同 Service 与 Service 之间的相互调用。

  4. 如果 2 个(或 2 个以上)表之间有一定的关联关系(一对多、多对多)并经常一起使用,则通过一个 Manager 对他们进行访问。
  5. 可以在这层加入缓存(当然我们体量小,一般只在 service 层加缓存),与我上面所说的为操作 Redis 定义对 Dao 进行访问。

# Service 层

对具体对业务逻辑进行操作(复用性很低),由于我们使用的是 dubbo,Service 层可能会被其他人调用,所以最好还是做一下参数校验(hibernate validate)

从 p3c 规范来看,如果不用 Manager 层来对多个 Dao 进行组合,那么 Service 层可以直接与 Dao 进行交互,但是这样会给人一种很混乱对感觉,所以我们定义:

Service 对数据库进行操作时,必须通过 Manager 层,其实这也是为日后开发可能遇到对问题留有余地;如果我们使用 Redis 做 Service 层缓存的话,那么可以直接调用对应 Redis 的 Dao。

# Web 层

web 层只做简单对参数校验,或者简单对业务处理等(例如:查询审查任务实体,但是前端只需要部分字段,进行转换的工作);Web 层与 Service 层一一对应。

# 2、分层领域对象

本系统采用对模型为贫血领域模型,p3c 规范定义对数据传输对象过多,这样就导致了一个对象可能会出现 3 次甚至 4 次转换在一次请求中,当返回的时候同样也会出现 3-4 次转换,这样有可能一次完整的“请求-返回”会出现很多次对象转换。如果在开发中真的按照这么来,恐怕就别写其他的了,一天就光写这个重复无用的逻辑算了吧,所以我们只定义了几个对象。

贫血领域模型中对象只作为数据载体,只有 getter/setter 方法,而不包含业务方法。

# DO(Data Object)

数据对象,对数据源数据的映射,如数据库表,ElasticSearch 索引的数据结构。所在包一般命名为 data 。

# DTO(Data Transfer Object)

数据传输对象,业务层向外传输的对象。如果在某个业务中需要多次查询获取不同的数据对象,最后将会把这多个数据对象组合成一个 DTO 并对外传输。所在包命名为 dto 。

# VO(View Object)

显示层对象,通常是 Web 向模板渲染引擎层传输的对象。现在的项目多数为前后端分离,后端只需要返回 JSON ,那么可以理解为 JSON 即是需要渲染成的“模板”。我一般会将这类对象命名为 xxxResponse ,所在包命名为 response。

# Query

数据查询对象,数据查询对象,各层接收上层的查询请求。

其实一般用于 Controller 接受传过来的参数,可以将其都命名为 xxxQuery,而我个人习惯将放在 request body 的参数(即 @RequestBody)包装为 xxxRequest ,而如果使用表单传输过来的参数(即 @RequestParam)包装为 xxxForm ,并分别放在包 request 和包 form 下。

注:web 层向 service 层传输对 query 对象,绝对不能传输到 Manager 层,因为 Manager 层是通用的逻辑

# 层间对象传递

领域对象在不同层之间的传递

其中 DTO 如果不可复用,那么可以直接传输给前端。

# 3、包结构及其含义

以辅助审查系统为例

辅助审查服务模块包设计

x5456deMBP:dgp-dubbo-server-root x5456$ tree dgp-ars-server-service/src -d -L 8
dgp-ars-server-service/src
├── main
│   ├── java
│   │   └── com
│   │       └── dist
│   │           └── ars
│   │               ├── aop
│   │               ├── config
│   │               ├── dao
│   │               ├── manager
│   │               │   └── remote
│   │               │        ├── ams
│   │               │        ├── ims
│   │               │        ├── mms
│   │               │        ├── pms
│   │               │        └── sms
│   │               └── service
│   └── resources
│       ├── META-INF
│       │   ├── dubbo
│       │   └── services
│       ├── config
│       ├── db
│       │   ├── oracle
│       │   │   ├── create
│       │   │   └── update
│       │   └── pg
│       │       └── create
│       └── libs
└── test
    └── java
        └── com
            └── dist
                └── ars
                    ├── service
                    └── manager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

辅助审查 Api 模块包设计

tree dgp-ars-server-api/src -d -L 8
dgp-ars-server-api/src
└── main
    └── java
        └── com
            └── dist
                └── ars
                    ├── constants
                    ├── exceptions
                    ├── helper      # web层与service共用的辅助类
                    ├── model
                    │   ├── dto
                    │   ├── entity
                    │   ├── query
                    │   │   ├── request
                    │   │   ├── form
                    │   │   ├── webQuery        # 由web层封装向service层传输的查询对象
                    │   │   └── commonQuery     # 由service层封装传输到Manager层的通用查询对象
                    │   └── vo
                    └── service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# helper 包

开发中会遇到一些很基础的,通用的业务逻辑,例如我们可能会根据每个用户的信息生成一个唯一的 account id 。又或者说有一个用户排名的需求,我们将从用户的相关信息中计算出一个分数,从而根据这个分数进行排名。那么这时候我们可能会将这些逻辑写在 User 数据对象或是其他相应对应的数据对象下。

由于我们采用的是贫血领域模型,数据对象中不应该包含业务逻辑,所以我们将这些通用的业务逻辑都抽出来,放到 helper 包中进行统一管理。如会将生成 account id 的逻辑放在 AccountIdGenerator 中,将计算排名分数的逻辑放在 RankCalculator 中。

我将这些类都归为 Helper ,用于提供底层的业务计算逻辑。而为什么不放在通用工具层中呢?因为这些 Helper 其实都是依赖于特定的领域,即特定的业务。而通用工具类则是业务无关的,任何系统,只要有需要都可以引用。

# GIT 提交规范

git提交规范