idea插件开发(一)DDD代码生成器和要生成的框架介绍

2,749 阅读9分钟

导读

本文介绍要生成的框架结构和代码生成插件要实现的功能,只有清楚结构才能根据需求生成对应的代码文件。插件是人写的,想实现什么样的功能,在学会如何编写插件后都可以实现,算是抛砖引玉了。小伙伴们有好的想法也可以在评论中提出来,我们一起讨论如何实现。

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上官方给出的分层和依赖关系图,分为如下层次:

  1. client层,定义对外提供的service和dto类,其中service可用于dubbo,而dto对象用于对外输出和接收请求;
  2. adapter层依赖app,对外提供controller接口和相应的适配程序,如pc和wap这类适配
  3. app层依赖client、domain和infra,app层对请求进行调度(如调用domain或直接调用infra),并返回Result,如果需要发布事件和监听事件也放在这一层,发布事件是解耦的一个好方法,当一个聚合根发生变化时,发布事件,另一个聚合根监听该事件并做相应的处理,两个聚合根之间没有强依赖,只需要最终一致即可;
  4. domain层,可以依赖client层,但其他层都不依赖,对infra层的调用通过gateway接口来实现依赖反转。domain层是整个系统中业务的核心层,业务逻辑都写在这一层,包含:领域对象、领域服务、领域事件和定义对infra层调用的gateway接口;
  5. infra依赖domain,实现domain层定义的gateway接口,实现持久化存储;
  6. start,在上图中没有体现,这层只提供spring的启动类和application.yml配置;
  7. 组件,上图中的组件指的是一些通用的模块,如异常、通用dto(Result和Page这些),在我们要生成的代码中,组件里包含一些第三方的引入和配置类,如统一的异常处理和swagger这些通用的代码。

根据上面各层的说明,这也是很多DDD书箱给出的分层思想,大家可以对照一下《领域驱动设计 软件核心复杂性应对之道》这本书。

分层理解

针对这个分层的思想,可以这么去理解: 我们把一个软件看成是一家公司,我们假设公司生产钢达姆机器人和动感超人玩偶,由于这些玩偶需要包装、存储和配送,所以公司里还有负责生产包装盒的部门以及仓库和物流部门。其实软件的架构和公司组织架构差不多,都是希望通过好的架构来提高运转的效率。

  1. client层是对外的宣传海报,告诉其他人我们公司有哪些产品,可以提供哪些服务,如:钢达姆机器人和动感超人玩偶;
  2. adapter层是公司的前台或客服,负责接收客户的讲求,客户的请求在这一层只是原始的基础数据,如Long, String等,这里说的基础数据并不是int, long这些,而是相对于值对象来说Long是基础数据了。比如客户请求订购哆啦A梦玩偶,但是公司里不生产此产品,所以这种请求直接被客服pass掉了;
  3. app层是各部门的经理,前台将客户请求筛选后发送给对应的经理,假设有客户请求订购钢达姆机器人,则adapter层客服根据client的定义拨打钢达姆机器人经理A的电话,经理A是个老油条,和客服妹子聊的很开心,然后转手就将这个需求发给了下面的产品经理;
  4. domain层是各个部门里的产品经理,此时产品经理对应一个聚合根,对该部门产品的需求全部先找产品经理,产品经理带领几个团队(就是DDD中其他非聚合根的实体)。产品经理带团队分析需求、设计模型、加班加班加班,苦B的很,这也是整个公司里真正技术的核心。同时产品经理还需要从infra层拿源材料,也需要将生产好的产品送到infra层保存,但是这回产品经理发怒了,说老子日常007,还要看你们这帮仓储部门的脸色,现在我把话撂这了要么我定好要哪些材料,你们infra层照我的清单整好材料,要么老子辞职回家养猪,我不管你们从哪搞来材料,也不管你们怎么存储;
  5. infra层是公司里的基础服务部门,提供源材料和成品的存储,由于domain层产品经理这么一闹,于是infra仓储部门也妥协了,好吧,你要啥就给你啥。不过仓储部门也有自己的难处,公司里这个叫MySQL的老仓库快放不下了,公司里最近业务大增,进了很多源材料也堆积了很多成品,仓储部门和公司领导商量,有人说咱们再建一个MySQL仓库吧,也有人说最近听说叫MongoDB的仓库很流行,咱们也可以建一个,讨论过程中有个小伙子提了一句,这个事要不要和domain层那个装B的产品经理说一下,要不他哪天来拿货和送货会找不到路的。。。
  6. start层是公司的领导,没有start一切都是空,所谓代码即是空,空即是色。。。这一层作为领导,当然是提供资源了,像MySQL、Redis这些资源。

经过上面这样理解,相信伙伴们应该能清楚各个层的职责,例子中对公司组织架构的划分可能不合适,但是能够帮助理解就好,毕竟我也不是大公司里设计公司组织架构的高级领导。

包名介绍

下图引用自cola github

从上图中可以看到,事件消息发布和消费,以及定时任务都放在app层;而mybatis mapper放在infra层,也可以把和持久化存储的DAO操作接口定义在infra,把具体实现单独拿出来作为一个模块,这样更换具体实现时就更方便。

此次插件开发只是生成基础代码,会保留上图中必选的包

POJO介绍

下面再说说每一层的POJO类,也是引用cola github上的图

这张图需要注意以下几点:

  1. Entity实体类,指的是领域实体,不再是数据库实体;
  2. 数据库实体叫Data Object,简写DO,如UserDo;
  3. infra层需要对DO和Entity进行双向转换,需要提供一个converter
  4. app层需要将Entity转为Dto,也需要提供一个converter,由于infra层也有converter,所以在app层改名为assembler,以示区分;
  5. 本文生成的代码中,DTO就是对外输出的对象了,不再创建VO,每多一层对象就需要转换一次。

代码生成器的功能

上面一直在说要生成的框架的介绍,这个不说清楚下面没办法进行。下面介绍一下咱们要实现的插件有哪些功能:

  1. idea新建一个maven项目或打开一个空目录,可以右击生成pom.xml、component和business模块,之后component模块下存放一些组件(如swagger等),而business模块下存放业务代码;
  2. 右击component模块生成一些常用的组件子模块;
  3. 右击business模块生成子业务模块(如business-system、business-user等,一个子业务模块即一个微服务)或把cola所有模块都生成到business模块下(整个程序是一个单体应用);
  4. 指定聚合根名并选择有哪些请求方法(如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)等等,下一篇文章就先来编写这个基础框架

觉得不错的朋友欢迎点赞支持一下,非常感谢