大V店组件化架构演进实践
大V店自2015年创建以来,业务快速发展,项目经过多次迭代,目前APP日销售额已占全平台百分之70以上,成为公司最重要的入口之一。大V店的用户端入口,从单一的平台独立App,拓展为大V店、成长盒子、等多个App入口。大V店所承载的业务,也从单一的电商,发展到音频、课程、视频播放、视频直播,等多个大品类业务。业务的快速发展对客户端架构不断提出新的挑战。
背景:
三年的时间里APP经过多次迭代,项目经手多人,代码繁复不堪,代码复用性查,已经无法满足现有的业务需求和快速迭代开发,公司新一年的产品战略是在现有平台的基础上多元素多面化快速发展,这就意味着可能会有多个新的App,多个业务线同时发展,现有的代码架构已经无法满足,重构迫在眉睫,于是在17年初我们定了年度计划,大V店APP组件化。
探索:
一个好的项目是什么形态,一个好的架构是什么样子,道理大家都懂无非是高内聚低耦合等等,但我认为好的架构应该是跟业务形态高度匹配的,公司业务的迭代促使架构的演变,而每一次演变都应该更贴合业务场景,能更好更快更稳定的满足业务的迭代,并不是所有的团队都需要组件化,团队在不同的时期受不同业务的影响应该做出相应的变化。
在组件化的路上不可能一帆风顺,不能一蹴而就,只能循序渐进,现有的业务需要开发同时组件化也要同步进行,现在的代码就像一盘散沙,然而要让一盘散沙变成堡垒绝不是往沙子里加水加泥就能完成的,团队经过反复讨论之后我们决定先进行项目分层,项目分为三层基础层,服务层,业务层,同层级之间可以相互调用,不同层级之间只可以向下调用不能相互调用,在确定了方向之后团队开始投入有条不紊的重构开发中。
阶段一
项目分层之基础层抽离实践:
基础层即项目的Command层,一个好的Command层可以快速复制到多个项目而不需成本
现有代码的混乱不好维护罪魁祸首之一就是工具类不统一,项目组件的滥用和重复功能的工具类和UI组件到处都是,工具类本身就跟业务功能毫无关系且存在天然的业务边界是最好抽离的部分,所以我们把它发在了第一步。
抽离时需要注意的事项:
功能重复或者类边界不明显的要直接删除或者归到对应的工具类中
重构要彻底,要保证所有的业务代码使用新的工具类或方法
UI库的抽离要尽量优化解耦,有许多业务场景可能会用到同一个UI组件库,一个好的UI库要有良好的解耦和丰富而自制的接口
HTTP库的抽离要考虑多工项目使用时不同的公参和不同的Base(此处我们使用的是暴露拦截器接口给业务层实现)
项目分层之服务层抽离实践:
服务层的定义
服务层我们定义为DVDServices,即项目中多个业务需要共同使用的服务,比如IM即时通讯服务,支付服务,AccoutService账户服务等等,服务层可以调用基础层不可以调用业务层,服务层各个服务之间不允许互相调用
服务层如何着手
服务层的抽离可以说是项目中最重要且最繁复的部分了,现在有的服务在项目中散落在各处跟业务紧紧的耦合在一起,要想拔出来可以说是非常的困难,但也是对技术能力架构能力最有提升的部分,团队此时也表现出前所未有的积极,在讨论之后我们决定使用模块负责人制度,划分好各个服务之后,分给各个组员对应的服务模块,对于这部分模块负责人需要完全重构,负责人在仔细了解过服务以及服务要用到的业务之后画出架构图,召集团队在一起讨论架构需要优化和不足的地方,最终确定方向之后负责人负责着手重构。
开发流程
服务负责人整理相关业务抽取出需要整合的服务
确定要分离的服务之后着手抽离,整理出技术方案,架构图
团队所有人一起过技术方案,确定出最终方案后评估开发时间着手开发
分离后的服务要具备良好的可移植性,能在公司项目范围内的APP中随意使用
分离的服务理论上属于独立项目,可作为SDK的形式使用
完善WIKI文档
测试问题
开发完成后最重要的就是测试上线了,在服务开发之前我们会与测试团队沟通,在确定好开发提测时间之后由服务负责人编写测试case,开发完成后负责人协同测试人员测试相关业务模块
技术架构应该反映出团队的组织结构,同时,组织结构的变迁,也应该导致技术架构的演进。
大V店MaMa+平台下包含学院和小书库、智能盒子等等业务,
对于在我们团队中已经有了的组织结构,优先组织结构,去拆出独立的业务库,方便子业务库的同学内部沟通协作,减少他们跨组织沟通的成本。
同时,我们将负责学院业务的大团队,再进一步细化成大V课小组、音频内容小组,由这些小组的同学去在学院业务下完成更细维度的学院子业务库拆分。
根据组织结构划分的业务库,天然的存在业务边界,每个同学都会按照自己业务的目标去继续完善自己的业务库。
这样的拆库对内是高内聚,对外是低耦合的,有效的降低了内外沟通协作的成本。
AccountService拆分实践
最开始并没有账户服务的概念,只是最简单的登录注册token存储,不涉及身份变化等等,随着业务的迭代账户状态逐渐变多需要支持多种身份状态随意切换,以前那种杂乱的写法使代码变得难以维护,且基本上无法移植,而平台基本上各个端,各个分支APP都是用的一套账户体系,所以我们决定优先把账户服务拆分出来,得益于基础层重构的经验和拟定好的开发步骤,账户服务有条不紊的开始了重构,由相关负责人开始拟定归纳现有的账户体系以及用户场景,画出架构图再由大家讨论是否合理,账户体系抽离的需要控制好那些接口需要开放,那些内容需要存储,数据改变的时机以及刷新机制和通知机制,缓存机制等等针对这些种种我们归纳出了以下几点:
- 业务层通过账户服务无非是需要获取信息和收到通知信息
在跟后端同学沟通之后我们把大部分零散的接口合并,组合成一个专门获取个人信息的接口,数据发生改变之后发送通知,接受到通知的业务即做出相应的改变
- 服务层要做的事,提供set和get个人信息的相关方法,个人信息的缓存,账户生命周期的管理,以及各种账户状态改变之后需要调用的后端接口
这一块的设计总结一下就是面向对象的封装思想业务不需要知道内部实现逻辑,只需要调用相关的API剩下的就交由服务自己去完成
AccountService工作示意图:
AccountService的拆分极大的方便了各业务层的调用,和维护成本,数据的统一处理更好的保证了各业务数据的统一性
对于账户服务的拆分总结了以下几点:
- 服务的设计不要拘泥于形式:不要拘泥于某种架构方式不可,能贴合业务场景的架构才是好架构
- 要掌握好服务自身收放的度:过多的对外借款意味着维护成本的增加,过少的话又不能满足业务场景,这个度需要掌握好,对于没必要开放的接口绝不开放
最终拆分出的服务
抽离了基础层和服务层,项目的解耦性和可扩展性大大的提升,不过这只是我们组件化的第一步,此时我们的服务层基础层尚还处在一个工程之中,服务层和基础层以module的形式依赖,下一步就是彻底的抽离出工程
阶段二
组件化实践:
服务层和基础层的抽离让组件化的关键部分变得了容易很多
架构的目的是用于管理复杂性、易变性和不确定性,以确保在长期的系统演化过程中,一部分架构的变化不会对架构的其它部分产生不必要的负面影响。这样做可以确保业务和研发效率的敏捷,让应用的易变部分能够频繁地变化,对应用的其它部分的影响尽可能的小。
-
服务的的抽离和管理
至此服务层和基础层我们已经抽离解耦,但是代码依然还在我们的工程中,使用还是需要花费不少成本,而且管理起来也不方便,所以这里我们选择了把组件上传到maven仓库(关于maven仓库的搭建大家自行搜索,下面会贴出一篇教学),在运维同学的帮助下我们搭建了基于nexus的android包管理私服,然后分别创建common层和services的gitlab仓库,把各个组件分别上传,就可以在需要使用的地方通过gradle引用,大大的提高了可维护性和使用效率,当更新完组件的时候各个业务模块只需要修改一下版本号即可。
当然各个组件之间有可能互相引用,尤其是common层的util组件,不过没关系在主工程中我们引用的时候使用exclude取除重复的依赖即可,Exclude可以设置不编译指定的模块,这样即可避免重复依赖问题
maven仓库搭建教程:
https://blog.csdn.net/ouyang_peng/article/details/56872556
-
路由的选择&为什么要选择路由
在比较了目前的主流框架之后,选择了ARouter,关于框架的使用和原理想必也不必多说,网上有很多不错的教学,为什么需要路由,这里我们直接引用一段关于Arouter博客的分析,里面已经说的很全面了:
显示Intent:项目庞大以后,类依赖耦合太大,不适合组件化拆分
隐式Intent:协作困难,调用时候不知道调什么参数
每个注册了Scheme的Activity都可以直接打开,有安全风险
AndroidMainfest集中式管理比较臃肿
无法动态修改路由,如果页面出错,无法动态降级
无法动态拦截跳转,譬如未登录的情况下,打开登录页面,登录成功后接着打开刚才想打开的页面
-
H5、Android、iOS地址不一样,不利于统一跳转
博客地址:http://www.jcodecraeer.com/a/anzhuokaifa/2019/0220/12655.html
其中关于公共组件数据交换的问题,在服务层我们已经解决了,被多个业务引用的功能我们已经抽离到下层,并且使用gradle管理
-
业务组件的拆分实践
业务层是整个组件化过程中最复杂繁复最需要耐心和配合的工作,越大越久的项目耦合越深,拆分起来越痛苦,也越容易出问题,总结以下两点:
分工问题:有些年代久远的业务,代码逻辑可能很久不动,甚至可能相关的开发者也已经离职,这类业务是最难拆分和最容易出错的,因此在拆分初期我们跟服务层一样首先为每位同学分配了业务方向,自己拆分相关的业务组件,在遇到和别的组件有耦合时可以直接找到相关负责人讨论技术方案,这样的好处是大家能最快的熟悉自己相关的业务代码,而不是面对一整个工程,当模块出现问题的时候也能第一时间找到负责人沟通解决
测试问题:由相关组件负责人联系测试妹子,商讨测试时间共同编写测试用例,在确定大概时间之后,上报相关开发节点和进度时间表
最终架构示意图:
结语
也许我们的组件化跟市面上的有所不同,但还是那句话:
“技术架构应该反映出团队的组织结构,同时,组织结构的变迁,也应该导致技术架构的演进。”,你的架构应该是最适合你当前团队以及公司业务线的,能与业务和团队高效配合的,好的架构并不是依照某个模板刻画而成,而是会根据团队和业务成长而成长。