在这篇文章开始之前,我想问你一个问题。您认为业务代码,尤其是互联网业务代码的特点是什么?以下是我能想到的几点:互联网业务迭代快,交期紧,导致代码结构混乱,几乎没有代码注释和文档。互联网人员变动频繁,很容易接手别人的旧项目。新人根本没时间去了解代码结构,紧迫的工期只能让这堆狗屎越来越大。很多人一起开发,每个人的编码习惯不同,各自使用不同的工具代码,业务命名经常冲突,影响效率。大多数团队几乎没有时间进行重构,任由代码腐烂。每当我们开始一个新的代码仓库时,都是信心满满,结构整齐的。但随着时间的推移,代码变得腐败,技术债务增加。这种情况有解决办法吗?还有:群里定期进行代码重构,解决技术债。组内精心设计的应用架构让代码烂得更慢。(当然很难做到完全不烂)设计尽量简单,让不同层级的开发者都能快速理解并上手开发,而不是在一堆复杂的代码上堆更多的狗屎没有人明白。而我们今天的主角COLA,就是提供一个实用的业务代码结构规范,让你的代码越烂越慢,团队的开发效率越快越好。什么是可乐?COLA是阿里巴巴老板张建飞提出的一种业务代码架构的最佳实践,目前已经作为阿里云脚手架代码生成器的一个选项,可见其已经具有了一定的影响力。COLA是CleanObject-OrientedandLayeredArchitecture的缩写,全称是“CleanObject-OrientedandLayeredArchitecture”。在最新版本的COLA4.0中,作者将COLA分为两部分:COLAArchitecture(原型)和COLAComponents(组件):COLAArchitecture:COLA应用程序的代码模板。COLA组件:提供一些非常好用的通用组件,可以帮助我们提高研发效率。两者互不干扰,可以独立使用。COLA的整体架构首先主要说说COLA的架构,在COLA的官方博文中有介绍:在我们平时的业务开发中,大部分系统需要:接收请求,响应响应;进行业务逻辑处理,如校验参数、状态流、业务计算等;与外部系统有联动,如数据库、微服务、搜索引擎等;正是因为有这样的共性,才出现了很多通用的架构思想,比如分层架构、六面Polygon架构、洋葱圈架构、干净架构(CleanArchitecture)、DDD架构等。虽然这些应用架构思想很好,但是很多我们同学还是“不谈道德,明白了很多道理,但还是过不了好日子”。问题是缺乏实践和指导。COLA的意义在于它不仅是一种理念,更是一种实践。应该是为数不多的应用架构层面的开源软件之一。COLA提供了一套完整的代码架构,随时可用。它包含了很多架构设计思想,包括讨论度很高的领域驱动设计DDD等等。注:每个人对建筑设计都有自己的理解。所以对于COLA的结构,本文只是本人对COLA的粗浅了解,大家可以批评指正。COLA的分层结构先来看两张官方介绍图。其次,官方有一张表介绍了COLA中各层的命名和含义:层包名函数必须选择Adapter层。无线处理无线端适配无Adapter层wap处理Wap端适配无App层执行器处理请求,包括命令和查询App层消费者处理外部消息无App层调度器处理定时任务无Domain层模型领域模型无Domain层能力Domain能力包括DomainService、Domain层网关、域网关。解耦工具是Infra层gatewayimpl网关实现。两张图一张表已经把整个COLA架构的大部分展示给大家了,但是一下子消化这么多信息可能有点困难。由于整个示例架构项目是一个Maven父子结构,所以我们从父模块开始,一一过一遍。首先,父模块的pom.xml包含以下子模块:demo-web-clientdemo-web-adapterdemo-web-appdemo-web-domaindemo-web-infrastructurestartstart该模块作为启动模块整个应用(通常是SpringBoot应用),只承担启动项目和全局相关配置项的存储责任。代码目录如下:startup分开了,好处是清晰简洁,也能让新手一眼就看出项目是如何运行的,以及项目的一些基本依赖。Adapter层接下来我们按照上一个架构图从上到下的顺序一一来看。首先是demo-web-adapter模块,这个名字是不是很新鲜?但其实可以理解为我们平时使用的controller层(针对Web应用),换汤不换药。在COLA官方博客中,还可以找到如下描述:Controller这个名字主要来源于MVC,因为是MVC,所以自带Web应用的烙印。但是随着移动端的兴起,只支持Web端的应用已经很少了。通常的标准配置是Web、Mobile、WAP三端都支持。cilent层有我们所说的“controller”层,有朋友肯定会疑惑是不是service层。是和不是。在传统的web应用中,controller层调用的服务层只能有一个,但是作为业务应用,除非你真的只是前端页面上一个无情的数据吐槽机,否则很有可能你的应用程序将有许多其他下游调用者,您需要为他们提供一个接口。这时候你给他们的应该不是web接口,而是RPC调用的服务层接口。原因不是本文的重点,就不详细展开了。所以在COLA中,你的适配器层调用了客户层,客户层就是你服务接口的定义。从上图可以看出,客户端包包含:api文件夹:存储服务接口定义dto文件夹:存储传输实体注意,这里只是服务接口定义,不是服务层的具体实现,所以在adapter中层,调用的其实是客户端层的接口:@RestControllerpublicclassCustomerController{@AutowiredprivateCustomerServiceIcustomerService;@GetMapping(value="/customer")publicMultiResponselistCustomerByName(@RequestParam(required=false)字符串名称){CustomerListByNameQrycustomer=newCustomerListByNameQry();customerListByNameQry.setName(名称);返回customerService.listByName(customerListByNameQry);}而最终接口的具体实现逻辑放在app层。@Service@CatchAndLogpublicclassCustomerServiceImplimplementsCustomerServiceI{@ResourceprivateCustomerListByNameQryExecustomerListByNameQryExe;@OverridepublicMultiResponselistByName(CustomerListByNameQrycustomerListByNameQry){返回customerListByNameQryExe.execute(customerListByNameQry);}}app层接口上面说的,我们的app模块作为服务的实现存储了各个业务的实现类,严格按照业务进行分包。这里的重点是先按业务分包,再按功能分包。你为什么要这样做?文章后面再多说几句,先看图:customer和order分别对应consumer和order这两个业务子区。它包含COLA在app层定义的以下三个功能:App层executor处理请求,包括command和query,App层consumer是否处理外部消息,App层scheduler是否处理定时任务。可以看到消息队列是一个消费者和定时任务。我们业务开发经常遇到的场景,也是放在app层的。domainlayer后面是domain,也就是领域层。我们来看一下领域层的整体结构:可以看到,首先是根据不同的领域(customer和order)进行分包,里面主要有三种文件类型:domainEntity:实体模型可以是血型(请自行理解)。比如官方例子中的Customer.java如下:@Data@EntitypublicclassCustomer{privateStringcustomerId;私有字符串成员ID;私有字符串globalId;私人长期注册资本;私有字符串公司名称;私有源类型源类型;私人公司类型公司类型;publicCustomer(){}publicbooleanisBigCompany(){returnregisteredCapital>10000000;//注册资本大于1000万的大公司}publicbooleanisSME(){returnregisteredCapital>10000&®istered<1000000;//注册资本10万以上100万以下的为中小企业}publicvoidcheckConfilict(){//不同的biz,checkpolicy可能不同,如果是,使用ExtensionPointif("ConflictCompanyName".equals(this.companyName)){thrownewBizException(this.companyName+"已经存在,不能添加&q不;);}}}域能力:在domainservice文件夹下,是域对外暴露的服务能力,比如上图中的CreditChecker域网关:gateway文件夹下的接口定义,这里的接口可以大致理解为一种SPI的,也就是交给基础设施层去实现的接口。比如在CustomerGateway中定义了接口getByById,这就要求基础设施的实现类必须定义如何通过消费者Id获取消费者实体信息,基础设施层可以实现任意数据源逻辑,比如从Getit从MySQL获取,从Redis获取,或者从外部API获取等。entity)由MyBatis查询,转换为Customer域实体,并返回。依赖倒置完成。@ComponentpublicclassCustomerGatewayImplimplementsCustomerGateway{@AutowiredprivateCustomerMappercustomerMapper;publicCustomergetByById(StringcustomerId){CustomerDOcustomerDO=customerMapper.getById(customerId);//转换为Customerreturnnull;基础设施层,这一层有我们刚才说的gatewayimpl网关实现,还有MyBatismapper等数据源的mapping和config配置文件。InfralayergatewayimplgatewayimplementationisInfralayermapperibatisdatabasemapping否Infralayerconfig配置信息否(AdapterLayer):负责前端显示(web,wireless,wap)的路由和适配。对于传统的B/S系统,适配器相当于MVC中的控制器;2)应用层(ApplicationLayer):主要负责获取输入,组装上下文,参数校验,调用领域层进行业务处理,必要时发送消息通知。层开放,应用层也可以绕过领域层,直接访问基础实现层;3)领域层(DomainLayer):主要封装核心业务逻辑,通过领域服务(DomainService)和领域对象(DomainLayerEntity)为App层提供业务实体和业务逻辑计算。域是应用程序的核心,不依赖于任何其他层;4)InfrastructureLayer:主要负责技术细节的处理,比如数据库的CRUD、搜索引擎、文件系统、分布式服务的RPC等。此外,领域防腐的重任也落在这里,以及外部依赖需要通过网关进行转义,才能被上层的App层和Domain层使用。COLA架构的特点说完了分层架构,我们再来回顾一下COLA架构的上述几个特点。设计领域和功能的分包策略就是下图的意思。先按领域分包,再按Functional分包,这样做的好处之一就是可以控制业务领域内的腐败。比如customer和order这两个领域,就是并行开发的两个后端开发。两个人对dto、util等文件夹的命名习惯不同,所以只会rot在各自的业务包下,而dto是不会被收录的。、util、config等文件夹放在一起,很容易造成文件冲突。上述包定义都是功能维度的定义。为了兼顾领域维度的内聚性,我们需要对包结构进行微调,即顶层包结构要按照领域进行划分,使领域具有内聚性。解耦业务领域和外部依赖上面提到的领域和基础设施层的依赖倒置是一个非常有用的设计,它进一步解耦了数据访问逻辑的实现。例如,在下图中,您的域实体是一个产品项。通过网关接口,您产品的数据源可以是数据库,也可以是外部服务API。如果是对外的商品服务,你调用API后,商品域会吐出一个大而全的DTO(可能包含几十个字段),而在下单阶段,订单需要的可能只是一个很少的领域。.你在外域获取DTO,在自己域中转化为Item,只留下title、price、inventory等必要的数据字段。可乐并不完美。诚然,COLA足够清晰和简洁,但它仍然有不完善的地方。比如每个接口的输入输出参数都会根据业务名称来定义,导致爆出很多结构非常相似的DTO。成长是个问题。参考:ISSUE-271但总的来说,COLA只是给你提供一个架构设计的思路,并没有深入到强制你使用某种规范的层面,让你觉得复杂或者不明白的地方在COLA,很多时候需要自己取舍取舍。取其精华,去其糟粕应用到你的项目中。综上所述,COLA架构并不复杂。COLA已从1.0版简化为当前形式。作为阿里云代码脚手架生成器中的一个选项,说明它已经成熟了。