背景
之前为了开发上的简便高效,整个开发平台源码基本上一体的,模块进行了粗略拆分,分了三块,一是platform-common,存放公用基础,如工具类、父类;二是popsoft-generator,也就是代码生成器,基于库表和模板技术快速生成entity、vo、dao、service、controller、ui的各层代码;三是platform-core,命名是核心模块,除了以上两个模块外,其他内容都放在这里面,框架类的放在的外围,内部建了一个包,按功能模块存放,不仅包括平台的功能模块,后来开发的业务系统,比如通用接口平台、文档管理系统、微信小程序后台,也都塞到了里面,大概的层次目录是下面这个样子。
这样面临的一个显著问题,是多个业务系统虽然从逻辑上隔离的,但是平台支撑是耦合在一起的,对于公共部分,如权限管理、身份认证什么的没什么问题,但对于个性化需求部分,例如全文搜索,仅文档管理系统才需要引入的组件,不应该在其他系统系统中引用。
我的目标是,开发平台是公用的,可以不断完善提升,独立升级;多个业务系统公用一套开发平台。
更具体点,也就是以下两点:
平台公用,独立升级:一方面,业务系统需使用公用平台,如果一套业务系统一套平台,则平台的代码难以复用与运维;另一方面,平台应保持独立,为业务系统提供强有力技术支撑能力。
技术组件按需加载:部分公共通用技术组件几乎是必然会用到的,比如日志、缓存、任务调度,适合放到开发平台,但有些技术组件则是某些业务系统才会用到的,比如工作流、消息队列、规则引擎,因此需要处理好这部分组件,全部加载会使平台很重,会降低编译、运行速度,增大发布包体积,因此最好通过配置来实现按需加载。
解决思路
参照spring-boot的解决方式,开发平台自身内部模块化,对外输出一个starter的jar包,业务系统引入该jar即可实现平台基础与内核功能,在其基础上专注于业务系统开发即可。
对于技术组件的集成,则做成类似spring-boot-starter-****的jar包,按需引用即可。
规划
平台核心模块
popsoft-platform:聚合项目,用于管理开发平台各模块的源码。
popsoft-common:公用基础,包括工具类
popsoft-framework:平台框架
popsoft-system:系统管理模块,包括组织机构、人员、权限、日志
popsoft-generator:代码生成器,基于库表和模板技术快速生成entity、vo、dao、service、controller、ui的各层代码
popsoft-boot-starter:平台启动项目,整合平台基础功能,类似于spring-boot-starter,业务系统引入该包进行依赖
popsoft-boot:启动项目,可视为业务系统模拟,用于平台自身功能开发与调试
业务支撑模块
popsoft-support:业务支撑,提供单据流水号、消息通知、内容模板、门户等功能
能力扩展模块
通常是中间件的集成,具体的业务系统按需依赖
popsoft-boot-starter-mail:邮件
popsoft-boot-starter-oss: 对象存储
popsoft-boot-starter-scheduler:任务调度
……
实现
在实现过程中,进行了比较多的思考,在重构过程中进行模块的拆分与合并,包和类的转移、配置文件的调整,特别是梳理模块间的依赖关系,最终输出版本与之前的规划并不完全一致。
先把最后输出的模块架构图成果放出来,然后把重构过程中思考的一些问题点放在其后。
最终结果
首先分成两部分,一部分是平台内核模块,命名规则是popsoft+模块功能名称;另一部分是能力扩展模块,命名规则是popsoft-boot-starter+模块功能名称。
popsoft-common作为公用基础,主要包括工具类、公用注解、公共父类、公共常量、公共枚举值,与前端UI交互定义的vo类,该模块为最基础的模块,无前置依赖。
popsoft-system是平台最核心的模块,主要包括组织机构、人员、角色、权限、日志、系统参数这些实体和服务的实现,需要注意的是,权限控制、日志记录,并不是在该模块实现,而是在popsoft-framework平台框架中实现,该模块依赖于popsoft-common。
popsoft-framework是平台框架,负责身份认证、权限控制、全局配置、数据分页、日志处理、自动填充(创建人、创建时间、修改人、修改时间),因为身份认证、权限控制等功能,不可避免需要使用处于popsoft-system模块中的人员、角色等实体和服务,因此依赖于popsoft-system。
popsoft-support是一个业务支撑模块,基于技术组件进行功能设计与封装,实现一些通用的功能设计,更方便业务逻辑的实现,提供附件管理、内容模板(用于短信、邮件、消息)、通讯组、单据流水号、门户等功能。这些支撑模块同样需要位于popsoft-system模块中的人员、组织机构等实体和服务,因此依赖于popsoft-system。
popsoft-boot-starter:平台启动项目,整合平台基础功能,类似于spring-boot-starter,业务系统引入该包进行依赖。该模块自身没有实体与服务,而是汇总整合,把popsoft-framework和popsoft-support引用进来,同时进行配置。配置分两方面,一方面是做一个配置类,加一些注解(如:@EnableRetry、@ServletComponentScan、@EnableTransactionManagement),使用开发平台实现的业务系统,就不需要在启动类上重复添加这些注解;另一方面,是位于yml配置文件中的配置信息,也分为两部分,一部分是三方组件自身的,如数据源、连接池、redis、quartz、logback,另一方面是自定义的系统参数,如用户默认密码、导出excel数据的批次最大行数量。
popsoft-boot-starter-demo:示例项目,实际是模拟业务系统如何使用开发平台,用于平台自身功能开发与调试。
右侧四个模块,比较好理解,通常是对第三方组件的封装与整合,依赖于公共基础模块popsoft-common,这些模块可以不断扩展的,业务系统按需引入即可,这样就实现了核心模块必选、扩展模块可选的目的。
popsoft-boot-starter-mail:邮件
popsoft-boot-starter-oss: 对象存储
popsoft-boot-starter-scheduler:任务调度
popsoft-boot-starter-notification:消息通知
对于扩展模块,平台的核心模块实际也可能会用到,例如popsoft-support中的附件功能,就会用到popsoft-boot-starter-oss;popsoft-system中的自动解锁用户功能,就会用到popsoft-boot-starter-scheduler。
为了主体逻辑清晰,以上依赖关系没有在图中绘制出来。
思考点
framework与system谁位于下层?
这是一个比较纠结的点。
原先的想法,公共基础模块common之上,就应该是平台框架framework,由框架来实现身份认证、权限控制等,然后才是基于框架产生的system、support等模块。
实际遇到的问题,要做身份认证,肯定会用到用户服务,而用户服务是位于system模块下,也就是依赖关系倒置了。
尝试过一些方法,例如,将用户服务的接口UserService,下沉到common模块,问题是能解决,但是一定程度上破坏了system的内聚性。而且,涉及到的不仅仅是用户服务,在framework模块中,用户登录成功后,我们需要同样位于system模块下的组织机构服务OrganizationService,查下这个用户的所属的部门、公司、集团等信息,缓存到用户对象中。
转变下思路,将system模块,作为common模块之上的首个基础模块,从架构上反而更合理。
support与framework两个模块谁依赖谁?
两个模块都依赖于system,职责不同,初期规划是一个单向依赖,实际发现二者并不存在谁依赖谁的问题,并列反而最合适。
为什么要有boot-starter模块?
承接上个问题,客观上需要一个模块,来把平台核心模块都给组装起来,对外提供一个包,就像使用SpringBoot一样,业务系统要使用平台,只要引入spring-boot-starter这个项目即可,需要在这个项目中做一些全局的配置工作。
如果缺少这个模块,那么业务系统在使用的时候,必须同时引入framework和support两个包,这样就很别扭。
为什么要有boot-starter-demo模块?
这个模块实际作用有两个,一是平台自身功能开发与调试,需要一个测试启动点,因为平台最终是打成不可独立运行的jar包,提供给业务系统去依赖,没有这个模块,总不能拿一个具体业务系统作为启动项目吧;另一方面,这个模块也真实模拟业务系统如何使用开发平台,更容易发现平台自身存在的问题。