当前位置: 首页 > 后端技术 > Java

系统设计——项目应该如何分层?

时间:2023-04-01 21:03:12 Java

前言最近公司在推进项目标准化,打算通过这个标准化对老项目进行重构,提高项目的可维护性、扩展性、可理解性。组里刚好有两三个项目也需要标准化。这时候,我重新阅读了《阿里巴巴Java开发手册》以及网上的相关文章,对项目分层有了更好的理解和实践。ProjectHierarchicalModelThree-tiermodel刚开始接触web开发的时候,我们使用的是三层模型进行开发。web层接收外部请求【web层接收请求,也可以处理简单的逻辑和参数校验】,然后将请求转发给Service层进行业务逻辑处理【Service层负责更复杂的业务逻辑处理】,而Service层与DB的交互需要通过Dao层【负责与数据库交互】来支持。上面的开发模式比较适合比较小的项目,代码层级比较清晰,不会因为层数太多而增加开发量。但是,如果采用上述分层结构来开发更复杂、更庞大的项目,就会暴露出一些弊端。从上面的描述来看,Service层承担了更大的责任,也就是说几乎所有的业务逻辑开发都需要在Servcie层完成。即使Web层可以为它共享一些简单的逻辑和参数验证,但随着时间的推移,随着业务的扩展和功能的叠加,单个类也会变得过于庞大和臃肿(现有代码也可以通过设计模式进行改进);其次,我们的总务加入了Service层。如果Service的某个方法的逻辑过于复杂,也会导致长事务和嵌套事务;四层模型。处理层、对外接口或第三方平台三个模块。以下内容与解释有关,同样引用自《阿里巴巴Java开发手册》。接口开放:可以直接封装Service方法,暴露为RPC接口;通过Web将其封装为http接口;进行网关安全控制、流量控制等。通用业务处理层:具有以下特点:1)第三方平台封装层,对返回结果进行预处理,异常信息转换;2)Service层通用能力的下沉,如缓存方案、中间件的通用处理;3)与DAO层交互,复用多个DAO的组合。对外接口或第三方平台:包括其他部门的RPC开放接口、基础平台、其他公司的HTTP接口。开放接口和对外接口或者第三方平台更好理解,就是给第三方提供接口,从第三方接口获取数据。在Dao层和业务逻辑层之间新增加了通用业务处理层,但是手册也给出了更明显的特性描述:第一篇【封装第三方平台的层,预处理返回结果和转换Exception信息]是为了说明Manager层可以作为第三方接口数据的接收层。在这一层可以发起请求,处理接口响应数据,比如将第三方返回的json数据转化为应用中的实体,或者将第三方返回的异常转化为应用中的异常,或者一些自定义处理等,那么这个就不会走Dao层;在第二篇【Service层通用能力的下沉,如缓存方案,中间件通用处理】这句话个人的意思是如果有AServcie和BServcie,AService需要调用BService的方法,那么BService的这个方法可以作为Service层的通用能力,然后下沉到Manager层,那么这里的逻辑可以是数据的缓存处理,也可以是调用中间件处理;第三篇中【与Dao层交互,复用多个Dao的组合】的意思是Service层调用Dao层如果多个Dao之间有联动操作或者需要联表查询,就会这时候比较臃肿写在Service层,然后在Manager层放置多个Dao操作,比如当你想增删数据同时处理A表和B表,那么这两个操作就可以了此时放在Manager层。如果没有上面提到的其他数据库操作,那么事务甚至可以放在Manager层,同样可以缩短事务时间;这里也可以标注Manager层,比如命名时使用XXXManager,注解时自定义@Manager注解,但不要滥用Manager层。逻辑复杂的时候可以用,逻辑简单的时候尽量不要用。毕竟会增加层的复杂度。;或者只是将第三方平台的接口放在Manager层进行处理;只要团队能达成共识,加这一层是有利无害的。层次领域模型项目在上面的内容中进行了分层之后,各层之间的数据传输是接下来要解决的问题。有些项目会直接使用数据库对应的对象作为各层之间的传输对象。这样做的好处是不需要维护那么多对象,但是问题是这个对象太重了,稍微修改一下就可能导致bug甚至上线失败。层次领域模型希望在每一层都有自己对应的领域模型。《阿里巴巴Java开发手册》有如下规定:DO(DataObject):与数据库表结构一一对应,通过DAO层向上传递数据源对象。DTO(DataTransferObject):数据传输对象,Service或Manager传输的对象。BO(BusinessObject):业务对象。封装Service层输出的业务逻辑的对象。AO(ApplicationObject):应用对象。Web层和Service层之间的抽象复用对象模型与表现层很接近,复用度不高。VO(ViewObject):显示层对象,通常是从Web传输到模板渲染引擎层的对象。Query:数据查询对象,每一层接收来自上层的查询请求。注意超过2个参数的查询封装禁止使用Map类传输。每一层都有输入和输出两个方向,因此每一层的输入参数和响应都有对应的模型。过分追求各层之间的转换也很麻烦。例如下图是我刚开始工作时画的领域模型传递过程:输入参数时,使用DTO作为传递对象到Mapper层(Dao层),然后转换成PO对象查询DB,以及输出时使用PO作为查询结果的存储对象。然后在Mapper层使用Vo(ValueObject),最后在Controller层使用Vo(ViewObject)作为最终对象响应客户端。看起来整个过程从request到response需要3-4次转换,在平时的开发过程中费时费力。其实这些层的领域模型怎么划分,或者说这些领域模型怎么命名,只要团队能达成一致,怎么做都是可以接受的。毕竟,规范代码是为了方便以后的代码维护,代码复用,提高开发效率。如果说追求代码分层模型导致开发效率降低,那就得不偿失了。这里介绍个人领域分层模型处理。Web层提供终端展示层时,输入参数统一命名为:XXXDto,统一响应为XXXVo;当对外提供接口和请求对外接口时,输入的参数名称统一为:XXXRequest,响应统一为XXXResponse。Dto可以直接从Web层请求到Dao层。Dao层查询响应之后是Entity实体层。如果查询是表连接,则返回Bo对象,或者在Manager层合并数据时也使用Bo对象返回,最后在Service层封装为Vo或Response后返回。上一节框架实践中说过,如果过分追求每一层的转换内容,那么开发效率会大大降低。然而,众所周知,Java的强大在于其日积月累积累的强大生态,各种开发框架应接不暇。比如Bean的转换,有BeanUtils、BeanCopier、Dozer、Orika、MapStruct等各种框架。当然,经过网上各种文章的评估,最安全高效的框架还是MapStruct。【详见链接5种常用Bean映射工具性能对比】介绍摘自mapstruct官网。从描述中可以看出,mapstruct是在编译期基于代码生成生成Bean映射处理器,效率高,也安全,编辑期间可以判断是否报错,大大减少程序执行期间出错的可能性。使用MapStruct也比较方便。具体使用方法在官方文档中有详细说明。mapstruct1.5.3文档有详细的文字说明和例子供参考。下面是几种常见的用法:当转换之间的对象属性相同时,不需要定义@Mapping来标识字段之间的映射关系。当需要字段映射时,通过@Mapping(source="fieldA,target="fieldB")进行映射;当不需要某个字段的映射时,可以通过@Mapping(target="id",ignore=true);当字段为常量时,可以通过@Mapping(target="payType",constant=1)忽略,标识该字段填充常量1;当需要通过变量获取字段时,可以通过@Mapping(target="time",expression="java(newjava.util.Date())"表示时间字段是为每个新的Date生成的时间,java()表示它是java代码的一个表达式;我在使用mapstruct的时候,会定义一个BaseConverter,其他常用的converter可以直接继承Converter,不用每次都写对应的方法,@Mapper(componentModel="spring")表示注入到Spring容器中,可以通过@Resource或者@Autowired注入;publicinterfaceBaseConverter{/***Entity-->Vo*/Voentity2Vo(Entityentity);/***实体-->Vo*/Listentity2Vo(ListentityList);/***Dto->Entity*/Entitydto2Entity(Dtodto);}@Mapper(componentModel="spring")公共接口AConverterextendsBaseConverter{}@Mapper(componentModel="spring")publicinterfaceBConverterextendsBaseConverter{}总结一下项目是怎么分层的,项目的领域模型是怎么设计的其实并不重要,并没有一个标准的规范来执行,每个团队在探索的过程中都会有自己的实践。归根结底是为了一个目的:让项目的代码高内聚低耦合,更加干净、清晰、易懂,让新开发者能够根据规范和提高团队中当前开发团队的效率,那么这件事情的意义就已经达到了。只要能够在团队中达成一致,开发??人员能够坚持执行,那么对于团队和开发来说,都是利大于弊的好事。参考链接我项目中的代码是如何分层的?阿里巴巴Java开发手册的同事问我代码结构中Manager层是干什么的。5种常用Bean映射工具性能对比