本文是 Uber 的客户端工程师团队是如何开发最新版本司机端的系列文章中的第二篇,代号 Carbon ,是咱们拼车业务的一个核心组件。除了其余新功能以外,司机端 APP 还为超过 300万 司机提供收入,引导他们挣钱。2017年咱们结合司机的反馈开始对司机端进行从新设计,并在2018年9月份启动了该项目。java
从新编写一个APP会引起关于新架构和新设计相关的许多问题。迄今为止,大多数开发者只关注应用程序如何正常工做。虽然以用户为中心的设计理念已经成为软件开发中的一些主流,可是辨别真实有效的用户需求并不容易。react
一旦决定 重构Uber司机端, 咱们必须以普遍多样的用户群里为基准,寻找如何设计更适用的工做流和保留最适用的产品功能。 咱们收集全世界各个城市的Uber司机的反馈,将这些反馈做为咱们用户体验的重要初衷。android
与此同时,咱们也必须考虑到,成百数千的工程师们,他们须要重构APP的同时也要功能迭代。一个通过深思熟虑的APP架构须要帮助工程师,在保证可靠性的同时高效、快速的工做。ios
幸运的是,Carbon 在知足这些服务时并无发生冲突,这些服务包括更实用的应用流程,改善司机体验功能,为开发者提供灵活稳定的架构,事实证实,它们结合的很是好。git
本篇文章,阐述了咱们是为了适应新司机端(代码Carbon)如何提出核心需求,而且讨论使用RIBs架构和插件设计,来知足司机端应用逻辑。github
自2013年发版以来,Uber司机端积累了须要功能。在将来的四年以后Uber司机端将成为司机的核心工具。随着应用程序的日益复杂,Uber确实作到组织化。上百个功能由横跨公司40多个不一样子团队开发和维护。截止2017年1月,Android司机端已经拥有由近200个工程师编写出428,685行代码。iOS司机端由200多工程师贡献720,273行代码。更为重要的是,咱们的APP安装在超过300万的设备中,天天超过100个国家中的100万个司机使用。swift
对于Carbon的成功,咱们知道咱们须要及时更新全部现有功能(已经一些新功能)的同时,也须要并行更新架构。设计模式
产品开发须要时刻记住用户。为此,咱们想和Uber司机一块儿建立构建咱们的应用。在开发的最初阶段,咱们在用户调研上投入大量资金。在11个国家,12个城市中采访了500位司机。网络
这些访谈对象帮咱们设计新司机端的用户体验,肯定最重要的功能。可是,除非司机在在真实条件下在实际道路中使用APP,不然没法获取完整的用户体验。咱们须要一种方法,它能够允收集用户反馈,快递迭代并每周发布新版本。架构
图一:在测试期间,为APP使用最初设计。
最难而且最重要的挑战是,在实际测试中的稳定性。当接触一个新APP,在Alpha和Beta测试阶段能够接受错误和问题。Uber司机端在Beta阶段,真实的司机已经能够在实际道路中可使用它挣钱了。实现可靠性是Carbon的关键目标。因此,进入首次测试阶段时,咱们必须确保Carbon像现有程序同样可靠。
阐述了如今工程的限制后,咱们采用了分段性方法,将项目分为四个阶段。每一个阶段的目标是开启下个阶段的开发。
咱们拥有一个通用模板,来描述咱们的APP必须包含哪些因素。这个模板包括网络库,存储库,ReactiveX,分析追踪,崩溃上报和咱们的自研的应用架构Ribs。利用这个模板,咱们构建了具有初始化架构的程序,包括存储能力,网络,崩溃上报和基础组件。然儿,在这个阶段。程序缺乏司机业务功能,这仅仅是咱们构建特性的框架。
使用RIBs框架的一个好处是,它如何将业务逻辑做为应用程序体系结构的核心。在Carbon阶段,一个好的起点是为司机定义高标准的用户状态。这将致使咱们为RIBs定义一些基础:
咱们利用RIBs树图,以下图2所示,说明应用架构。这个简单的树形图展现了,如何使用RIBs组件相互关联。
图二:RIBs树图向咱们展现了组件关联的可视化方法。
在关注用户阶段,咱们能够将UI分离出来。这个方法能够促使从司机中吸收持续反馈建议,并将建议吸纳到设计中。同时促使咱们维护用户程序的基本。
在一些基础上,咱们重点转移到协做中。在第三阶段,咱们的目标是扩大规模,使大约40个团队在同一个APP中可靠无缝的并行工做。基于用户的反馈和设计、产品团队的协做,咱们定义出更详细的RIBs组件。
在合并了框架以后,RIBS树结构变大了,以下面的图三所示。
图三: 按照咱们的定义在RIBs树中增长层级和新的功能框架。
在咱们细分以前, 咱们清晰下在使用的RIBs架构中的一些概念。
这是一个对象,它直接关联到RIBs生命周期,具备启动/中止生命周期功能方法。换句话说,添加到RIBs的工做者从RIB链接时启动,RIB分离时中止。工做者确保交互(一个RIB的业务逻辑组件单元)不会太大,而且容许更好的分离关注点。(在咱们的仓库中能够找到 Android 和 iOS)
插件是一种设计模式,插件容许咱们用灵活的方式进行特征标记。(进一步了解Uber若是利用插件 上一篇文章)。首先咱们为集成的核心代码定义一个公用的API,而后开发者能够实现本身的API实例。这是由于他们了解代码由遵照这个架构的特征标记隐形保护。把每一个插件点想象成微服务体系中一个服务,而后插件工厂做为该服务的消费者,这样将使得插件和API或者他们之间的契约类似。
结合RIBs,插件和工做者,能够定义出架构中的核心和非核心部分。核心组件是必须的组件,而且不能被标记成禁用。另外一方面,若是非核心组件引入了重大问题或者致使回归,能够被禁用。在上面的图四种,Map和MyHub是非核心组件,他们能够在不关闭应用程序功能的基础上被禁用。
进一步查看图 4 中 RIBs 树图的细节功能,咱们就能看到:如何使用核心 RIBs,Plugins,Workers, 以 worker/plugin 模式去实现 Agenda 的所有功能。Agenda 功能暴露了两个不一样的插件点(plugin points): Agenda Worker 和 Agenda Section。 使用 Worker plugin point 集成非用户体验功能,Section plugin point 去扩展用户体验功能。
图四:仔细观察RIBs树的一部分,能够发现集成的多个领域,它容许在灵活添加特性,也容许并行开发。
使用这个设计模式,咱们能够理解许多领域APP。例如一些工程师构建登陆和注册界面,其余工程师重点开发非核心RIBs的地图架构。
第四阶段,其余团队加入咱们开放了Carbon的开发。因为想要确保每一个功能彼此独立,咱们建立到了插件框架,所以特性合并是一个相对顺畅的过程。若有必要,一些小RIBs上升为核心组件,可是咱们大部分代码仍然包含在插件中,因此咱们的架构中一些部分是可选性的(咱们计划在后续章节中讨论一些使人兴奋的新特性)。
图五: 如动画所示,RIBs生命周期与他们的各自单元相关
软件工程的架构是一个一般是在牺牲别的资源条件下去优化团队更为重要的一些指标。虽然咱们的方法容许咱们优化可靠性、可扩展性和模块化,可是其余方面咱们必须妥协。在一个严谨的流程中,咱们只容许少许的组件是核心组件。为了维护核心组件的质量,咱们拥有一个内部审查团队,他们审查修改核心代码中每一个代码。这个流程要求一些工程师投入一些时间去作核心代码审查,这也减慢了其余工程师的提交频率。
大多数应用开发者熟悉MVP或MVC模型。和他们相比,RIBs好像更冗长。RIBs有更多的组件,须要更多的前期规划。较小的软件开发团队也许不须要采用相似的流程。以前咱们已经作完一个重构重写咱们的乘客端APP,所以我对如何构建Carbon更清晰,咱们将咱们的学习总结以下:
按规模工做: 在Uber, 规模 既是最高限制也是最有价值资源. 300万司机在使用咱们的APP,因此咱们不能让他们失望。 同时, Uber做为一个技术组件公司,咱们已经发展到数百万工程师一块儿构建应用程序。规模 是咱们从规划到推广决策的关键。
RIBs to the rescue: Carbon经过使用RIBs和插件,咱们在如下方面得到成绩:
共同构建: 若是没有司机的帮助,以及他们在用户调研和测试阶段给咱们的建议,咱们不可能作到这一点。