前言
- 在业务开发过程中,普遍的分层为 controller、service、serviceImpl、mapper或dao、entity,等几层。但是在业务复杂度相对比较高的情况下。serviceImpl层中的逻辑会非常复杂,并且冗长。
- 所以在参考了其他的分层设计,并且根据自己的经验。对分层与职责进一步细化,项目与模块,根据实际情况进行取舍。
- 公共组件和框架组件,这里不分析。
项目结构
XXX项目
│
├─doc 项目文档
│ │
│ └─sql sql文件,可以按照日期进一步区分。
│ │
│ └─ yyyy-MM-dd-功能.sql
│
├─xxx-业务模块-api 对外接口(com.公司.项目.业务.模块.api)
│ │
│ ├─dto 对外实体包
│ │ │
│ │ ├─in dto入参(类名:XxxInDTO)
│ │ │
│ │ └─out dto出参(类名:XxxOutDTO)
│ │
│ ├─feign feign接口(接口类名:XxxFeign)
│ │
│ └─hystrix feign接口熔断返回实现(类名:XxxFallback)
│
├─xxx-业务模块-service 业务服务(com.公司.项目.业务.模块.service)
│ │
│ ├─provider 服务提供者,对应feign接口的实现(类名:XxxProvider)
│ │
│ ├─controller 控制层(类名:XxxController)
│ │
│ ├─service 服务层(类名:XxxService)
│ │ │
│ │ ├─impl 服务实现(类名:XxxServiceImpl)
│ │ │
│ │ ├─helper 服务帮助者(类名:XxxHelper)
│ │ │
│ │ └─checker 服务校验者(类名:XxxChecker)
│ │
│ ├─mapper 持久层(类名:XxxMapper)
│ │
│ ├─domain 实体
│ │ │
│ │ ├─converter 实体转换(类名:XxxConverter)
│ │ │
│ │ ├─bo 当前服务之间传输对象
│ │ │ │
│ │ │ ├─in 入参(类名:XxxInBO)
│ │ │ │
│ │ │ └─out 出参(类名:XxxOutBO)
│ │ │
│ │ ├─vo 显示层传递过来的参数(类名:XxxVO,表单提交与详情,共用一个)
│ │ │ │
│ │ │ ├─in 入参(类名:XxxInVO、XxxQueryVO)
│ │ │ │
│ │ │ └─out 出参(类名:XxxOutVO)
│ │ │
│ │ └─entity 数据库实体(类名:XxxEntity、XxxDO)
│ │
│ ├─remote 远程调用(类名:XxxRemote)
│ │ │
│ │ ├─converter 远程对象转换器(类名:XxxRConverter)
│ │ │
│ │ ├─rbo 远程入参和出参对象(类名:XxxRBO)
│ │ │
│ │ └─helper 帮助者(类名:XxxRHelper)
│ │
│ └─util 静态工具类(类名:XxxUtil)
结构说明
| 层名 | 描述 |
|---|---|
| feign | 提供服务接口,给外部服务调用。如果有条件,通过接口的 default 关键字,返回处理后的数据。 |
| hystrix | feign异常时,返回处理方法。 |
| dto.in | feign接口的入参 |
| dto.out | feign接口的出参 |
| provider | feign接口的实现,提供 feign 对应的服务 |
| controller | 正常服务的控制层。接收 domain.VO 中的对象 |
| service | 服务接口。如果接口太多,可以试着拆分。简单拆成 ReadService 和 WriteService 两个服务,可以接收所有的实体参数,并且使用 default 关键字,在接口层进行转换,不用额外的实现。 |
| service.impl | 服务实现。与 service 对应,只能引用当前 serivce 实体的 mapper,不能引用其他实体 mapper。如果需要,必须通过引入其他 service 来进行处理。如果逻辑过长,可以使用专用的 hepler 进行抽取封装,提高可读性。 |
| service.checker | 服务校验者。将服务的一些复杂的校验规则,封装到该类中,可以提供给其他服务的 helper 使用。 |
| service.helper | 服务帮助者。主要功能是将 serviceImpl 中的复杂逻辑进行封装。使得 serviceImpl 简化。并且对 checker进行组合,将多个校验规则进行整合。 |
| mapper | 持久层。没啥注意点,也可以改名为 dao |
| domain | 实体 |
| domain.entity | 与数据库表结构对应,不允许添加额外的信息 |
| domain.bo | 当前工程中,service 之间调用的传参。in入参,out出参。 |
| domain.vo | 显示层的入参和出参。如果入参和出参相同,则直接放在 vo 目录下,命名为 Xxx[修饰词]VO。 |
| domain.converter | 实体转换器。service 调用 converter 对 实体进行转换。 |
| remote | 远程调用。如果 service 需要调用外部项目的接口,则不能在 serviceImpl 中直接引用,必须通过 remote 层操作。目的是为了减少 serviceImpl 的逻辑,与异常操作。如果想要改成同步和异步调用,也方便。 |
| remote.rbo | 远程调用实体与本工程实体的映射。保证如果远程对象修改,本工程不需要改动很多内容。 |
| remote.converter | 远程调用实体与本工程实体转换器。目的是避免字段修改,导致需要大概逻辑。 |
| remote.helper | 如果逻辑不是很复杂,则可以不需要。 |
| util | 静态工具类 |
总结
目前的分层对于复杂的业务场景基本能够适应。也可做进一步的扩展。比如说 相同类型的操作,可以再次归纳成一层。 如:有很多表都有历史表备份、还原、复制数据、复制远程数据,等相同操作。就可以再次抽取 service.his、service.his.impl、mapper.his、domain.his 等层进行进一步区分。 分层的目的是为了让工程看起来更加规范,通过层名就能够知道职责,快速定位问题,解耦和复用。
后续补充备份还原业务框架