此文已由作者黎星授权网易云社区发布
欢迎访问网易云社区,了解更多网易技术产品运营经验
投放工程架构调整
有了前面的“理论基础”,以及跃跃欲试的心动,我们来对投放工程的架构做一次调整和优化,原则是不改变原有的业务逻辑,目的是使投放工程的业务边界和业务功能更为清晰。
旧工程架构
资源投放系统一共分为4个工程,分别是
-
后端前台工程:kaola-resource-app
-
后端离线工程:kaola-resource-offline
-
后端后台Web工程:kaola-resource-web
-
前端后台fed工程:kaola-resource-fed
其中,kaola-resource-app和kaola-resource-offline提供纯dubbo服务。app提供前台服务,负责给其他业务在线获取投放的素材信息;offline提供离线服务,负责给其他业务的后台系统获取投放相关的数据。kaola-resource-web和kaola-resource-fed分别是投放系统的Web后端和前端工程,资源的分配、审核、投放都在该系统完成。kaola-resource-fed属于前端工程,不在本文讨论范围内,下面介绍三个后端工程的架构体系。
工程依赖关系
工程依赖关系如图所示,主要分为几类,分别是工具模块、接口模块、业务模块以及启动模块,箭头表示依赖关系。
-
工具模块
kaola-common-cache:主要包含Solo缓存中间件的封装,JVM缓存相关的不在该模块里。
kaola-common-util:工具类,包括常量、异常信息及util类。
kaola-resource-search->kaola-resource-generic:ndir索引服务工程。
kaola-resource-schema(图中未列出):存放投放自带部分模板xml的地方,暂无其他工程引用。
-
接口模块
kaola-resource-api:提供在线和离线服务的dubbo接口。
kaola-resource-generic:通过mybatis与数据库打交道,提供数据的工程,相当于DAO层。
kaola-resource-facade->kaola-resource-generic:提供Web层的facade接口,相当于Service层。
-
业务实现
kaola-resource-cache->kaola-resource-api、kaola-resource-generic、kaola-common-cache:提供业务报警、disconf配置、JVM缓存、NCR缓存、熔断、业务监控、线程池管理等功能,且提供resource-api部分facade接口的实现、Service接口定义与实现,是resource-app工程的业务实现工程。
kaola-resource-provider->kaola-resource-facade、kaola-resource-api、kaola-service-util、kaola-resource-search、kaola-resource-generic:提供web工程facade接口的实现、Service接口定义与实现。
kaola-resource-service->kaola-resource-generic、kaola-resource-api:提供offline工程facade接口的实现、Service接口定义与实现。
kaola-service-util->kaola-resource-facade、kaola-common-util、kaola-resource-api:主要包含外部模板XML校验业务的工程。
-
启动模块
kaola-resource-app->kaola-resource-cache、kaola-common-cache、kaola-resource-api、kaola-resource-generic:app工程启动入口,SpringBoot配置。
kaola-resource-offline->kaola-resource-service、kaola-resource-generic、kaola-resource-api:offline工程启动入口,SpringBoot配置。
kaola-resource-fed:前端后台工程
kaola-resource-web->kaola-resource-facade、kaola-resource-provider、kaola-resource-facade、kaola-resource-api、kaola-service-util、kaola-resource-search、kaola-resource-generic、kaola-common-util:web工程启动入口,提供http服务。
现有工程问题
-
工程模块命名混乱
刚接触投放工程的时候完全不知道从何入手,只能根据启动工程的依赖关系找到对应的业务模块。总工程里有4个小工程,通常找一个模块的业务实现不知道去哪个模块去找。例如,从命名上来看,并不知道kaola-resource-facade是web工程的业务接口,kaola-resource-provider是web工程的业务实现,kaola-resource-service是offline工程的业务实现,对新人来说命名不够友好。
-
模块职责不清晰
由上面梳理的模块功能可以知道,kaola-resource-cache工程不仅承担了缓存管理、业务报警、熔断等基础业务,也是app工程的业务实现;kaola-resource-service既提供了业务接口,又提供了业务实现;kaola-common-util没有发挥它作为工具类应该提供的职责和义务。总体而言,模块的职责可以通过一定的修改变得更为清晰。
-
工具类、常量类管理混乱
目前几乎每个工程下面都有一个util包,且工程包含多个BusinessException/ParameterException类,带来的问题是使用时不知道用哪个类。工具类与常量类应统一为一个工具类模块,统一管理。
-
版本依赖管理
每个工程的依赖目前都是独立管理的,存在不同工程依赖中间件版本不一致的情况。主工程的pom管理机制可以优化一下,比如disconf是每个工程都要使用的,可以放在父工程使用dependencyManagement统一管理。
-
业务代码分支多
当前工程存在很多业务分支,遇到业务分支时大部分是通过if/else判断的,修改一个业务点需要修改很多地方,通常很容易遗漏,造成隐藏的bug。
新工程架构
考虑到现有工程架构的特点,在新工程的设计上采用平滑过渡的方式,后端主体还是分为web/offline/app三个小工程,这三个小工程分别采用传统的MVC架构。前端工程由于与后端工程关系不大,后续可以迁移到新工程里。
工程依赖关系调整
针对旧工程中存在的不合理的点,以及工程中存在的问题,我们对工程及工程的依赖关系进行了如下调整。
红色背景为删除或整合的模块,包括kaola-service-util、kaola-common-util、kaola-common-cache。 黄色背景为重命名的模块,目的是使模块功能定位更清晰,包括
-
kaola-resource-provider->kaola-resource-web-provider,表示web工程的业务实现
-
kaola-resource-facade->kaola-resource-web-facade,表示web工程的业务接口
-
kaola-resource-generic->kaola-resource-dao,表示对象关系映射,即DAO层
-
kaola-resource-service->kaola-resource-offline-provider,表示offline工程的业务实现
绿色背景为新增模块,包括kaola-resource-util、kaola-resource-offline-facade、kaola-resource-app-provider、kaola-resource-app-facade,模块的定义也比较明确。 红色的线为可以移除的依赖关系,黄色的线表示修改为runtime依赖,绿色的线表示新增依赖。
经过上述调整后,一张更为清晰的投放工程架构图便展现出来了。
工程架构基本思想
新的工程架构,模块间的职责与依赖关系更为清晰。
从模块的职责来说,浅蓝色表示底层提供的工具类(包括各种util、缓存、索引服务等);绿色表示数据对象关系映射服务层;黄色表示对第三方暴露的api;粉红色表示对内提供的业务接口;土黄色表示业务接口的真正实现;深蓝色则是工程的启动入口,包含各种配置文件。
从模块的依赖关系来说,启动工程在编译期仅依赖业务接口,运行时才会依赖业务实现以及与业务实现相关的底层服务,这一操作通过maven的scope标签完成,编译期编译使用的标签为<scope>compile</scope>
,运行时编译使用的标签为<scope>runtime</scope>
。索引、缓存、工具类作为基础服务以独立的模块存在,需要与数据库打交道的模块则直接依赖kaola-resource-dao即可。
对于当前的架构还不是最完美的,存在两个问题。
-
kaola-resource-cache提供了kaola-resource-api的实现,应将这一部分的业务逻辑移至kaola-resource-app-provider中。
-
业务接口依赖了kaola-resource-dao,这一部分因为接口使用了dao层的Entity,需要重新规划建模,去除对dao层的依赖。
该架构的设计可以做到业务层仅和接口打交道,在业务层实现业务逻辑时,不关心底层服务的实现。业务层也没有办法直接和dao层打交道,避免数据被业务层无故修改。
工程模块划分
-
基础模块/服务
kaola-resource-fed:前端工程单独拆出,不与后端工程公用同一个git仓库
kaola-resource-util:工具类、异常类、常量,可以被所有子工程引用
kaola-resource-cache:缓存相关的模块,需要使用缓存的工程引用此module
kaola-resource-index:索引工程服务
kaola-resource-dao:数据库dao层
kaola-resource-api:暴露对第三方提供的所有服务api,包括online和offline接口
kaola-resource-facade-base:存放所有facade工程的基础接口和异常类,上述流程图没有展示出来,但确实是有必要的基础模块。
-
web工程
kaola-resource-web-facade:web工程服务暴露,仅提供facade接口和数据实体类
kaola-resource-web-provider:web工程服务实现,根据需要自定义service层组件
kaola-resource-web:web工程启动类,compile依赖facade,runtime依赖provider
-
offline工程
kaola-resource-offline-facade:offline工程内部服务,仅提供接口和数据实体类
kaola-resource-offline-provider:offline工程服务实现,及部分kaola-resource-api实现,提供dubbo provider
kaola-resource-offline:offline工程启动类,compile依赖facade,runtime依赖provider
-
app工程
kaola-resource-app-facade:app工程内部服务,仅提供接口和数据实体类
kaola-resource-app-provider:app工程服务实现,及部分kaola-resource-api实现,提供dubbo provider
kaola-resource-app-starter:app工程启动类,compile依赖facade,runtime依赖provider
现有工程改造方案
工程模块改造
在上述改造原则基础上,投放工程的模块改造计划可以清晰地列出来。针对基础模块来说,需要进行如下改造:
-
新增kaola-resource-facade-base工程,存放facade的异常类
-
kaola-common-cache重命名为kaola-resource-cache
-
kaola-common-util重命名为kaola-resource-util
-
kaola-resource-generic重命名为kaola-resource-dao
-
新增kaola-resource-facade-base工程,存放facade的异常类
剩余的三个工程,以resource-web工程为例,需要进行如下改造:
-
将kaola-resource-facade重命名为kaola-resource-web-facade,存放对web工程提供的服务接口,将BusinessException和ParameterException移除放到facade-base工程中
-
kaola-resource-provider重命名为kaola-resource-web-provider,将模块中的util迁移至resource-util工程
-
kaola-service-util中非业务相关的所有util全部移动到kaola-resource-util这个module中,剩余业务相关的,移动到kaola-resource-web-provider工程
-
kaola-resource-web,将web对provider的依赖由compile改为runtime
offline和app工程采用类似的方案,不再赘述。
maven依赖改造
maven依赖改造的目的,一是减少由于版本依赖不一致带来的新坑,方便第三方中间件的统一管理;二是对工程权限有所约束,不至于代码乱放,难以维护。
-
共有的jar包依赖管理 在父pom增加dependencyManagement标签,把常用的Spring、log、guava、junit、jsonserializer、disconf和网易自研的中间件等jar包统一进行版本管理。子工程需要相关组件时,仅需引入依赖,无需指定版本。
-
模块依赖权限管理 由改造后的工程架构可知,启动工程是没有办法接触到业务实现的,这需要依靠runtime时再依赖业务实现模块来实现。因此,对于每个需要依赖*-provider的工程,都需要通过runtime来依赖。
-
api版本发布管理 需要对外发布的包,依赖尽量使用provided引入。在父pom增加本地api发布的版本,子工程如需依赖api或实现api的业务,需要引用父pom的api版本,api版本的升级,需要同时升级父pom和api模块工程两个地方 resource-util工程与api工程管理一致。
TODO
-
kaola-resource-api迁移所有page相关的api接口
-
kaola-resource-fed迁移使用新的git工程
-
kaola-resource-cache逻辑迁移
-
kaola-resource-dao业务接口的依赖去除
架构调整总结
架构永远是聊不完的话题,特别是涉猎了知识面较广的后端以后,越发感觉需要学习的东西越来越多。本文分别从移动端和后端的架构思想谈起,从中探索共同点,结合微服务的思想,探索过程中对多端架构有了思想的摩擦,总结了一套业务接口与业务实现的规范,能够更好地为其他业务提供服务。在此基础上,将这一套规范运用在投放工程上,完成从旧架构的问题分析,到新架构的优化设计与实现的整个过程。从结果来看,模块的职责更为清晰,代码的权限与边界得到了比较好的控制,对以后业务的纵横向扩展、代码的健壮性都有了质的保障。
参考链接
-
https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84
-
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
-
https://docs.gradle.org/current/userguide/dependency_types.html
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 如何做好iOS应用安全?这有一把行之有效的“三板斧”