移动端工程架构与后端工程架构的思想摩擦之旅(2)

此文已由作者黎星授权网易云社区发布

欢迎访问网易云社区,了解更多网易技术产品运营经验

 

 

投放工程架构调整

 

有了前面的“理论基础”,以及跃跃欲试的心动,我们来对投放工程的架构做一次调整和优化,原则是不改变原有的业务逻辑,目的是使投放工程的业务边界和业务功能更为清晰。

 

旧工程架构

 

资源投放系统一共分为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即可。

对于当前的架构还不是最完美的,存在两个问题。

  1. kaola-resource-cache提供了kaola-resource-api的实现,应将这一部分的业务逻辑移至kaola-resource-app-provider中。

  2. 业务接口依赖了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业务接口的依赖去除

 

架构调整总结

 

架构永远是聊不完的话题,特别是涉猎了知识面较广的后端以后,越发感觉需要学习的东西越来越多。本文分别从移动端和后端的架构思想谈起,从中探索共同点,结合微服务的思想,探索过程中对多端架构有了思想的摩擦,总结了一套业务接口与业务实现的规范,能够更好地为其他业务提供服务。在此基础上,将这一套规范运用在投放工程上,完成从旧架构的问题分析,到新架构的优化设计与实现的整个过程。从结果来看,模块的职责更为清晰,代码的权限与边界得到了比较好的控制,对以后业务的纵横向扩展、代码的健壮性都有了质的保障。

 

参考链接

  1. https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84

  2. https://zh.wikipedia.org/wiki/MVC

  3. https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

  4. https://docs.gradle.org/current/userguide/dependency_types.html

 

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

 

相关文章:
【推荐】 如何做好iOS应用安全?这有一把行之有效的“三板斧”

posted @ 2018-12-06 18:33  网易数帆  阅读(389)  评论(0编辑  收藏  举报