app框架一(组件化)
背景
在早期app功能比较简单业务不复杂的情况下,我们一般都不需要组件化,最多就是基础库抽成私有库,正常情况下不需要划分业务组件也不需要路由等。在合适的时机引入合适的框架,而不是盲目的跟风炫技。特别是有些app就1-2个人更新迭代,业务也相对比较简单,如果硬要引入组件化无疑是加剧迭代成本。所以组件化我认为他应该是中大型项目为了更好的业务解耦以及布局平台化、生态化的必要手段之一。
我们的app是一款saas B端app,集成了新零售、微商城、餐厅、智慧美业、客来店等十几个业务线的B端app集合.理论上每个解决方案都是要有一个独立自己的商家app,方便商家处理订单,管理会员等.但是如果独立app会有以下几个问题.
1.商家如果购买了多个解决方案,需要下载多个app
2.商家如果从一个解决方案升级到另外一个解决方案,需要重新下载app
3.一个业务线想把其中某些功能当作插件给其他解决方案使用就比较麻烦.比如一家连锁火锅店,他应该购买我们餐厅解决方案,但是他同时又有火锅底料需要在微商城售卖.这种业务场景,你多个app是很难处理的.
都是给商家用的,处理订单,管理会员等,虽然业务场景不一样,但是有些东西还是可以抽象共用,节约研发成本. 这就是后面要说的app中台.
基于以上几点原因,我们就需要把不同业务功能合并到一个app上,并且需要保证每个业务的独立迭代、互不影响,每条业务线的功能都可以独立运行调试发布,必要的时候也可以拆分成独立app,保持子app和主app并行迭代。类似天猫和淘宝、外卖和美团还有主app上的其他业务,都是一个个独立的团队在迭代。而我们虽然是B端app,但是用到的技术却是一样的,区别应该就在于业务组件的拆分和基础组件的封装以及业务场景组装等。
组件化
单工程app框架
如图一个app不同功能模块后端已经进行了微服务了,但是前台app还没有进行组件化,还是单过程app,如果业务不是很复杂其实如上图构架图每个模块用文件夹区分也是可以的。但是如果业务已经足够复杂,并且每个迭代按模块划分的比较清楚的情况下,还是建议采用组件化。这个app是组件化的,且每个业务内部同样也可以进行二次拆分子组件。
app业务全景图先来看看我们app的全景图,如图可以很清楚的看出这是一个多业务的聚合型app,每个业务他们的框架还可以不一样,具体后面展开说明下。先说下入口工程,也就是app的主工程又称壳工程,主工程不要有业务代码,连基础库工具类也不要有,只放一些icon、启动页等和app强相关的基本配置。检查一个app是否组件化彻底,第一步就是看他的主工程是否有代码。
为什么主工程不能用代码?
有些开发者只把相对独立的模块拆分出业务组件,一些不知道如何拆分的或组装业务组件的代码会放到主工程。这种做法是刚开始做组件化或老工程改造很容易采用的方法,但是具体去看他们的代码就会发现,每个业务组件虽然功能独立但是拆的很轻不彻底,主工程还是会不可避免的臃肿,特别是随着业务越来越复杂,好像需要组装和跨组件的业务处理也越来越多了。不自觉的研发可能还会把一些本来应该是业务组件处理的逻辑,为了写起来更加方便就直接写到主工程了。这样的组件化能算彻底吗?所以要做到彻底组件化,主工程不要有任何代码,切断研发对主工程的依赖,每个业务组件都是平等的,也不能相互依赖。
主工程没有任何代码还有一个好处,那就是后面如果需要做平台化或子业务需要有自己独立app,只要选择合适的业务组件给一个壳工程配置上icon等相关配置一个app就完成了,不需要担心主工程里面的代码如何改造剥离等问题。
还有组件化很重要的一个原则,必须保证每个组件向下单项依赖,不要出现相互依赖或反向依赖的情况。由于很多开发者做组件化的时候是用pod来管理自己的组件,由于pod依赖进来私有库后是没有层次限制的,都可以进行import调用,这样如果开发者在框架层面没有规定限制好,具体业务开发就很难避免相互调用导致依赖混乱。
从构架图上看每层都只能向下依赖,不能向上依赖或左右依赖,也就是主工程依赖业务组件,业务组件依赖基础库,基础库依赖私有库和第三方库,业务组件之间不能相互依赖。由于我们的app比较大型不像其他电商类app一个业务组件就是一个模块,这边图上的业务组件都是一个个业务线,也就是说他们都可以是一个个独立的app,所以每个业务组件都很复杂,所以这里指的业务组件可能是多个业务组件和基础组件组成的聚合组件。
下面我们来简单展开几个“业务组件(聚合组件)”,来对整个app框架有更深层次的了解。
ec业务组件 酒店业务组件随便找了2个业务组件展开,可以很明显的看到每个业务线的架构都不一样,每个业务可以根据自己的业务特性选择不一样的框架,进行不一样的业务划分很组装方式。但是必须都有统一的interface层用于该业务组件和其他业务通信,就好比每个业务组件的链接头,插槽和插头必须安装一样的规范,不能一个USB的插头去对接一个TPC的插槽。
问题
把原来应该是十几个app的功能合并成一个app,框架应该如何设计才能让这十几个业务迭代互不影响.我们会遇到哪些问题呢?
1.如何避免业务间迭代不会相互牵制?多方捆绑形成两人三足的局面呢?
按照传统早期的app框架,一般一个app就一个代码仓库单一工程,选择MVC或MVVM、MVP进行分层,然后再抽一些通用工具类等.大一点的公司一般还会搭建自己的私服(cocoapods和maven)来沉淀公司的各种工具类. 一般的小项目、小团队采用这种方式没啥问题,招一个ios一个android就能够先跑起来. 但是我们是要把十几个app合并成一个app,如果采用这样的框架,无法避免的就是所有的业务方都相互捆绑在一起了,每次迭代发布都需要等所有人ready,需要所有人一起配合,一个倒下了所有人玩完;所以也就必然造成发布周期长、过程痛苦。
•单一工程的开发模式,客户端承载业务越来越多,业务之间的依赖越来越多,系统耦合严重, 扩展困难,维护变得异常复杂
•业务更新:端侧的发版特性,限制了业务更新的灵活性
•协同成本高,牵一发而动全身,不敏捷
•APP发版周期长,业务创新慢
•各自业务发展依赖版本,无法独立迭代
如上图是一个单一工程同一个代码仓库的普通项目,它通过统一的网关对接3个后端团队,也就是三条独立业务.按理说这3条业务应该互相不影响,他们的迭代更是不应该出现互相牵制的情况.但是由于app是一个,可能都需要改同一个模块,很容易出现代码冲突迭代相互牵制.所以我们不能采用传统框架,我们需要改造.第一步按业务拆分app,每个业务都有一个独立的代码仓库,独立的工程,让他们之间的迭代互不影响.新框架优势:
1.每个业务线都是单独的一个项目,有自己的代码库管理,有自己的代码版本。
2.几个业务线可以并行开发,先开发好的可以先提测,服务可以先上线。
3.如果某个服务延期了,我们可以把那条业务线的代码切换到上个版本的代码,打包先发布上线。
4.后期如果需要抛弃一个业务线的话我们也可以很快的把相关代码和资源去掉。
5.如果需要某2个业务线打包成一个新的app,我们只需要搞个新的主工程,做些配置和小调整就可以搞个新app出来。
每个业务都是一个独立的代码仓库一个独立的工程,只要给每个项目配上一个空的主工程它就是一个独立的app,可以独立开发、调试、提测,只是在对外发布的时候用一个主工程包含全部业务打包发布,就实现了我们需要把十几个app合并成一个app,而且迭代互不影响的目标.
涉及到的一些技术点:
1.每个业务组件都是一个个库项目,它所对应的资源都应该归属它自己
2.全局变量解耦设计,拆分出基础的下沉,业务的回归业务
3.账号体系、权限、设置等所有业务线都需要引用和设置. 这块是拆分出账号组件,公司账号体系只要是打通的,他们基本通用.
2.版本更新提示如何设计?
每个app都会经常看到一些弹框,提示用户升级新版本,有些甚至不升级就不让用app.这就导致了用户不得不升级,而有时候升级了却发现功能根本没有变化.这很可能就是他升级的功能你没有权限或根本没用到.如何精准提示用户升级,以及如何灰度提示都需要好好设计.
关于这块设计我们是这样做的,每个业务组件都有一个唯一的组件标识,在组件创建的时候定义一个唯一的组件标识,并注册到DevOps平台上,这样我们就可以控制对每个业务组件单独配置升级提升,也只有当app进入到该业务组件的时候才会触发。还有很重要的一点,不管是哪个业务组件触发了升级提升,点击升级的时候必须直接升级到最新版本。这样就不会出现进入一个组件升级到1.1,再点击另外一个组件又提升升级到1.3等这种情况了。配置还是可以更新组件更新情况来配置,但是用户体验上却不会出现多次升级的情况。
其实引入组件标识的概念除了做版本升级设计之外还有很多其他的用处,比如虚拟路由管理等都需要用到。下面稍微扩展下组件标识和组件以及业务的关系。如下图多个组件库可以对应同一个组件标识,但是不能一个组件库有多个组件标识,这样查询的时候会戳乱,就好比一个人只能有一个名字,但是可以很多个人都叫张三一样。如果组件标识是按业务为度拆分定义的正常情况下一个组件标识对应一个业务线,但是偶尔有一些奇葩的需求,就是这2个业务功能基本差不多,只是换个名字换个概念包装下变成一个新的业务售卖的时候,只有少数功能做了隐藏,那不可能复制2份一样的代码,迭代开发2次,这时候就会出现多个业务线对应同一个组件标识的情况。
组件标识关系图3.发版如何操作?
火车式发布应该是行业比较通用的一种发布方式,一周一个版本或2周一个版本,赶的上这趟火车的项目就这次一起发布,赶不上的等下一班车.但是这块也是有一些规范来约束不要就会很乱,导致漏发错发等问题.
火车式发布和版本制最大的一个区别就是是否提前圈定版本上线内容,而不是有固定的发版时间就能算火车式发布了.我之前和一个面试的小伙伴聊起这个火车式发布模式,他们公司也基本是2周一个版本,看上去也是火车式发布模式,但是细聊下来我发现他们可能并不是严格意义上的火车式发布模式,比如他们会在这次发布上线后开会确认下次发布版本包含哪些内容.然后在圈定为下个版本发布的项目就按这个时间点来赶.这样只要其中一个项目出现问题,可能就会影响到这个版本发布,虽然如果评估赶不上这趟发布最后也是可以拿掉这个项目在这次迭代里面.但是这样给人的感觉其实就不一样了,火车式发布应该是在发车前多久某个时间(停止检票之前),我可以随时买票和退票,而不是像那种上个版本上线之后就需要确认下一班车是否买票.同样我开车之前,这个开车人员(打包人员)其实也不需要去清点买票的人是否都上车了(虽然我们现在会做一个简单的清点),是否来得及上车,上不上车应该是这个项目自己决定.
今天的组件化没有讲路由也没有讲组件化的细节和具体技术点,因为网上类似的文章已经有很多了,没必要重复来说,只要选择自己合适的技术应用就可以,今天主要讲的是大框架上如何拆分如何设计。关于B端app的组件化这是之前很早写的一篇关于组件化细节的文章有兴趣的可以看一看,里面关于Assets.xcassets合并问题已经突破处理了,只是没更新。