导读
本文介绍要生成的框架结构和代码生成插件要实现的功能,只有清楚结构才能根据需求生成对应的代码文件。插件是人写的,想实现什么样的功能,在学会如何编写插件后都可以实现,算是抛砖引玉了。小伙伴们有好的想法也可以在评论中提出来,我们一起讨论如何实现。
cola框架
开始之前要感谢阿里分享的cola框架,当前最新版本是4.0,这个框架是简洁框架,也可以用来作为DDD框架的基础,业内应该有不少团队在使用这个框架吧。框架自身提供了maven生成工具,不过只能生成example代码,生成方式如下:
mvn archetype:generate \
-DgroupId=com.alibaba.cola.demo.web \
-DartifactId=demo-web \
-Dversion=1.0.0-SNAPSHOT \
-Dpackage=com.alibaba.demo \
-DarchetypeArtifactId=cola-framework-archetype-web \
-DarchetypeGroupId=com.alibaba.cola \
-DarchetypeVersion=4.0.1
生成好之后结构如下图:
├── demo-web-adapter
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── alibaba
│ └── demo
│ ├── mobile
│ │ └── CustomerMobileAdaptor.java
│ ├── wap
│ │ └── CustomerWapAdaptor.java
│ └── web
│ └── CustomerController.java
├── demo-web-app
│ ├── pom.xml
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── alibaba
│ │ └── demo
│ │ ├── customer
│ │ │ ├── CustomerServiceImpl.java
│ │ │ └── executor
│ │ │ ├── CustomerAddCmdExe.java
│ │ │ └── query
│ │ │ └── CustomerListByNameQryExe.java
│ │ └── order
│ │ └── OrderServiceImpl.java
├── demo-web-client
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── com
│ └── alibaba
│ └── demo
│ ├── api
│ │ └── CustomerServiceI.java
│ └── dto
│ ├── CustomerAddCmd.java
│ ├── CustomerListByNameQry.java
│ ├── data
│ │ ├── CustomerDTO.java
│ │ └── ErrorCode.java
│ └── event
│ ├── CustomerCreatedEvent.java
│ └── DomainEventConstant.java
├── demo-web-domain
│ ├── pom.xml
│ └── src
│ ├── main
│ │ └── java
│ │ └── com
│ │ └── alibaba
│ │ └── demo
│ │ └── domain
│ │ ├── customer
│ │ │ ├── CompanyType.java
│ │ │ ├── Credit.java
│ │ │ ├── Customer.java
│ │ │ ├── CustomerType.java
│ │ │ ├── SourceType.java
│ │ │ ├── domainservice
│ │ │ │ └── CreditChecker.java
│ │ │ └── gateway
│ │ │ ├── CreditGateway.java
│ │ │ └── CustomerGateway.java
│ │ ├── order
│ │ │ └── Order.java
│ │ └── package-info.java
├── demo-web-infrastructure
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── alibaba
│ │ │ └── demo
│ │ │ ├── config
│ │ │ │ └── DiamondConfig.java
│ │ │ ├── customer
│ │ │ │ ├── CreditGatewayImpl.java
│ │ │ │ ├── CustomerDO.java
│ │ │ │ ├── CustomerGatewayImpl.java
│ │ │ │ └── CustomerMapper.java
│ │ │ └── order
│ │ │ └── OrderGatewayImpl.java
│ │ └── resources
│ │ ├── logback-spring.xml
│ │ └── mybatis
│ │ ├── customer-mapper.xml
│ │ └── mybatis-config.xml
├── pom.xml
├── start
│ ├── pom.xml
│ ├── src
│ │ ├── main
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ └── alibaba
│ │ │ │ └── demo
│ │ │ │ └── Application.java
│ │ │ └── resources
│ │ │ ├── application.properties
│ │ │ └── logback-spring.xml
这是一个example工程,当新增一个聚合根时,需要创建大概20多个文件和package(比如query和cmd就有很多,通常有分页查询、列表查询、添加/编辑、状态修改和删除等请求),这个过程是很繁琐的,如果能够写一个插件来实现这些操作,然后在生成好的代码中再添加自己需要的代码和逻辑,这就很爽,把简单的事件交给程序来做,让程序员把这些时间花在领域知识上,只有理解这个领域才有可能写出好的程序。
分层介绍
框架我们采用阿里开源的cola框架的分层设计,模块的名字和cola保持一致,只有infrastructure
这个模块的名字太长了,我给改成了infra
,下面重点介绍cola的分层和每一层的功能
上图是cola github上官方给出的分层和依赖关系图,分为如下层次:
- client层,定义对外提供的service和dto类,其中service可用于dubbo,而dto对象用于对外输出和接收请求;
- adapter层依赖app,对外提供controller接口和相应的适配程序,如pc和wap这类适配
- app层依赖client、domain和infra,app层对请求进行调度(如调用domain或直接调用infra),并返回Result,如果需要发布事件和监听事件也放在这一层,发布事件是解耦的一个好方法,当一个聚合根发生变化时,发布事件,另一个聚合根监听该事件并做相应的处理,两个聚合根之间没有强依赖,只需要最终一致即可;
- domain层,可以依赖client层,但其他层都不依赖,对infra层的调用通过gateway接口来实现依赖反转。domain层是整个系统中业务的核心层,业务逻辑都写在这一层,包含:领域对象、领域服务、领域事件和定义对infra层调用的gateway接口;
- infra依赖domain,实现domain层定义的gateway接口,实现持久化存储;
- start,在上图中没有体现,这层只提供spring的启动类和application.yml配置;
- 组件,上图中的组件指的是一些通用的模块,如异常、通用dto(Result和Page这些),在我们要生成的代码中,组件里包含一些第三方的引入和配置类,如统一的异常处理和swagger这些通用的代码。
根据上面各层的说明,这也是很多DDD书箱给出的分层思想,大家可以对照一下《领域驱动设计 软件核心复杂性应对之道》这本书。
分层理解
针对这个分层的思想,可以这么去理解: 我们把一个软件看成是一家公司,我们假设公司生产钢达姆机器人和动感超人玩偶,由于这些玩偶需要包装、存储和配送,所以公司里还有负责生产包装盒的部门以及仓库和物流部门。其实软件的架构和公司组织架构差不多,都是希望通过好的架构来提高运转的效率。
- client层是对外的宣传海报,告诉其他人我们公司有哪些产品,可以提供哪些服务,如:钢达姆机器人和动感超人玩偶;
- adapter层是公司的前台或客服,负责接收客户的讲求,客户的请求在这一层只是原始的基础数据,如Long, String等,这里说的基础数据并不是int, long这些,而是相对于值对象来说Long是基础数据了。比如客户请求订购哆啦A梦玩偶,但是公司里不生产此产品,所以这种请求直接被客服pass掉了;
- app层是各部门的经理,前台将客户请求筛选后发送给对应的经理,假设有客户请求订购钢达姆机器人,则adapter层客服根据client的定义拨打钢达姆机器人经理A的电话,经理A是个老油条,和客服妹子聊的很开心,然后转手就将这个需求发给了下面的产品经理;
- domain层是各个部门里的产品经理,此时产品经理对应一个聚合根,对该部门产品的需求全部先找产品经理,产品经理带领几个团队(就是DDD中其他非聚合根的实体)。产品经理带团队分析需求、设计模型、加班加班加班,苦B的很,这也是整个公司里真正技术的核心。同时产品经理还需要从infra层拿源材料,也需要将生产好的产品送到infra层保存,但是这回产品经理发怒了,说老子日常007,还要看你们这帮仓储部门的脸色,现在我把话撂这了要么我定好要哪些材料,你们infra层照我的清单整好材料,要么老子辞职回家养猪,我不管你们从哪搞来材料,也不管你们怎么存储;
- infra层是公司里的基础服务部门,提供源材料和成品的存储,由于domain层产品经理这么一闹,于是infra仓储部门也妥协了,好吧,你要啥就给你啥。不过仓储部门也有自己的难处,公司里这个叫MySQL的老仓库快放不下了,公司里最近业务大增,进了很多源材料也堆积了很多成品,仓储部门和公司领导商量,有人说咱们再建一个MySQL仓库吧,也有人说最近听说叫MongoDB的仓库很流行,咱们也可以建一个,讨论过程中有个小伙子提了一句,这个事要不要和domain层那个装B的产品经理说一下,要不他哪天来拿货和送货会找不到路的。。。
- start层是公司的领导,没有start一切都是空,所谓代码即是空,空即是色。。。这一层作为领导,当然是提供资源了,像MySQL、Redis这些资源。
经过上面这样理解,相信伙伴们应该能清楚各个层的职责,例子中对公司组织架构的划分可能不合适,但是能够帮助理解就好,毕竟我也不是大公司里设计公司组织架构的高级领导。
包名介绍
下图引用自cola github
从上图中可以看到,事件消息发布和消费,以及定时任务都放在app层;而mybatis mapper放在infra层,也可以把和持久化存储的DAO操作接口定义在infra,把具体实现单独拿出来作为一个模块,这样更换具体实现时就更方便。
此次插件开发只是生成基础代码,会保留上图中必选的包
POJO介绍
下面再说说每一层的POJO类,也是引用cola github上的图
这张图需要注意以下几点:
- Entity实体类,指的是领域实体,不再是数据库实体;
- 数据库实体叫Data Object,简写DO,如UserDo;
- infra层需要对DO和Entity进行双向转换,需要提供一个converter
- app层需要将Entity转为Dto,也需要提供一个converter,由于infra层也有converter,所以在app层改名为assembler,以示区分;
- 本文生成的代码中,DTO就是对外输出的对象了,不再创建VO,每多一层对象就需要转换一次。
代码生成器的功能
上面一直在说要生成的框架的介绍,这个不说清楚下面没办法进行。下面介绍一下咱们要实现的插件有哪些功能:
- idea新建一个maven项目或打开一个空目录,可以右击生成pom.xml、component和business模块,之后component模块下存放一些组件(如swagger等),而business模块下存放业务代码;
- 右击component模块生成一些常用的组件子模块;
- 右击business模块生成子业务模块(如business-system、business-user等,一个子业务模块即一个微服务)或把cola所有模块都生成到business模块下(整个程序是一个单体应用);
- 指定聚合根名并选择有哪些请求方法(如page, list, get, add, edit, status, delete等)生成对应的代码。此次插件开发基于mybatis插件生成DO类和Mapper,打开DO类右击生成代码。基于DO类的原因是想根据DO类判断当前DO是不是树型表(就是那种无限级分类),当然这个完全可以在生成代码时弹出的窗口中选择,只是不想麻烦。
编写一个通用的基础框架
cola框架提供了分层的设计思想,没有一些基础的代码,每个项目全部从头再来显然是不现实的,所以咱们需要新建一个项目来编写一些通用的代码,如接口、异常、值对象、mybatis的封装等,像常用的值对象(UserName, Password, UserId, LongId)、DO类(BaseDo, BaseTreeDo)和转换接口(内置常用对象的互相转换,如UserName转String)等等,下一篇文章就先来编写这个基础框架
觉得不错的朋友欢迎点赞支持一下,非常感谢