作者:于佳鑫
我们主要采用的是阿里巴巴规约,请熟读并理解每条的确切含义。
本系统采用的分层结构与 p3c 规范中的基本相同。
数据访问对象(Data Access Object),用于对数据库进行访问,负责数据的 CURD。
当然 Dao 不仅限于与数据库进行交互,假如日后系统引入的 ElasticSearch、Mongo 甚至 Redis,我认为都可以定义一个 Dao 对其进行访问。
这样的定义,可以将数据的 CURD 和业务逻辑进行分离。
p3c 规范中对其定义如下:
由这个定义并结合我们的系统,可以得出 Manager 层主要作用为:
在开发中,我们经常会遇到 AService 中的某个业务可以提供给 BService 调用,从而让 BService 调用 AService 的方法,认为是 Service 之间具有共同的业务。其实 Service 之间没有共同的业务,而是具备通用的逻辑,这时应该将其抽离出来放在 Manager 中。无论何种工程架构都好,我都不赞同 Service 与 Service 之间的相互调用。
对具体对业务逻辑进行操作(复用性很低),由于我们使用的是 dubbo,Service 层可能会被其他人调用,所以最好还是做一下参数校验(hibernate validate)
从 p3c 规范来看,如果不用 Manager 层来对多个 Dao 进行组合,那么 Service 层可以直接与 Dao 进行交互,但是这样会给人一种很混乱对感觉,所以我们定义:
Service 对数据库进行操作时,必须通过 Manager 层,其实这也是为日后开发可能遇到对问题留有余地;如果我们使用 Redis 做 Service 层缓存的话,那么可以直接调用对应 Redis 的 Dao。
web 层只做简单对参数校验,或者简单对业务处理等(例如:查询审查任务实体,但是前端只需要部分字段,进行转换的工作);Web 层与 Service 层一一对应。
本系统采用对模型为贫血领域模型,p3c 规范定义对数据传输对象过多,这样就导致了一个对象可能会出现 3 次甚至 4 次转换在一次请求中,当返回的时候同样也会出现 3-4 次转换,这样有可能一次完整的“请求-返回”会出现很多次对象转换。如果在开发中真的按照这么来,恐怕就别写其他的了,一天就光写这个重复无用的逻辑算了吧,所以我们只定义了几个对象。
贫血领域模型中对象只作为数据载体,只有 getter/setter 方法,而不包含业务方法。
数据对象,对数据源数据的映射,如数据库表,ElasticSearch 索引的数据结构。所在包一般命名为 data 。
数据传输对象,业务层向外传输的对象。如果在某个业务中需要多次查询获取不同的数据对象,最后将会把这多个数据对象组合成一个 DTO 并对外传输。所在包命名为 dto 。
显示层对象,通常是 Web 向模板渲染引擎层传输的对象。现在的项目多数为前后端分离,后端只需要返回 JSON ,那么可以理解为 JSON 即是需要渲染成的“模板”。我一般会将这类对象命名为 xxxResponse ,所在包命名为 response。
数据查询对象,数据查询对象,各层接收上层的查询请求。
其实一般用于 Controller 接受传过来的参数,可以将其都命名为 xxxQuery,而我个人习惯将放在 request body 的参数(即 @RequestBody)包装为 xxxRequest ,而如果使用表单传输过来的参数(即 @RequestParam)包装为 xxxForm ,并分别放在包 request 和包 form 下。
注:web 层向 service 层传输对 query 对象,绝对不能传输到 Manager 层,因为 Manager 层是通用的逻辑。
其中 DTO 如果不可复用,那么可以直接传输给前端。
以辅助审查系统为例
辅助审查服务模块包设计
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
辅助审查 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
开发中会遇到一些很基础的,通用的业务逻辑,例如我们可能会根据每个用户的信息生成一个唯一的 account id 。又或者说有一个用户排名的需求,我们将从用户的相关信息中计算出一个分数,从而根据这个分数进行排名。那么这时候我们可能会将这些逻辑写在 User 数据对象或是其他相应对应的数据对象下。
由于我们采用的是贫血领域模型,数据对象中不应该包含业务逻辑,所以我们将这些通用的业务逻辑都抽出来,放到 helper 包中进行统一管理。如会将生成 account id 的逻辑放在 AccountIdGenerator 中,将计算排名分数的逻辑放在 RankCalculator 中。
我将这些类都归为 Helper ,用于提供底层的业务计算逻辑。而为什么不放在通用工具层中呢?因为这些 Helper 其实都是依赖于特定的领域,即特定的业务。而通用工具类则是业务无关的,任何系统,只要有需要都可以引用。