写点什么

统一架构——优酷主客的标准化开发之路

2020 年 5 月 06 日

统一架构——优酷主客的标准化开发之路

一、超级 APP 的标准化

在技术领域,标准化有很多很好的实践案例。比如服务端容器的出现,应用的构建、分发和交付得以在容器层面上实现标准化,提升了业务创新的效率。


那么一个客户端应用的标准化是什么呢?一个业务需求通常可以拆解为功能需求,设计需求,体验需求,数据需求和运营需求,而


每一种需求都对应着一类开发工作。


通过对每一类开发工作进行分析研究,我们得出了一个结论,客户端开发的标准化是运行时环境一致,视觉效果统一,遵守相同的数据协议,用相同的方法搭建,投放和获得用户反馈的标准化。具体抓手则是以渲染架构标准化为核心的一系列标准化设计和工作。



二、优酷的标准化实践

1.渲染架构标准化


通常一个应用是由一个一个页面组成的,而页面是由什么组成的呢?我


们可以大致分为三个部分:数据,组件和服务。


数据模型是业务模型的抽象化产物,是页面的里,它的结构来源于业务的需要,影响着组件的实现。数据从媒资,算法等生产源头通过 CMS(内容管理系统)汇聚而来,通过网络以字节流传输到客户端,客户端下载,解析,转换成对象,或统一保存,或组织分发给每一个显示单元,从而完成其从生产到消费漫漫征途。


数据需要贴合业务,如果有一个环节的格式定义出现差异,最终的结果可能就会大不相同,而刷洗数据,转化格式又会带来效率上的降低。而对统一架构和标准化组件来说,我们又希望数据的格式和字段是一致的。如何来解决这一对矛盾获得最优解呢?


我们从两个方向进行了尝试,一个是在页面搭建方向上,推进了服务端的数据协议的标准化,之前服务端的数据模型大致都是 page-module-component-item 的多层嵌套结构,它对应了运营同学在搭建页面时,从整体到局部到细节的设计思路和操作行为。我们的标准化思路就是虚化节点定义,明确节点结构。虚化节点定义的意思,我们不再以 page,module 等有具体意思和层级关系的名称作为节点 tag,而是统一为 Node。这样原本有层级关系的结构,就变成了可以任意嵌套的结构。而对于 Node,明确其内部为 level,data[],style[]和 subNodes[]等含义明确的标准化结构。而在组件渲染方向上,我们将数据结构演化抽象为从 PO-DTO-DO-VO【备注】的过程。而我们在 VO 这一层去抹平之前的协议差异。


1)组件是页面的表,直接响应用户体验。组件的核心设计原则是组件在不同页面上的复用,为此我们将组件的内部实现分解为 Model,View 和 Presenter 三个部分,各部分之前只以 Contract 约束接口定义。这样三类模块都可以进行不同实现的组合和替换,我们刚才说的在 VO 抹平数据协议差异,就是通过配置不同的 Model 完成的。此外,我们在组件的布局管理使用了 vLayout 的布局混排能力,在配置上通过文件配置进行布局样式上配置和组件 MVP 的搭配,还提供了组件间的通信能力。这些标准化接口的提供,统一了组件的使用环境,大大降低了组件的 UI 开发的难度。


2)服务是页面的神,有了服务页面才是活的。对于服务我们抽象为域内服务和域外服务,域内服务指的是页面范围的视觉动作,比如组件的插入,删除,刷新,加载更多等,这部分能力我们在数据域对象提供了内置的方法,而域外服务则是指对业务中间件和应用基础能力的调用,对于这部分调用,我们在服务能力标准化再介绍。


在渲染架构标准化的设计中,我们强调了解耦,而这对配置化的实现也带来了便利。通过对页面布局能力的拆解,对组件 MVP 的结构设计,渲染标准化架构把一个组件拆解成了这个组件的容器,容器的 adapter,组件的 MVP 部件,组件交互需要的领域能力,组件交互需要的基础服务等独立部分,可以自由配置组合。在组件承载的业务上,我们也通过代理来进行配置,代理可以拿到页面的上下文,监听业务的消息进行响应,不同的使用场景都遵循着相同规则。



  1. 设计体系标准化


客户端用户界面一致性是设计体系标准化需要关注的核心问题,保证一致性和体验品质,并实现设计开发的工作提效,是“设计语言标准化管理体系”的建设的核心诉求。为此,我们联合设计一起,从零创立了优酷自己的设计体系。我们为设计体系设定了核心目标:建立设计语言的标准化管理体系,改变设计与开发生产模式,实现设计与开发的品质和效率提升。


为了完成这个目标,我们进行了很多工作。设计同学对全站的现有设计进行了盘点,统一设计和技术对界面元素的认知体系,定义了设计规范。开发同学建立了标准化组件库,以视觉规范为基础,把设计侧的规范文档及组件库,通过一种协作语言形成设计与技术的映射关系,实现客户端设计组件与技术组件的统一拉通,沉淀成统一的 SDK 共同维护。而协作语言的确立,标志着标准化的工作的具象,从根本改变了之前设计开发的工作流程。


以往开发实现过程中,研发代码中会写原始属性值,而这个写的过程本身就是孤立的,非标准化的,而现在研发以引入 Design Token 的方式,把视觉样式的代码进行统一管理,形成统一的研发组件库,各业务线可以直接调用,收拢了散落各处的代码。Token 最终会对应什么样的呈现,完全基于标准规范。而通过对规范的调整,可以对应用整套设计进行体系化的调整。



  1. 服务能力标准化


渲染架构的标准化有一个重要的设计就是将服务和渲染分离开。


不同业务模块,一般都会依赖不同的中间件,按照之前的服务和渲染耦合的设计,相同的服务能力在不同业务模块间往往实现多次,这既增加了学习成本,形成技术壁垒,也使代码结构脆弱和臃肿。而且服务的接入成本高,迭代成本也高,冗余的代码既影响了编译速度,更影响了启动速度。


为此,我们设计了标准化的服务接口和服务中间层,每个模块都通过调用服务中间层的标准化接口,来获得或者触发服务。这既使渲染和服务解耦,也完成了中间件之间的接耦,形成了高内聚低耦合的架构层次。


1)未标准化之前。



2)标准化之后。



这是标准化前后的对比图,从图上我们可以看到,标准化之后,上层业务依赖的不再是具有某一套服务接口和实现,而是可以根据实际的情况,灵活配置服务。基础的服务和业务中间件的变更成本也降到了最低。


三、标准化后的成果

完成了这些标准化工作之后,目前优酷的整体架构大致是这样的。



通过一系列的标准化工作,实际上改变了业务需求的开发的模式。


1)从设计的角度看,现在的设计首先考虑的整体的设计规范,然后才是个性化需求的表达,从设计的输出物上看,不再是基于数值的标注,而是基于协作语言的标注,对于最终的效果,设计在设计开发阶段就可以有明确的感知,不再需要逐个元素和开发细调,解放了人力。


2)从开发的角度看,开发只需要关注组件的个性化需求实现,而可以复用大部分代码。组件开发上,有公共组件库提供从属性到布局多个维护的标准化实现,而且由于在框架层面的隔离,所有的布局和大部分的卡片都是可以复用的,UI 的复用可以使产品需求的实现只需要关心产品的业务逻辑实现。而且组件是自我独立的,和外部容器之间没有直接引用,所以可以在任何环境下正常工作。经过一定时间的积累和沉淀,我们将会有支持大部分 UI 的布局卡片库。而对于能力的调用我们也提供了标准化的调用接口,开发同学不再需要关注最终具体的实现,只需要关注接口本身可以提供的能力即可。


3)对于产品和运营,统一的配置平台,组件产品全站拉通,都给了他们更低的成本和更好的收益,而设计开发效率的提高也增强了产品试错和复制成功经验的能力。


4)在实际收益上,渲染标准化之后我们已经积累十余个通用组件,节约了垂类业务的开发人力,研发效率提高了 40%。我们的用户体验优化的项目中,比如异步加载,轻量化等等都通过标准化快速落地到了每一个业务场景,将之前两三个月的落地周期缩短到了两个迭代。再如设计标准化,为暗黑模式的快速完成,提供了技术基础,使之前一个月全员投入的需求在两周之内完成上线。


我们的架构设计成果,还在促进着阿里集团其他应用的标准化,比如来疯等独客在接入后,一次开发,可以快速在优酷,来疯,虾米等多个应用投放。


备注


PO(Persistent Object):持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应 PO 的一个(或若干个)属性。


VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。


DTO(Data Transfer Object):数据传输对象,泛指用于展示层与服务层之间的数据传输对象。


DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。


作者 | 阿里文娱无线开发专家 涵父


2020 年 5 月 06 日 17:571252

评论

发布
暂无评论
发现更多内容

如何优雅滴在手机上跑Python代码

王坤祥

Python 移动应用 手机编程

把成功过成自己的生活

子铭

成功学 生活状态

怎样算是一个好的开发者?

水滴

开发者

死磕Java并发编程(7):读写锁 ReentrantReadWriteLock 源码解析

七哥爱编程

Java并发 读写锁 ReentrantReadWriteLock

程序员陪娃漫画系列——魔方

孙苏勇

程序员 生活 程序员人生 陪伴 漫画

浅析 Cocoapods-Packager 实现

Edmond

ruby ios CocoaPods binary packager

南丁格尔科普

Sicolas Flamel

多云的一点思考

HU

消息队列Kafka - acks参数

Java收录阁

kafka

即将步入职场,忐忑而又期待的新人菜鸟

菜农阿飞

成长 新人

Day 47|Week 07-5 曾国藩家书|问学篇-学问何处何时都可做

熊小北同学

说出来就不灵啦

伯薇

糊涂 活在当下 享受状态 生活状态 观察者

MySQL中order by语句的实现原理以及优化手段

天堂

Java MySQL 性能优化

DDD 实践手册(2. 实现分层架构)

Joshua

设计模式 领域驱动设计 DDD 系统架构 分层架构

Netty 源码解析(四): Netty 的 ChannelPipeline

猿灯塔

Java并发编程系列——锁

孙苏勇

Java Java并发 并发编程 多线程

韦小宝真的幸福吗 | Random Forest

张利东

Python 学习

和邓小平、基辛格“谈笑风生”的世界第一女记者:奥琳亚娜·法拉奇

赵新龙

记者 编辑 采访 法拉奇

学习来应对创业的未知

Neco.W

创业 重新理解创业

阿里巴巴Java开发手册泰山版解读

Bruce Duan

HashMap 的 7 种遍历方式与性能分析

Bruce Duan

Java 性能 hashmap 遍历

经济大萧条对我的启示

Neco.W

创业 自我管理 职场 自我提升

使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源

mzlogin

CDN Jekyll GitHub Pages 个人博客

下一代存储NVMe over Fabrics

HU

从数据闭环谈微服务拆分

松花皮蛋me

微服务架构 微服务拆分 微服务冶理

InfoQ写作平台首秀,来个自我介绍

nuhcoad

个人感想

神经网络的激活函数为什么要使用非线性函数

王坤祥

神经网络 激活函数

为什么正在使用的Java版本跟环境变量的版本不一致

阡陌r

Java 踩坑

MySQL的死锁系列- 锁的类型以及加锁原理

程序员历小冰

MySQL

高仿瑞幸小程序 00 准备工作

曾伟@喵先森

小程序 微信小程序 前端 瑞幸

游戏夜读 | 2020周记(3.27-4.3)

game1night

4月17日 HarmonyOS 开发者日·上海站

4月17日 HarmonyOS 开发者日·上海站

统一架构——优酷主客的标准化开发之路-InfoQ