写点什么

网易新闻 App 架构重构实践:DDD 正走向流行

  • 2020-05-13
  • 本文字数:4514 字

    阅读完需:约 15 分钟

网易新闻App架构重构实践:DDD正走向流行

当前,大多数移动开发团队选择以 MVP 作为业务层的核心架构模型,在此基础上实现了客户端的组件化、插件化、容器化等,但作为业务层核心的 MVP 架构模式至今仍有诸多弊端。网易新闻 App 在领域驱动设计(DDD)思想指导下,对其架构做了整体重构,得到了不错的重构质量与项目收益。移动端架构与网站架构的区别是什么?网易新闻客户端的架构演进历程是怎样的?为什么要选择 DDD 思想来指导重构?DDD 落地中应当关注哪些方面?带着这些问题,InfoQ 记者采访了网易高级客户端工程师李云鹏,他也将在 QCon 北京 2020 上带来《在领域驱动设计(DDD)下重构网易新闻 APP 架构》的演讲。

移动端架构与网站架构的区别

传统意义上的网站架构,通过超文本传输协议,浏览器可以快捷方便地将我们想要访问的页面呈现在眼前。每个网站由多个页面组成,这些页面也都属于 Web Server 的一部分,如图 1 所示。



图 1 Website 与 Server 抽象模型


网站大多数时候不需要关注离线化,而主要关注负载均衡、高并发和多级缓存等场景,以实时响应大规模流量。


而移动端 App 需要用户先进行安装操作,然后再进行页面访问,其中的高并发网络请求等场景通常交由 Server 端处理,移动端则更关注处理用户交互与界面变化,如图 2 所示。



图 2 Application 与 Server 抽象模型


所以移动端和网站端的核心关注点不同,也就造成了二者在架构上的历史演进的不同。举一个简单的例子,最初起源于 .NET Framework 3.0 的模型 / 视图 / 视图模型(MVVM)思想,从 WPF 应用过渡到 ASP.NET,用于网页的开发,后来却在移动端成为最流行的架构模式之一。


随着网站端的历史演进,网页的工作量和跨平台的需求增长迅速,使得网站前端开发的重要性日趋明显,网站端已经抽象为了前端和后端,网站前端通过浏览器实现跨平台,与移动端共同组成了大前端。


目前常见的移动端架构设计模式主要包括关注面向接口编程的 MVP(Model-View-Presenter)、关注数据驱动与双向绑定的 MVVM(Model-View-ViewModel)、关注表现层分离的 MVC(Mode-View-Controller)和符合领域驱动设计思想(DDD)的 The Clean Architecture 等。

网易新闻客户端的架构演进历程

网易新闻客户端的架构演进主要经历了四个阶段:Static Method、MVP、MVPs 和 VIPER。


为摆脱沉重晦涩的架构模型束缚,网易新闻客户端团队将一些软件设计中的元素抽象为轻松的食品加工中的元素,用蛋糕模型来做示例。

第一阶段:Static Method

从最初的设计阶段开始,为了简单地达到代码的可复用性,新闻客户端采用了一种 Static Method 的设计方式,将业务逻辑按照业务模块转移到一些工具类中,使得开发人员可以用最小成本复用这些业务逻辑。


在 Static Method 的模式中,技术团队将 View 抽象为一块蛋糕,蛋糕上面需要奶油,柠檬,樱桃(Model)等食材,所有负责输送材料的加工厂(Static Method)派工人(而不是厨师)将材料直接运送并摆放在蛋糕上面,如图 3 所示。这使得蛋糕最后拥有了所有他该有的食材成分。但这些食材存在随意摆放,使蛋糕变得混乱的情况,如果再继续这样堆砌食材,它就不再像是一块蛋糕了。



图 3 Static Method 蛋糕加工模型


所以,随着时间的推进和业务迭代的不断加速,这种丧失了封装、多态和继承的面向对象特性的工具类设计,难以应对业务的变化,过渡到流行的 MVP 模式已成必然趋势。

第二阶段:MVP

传统的 MVP 模式中,一个 View 由一个 Presenter 管理,在这种模式下产生了代码复用的难题。


由于 Presenter 与 View 通过接口协议绑定,通常一个 Presenter 里的业务逻辑只能为一个 View 服务,代码复用性的丧失会导致大量冗余代码的产生。


Presenter 与 View 为一对一的方式,就像一块蛋糕(View),指派给一个厨师(Presenter)去制作,但是厨师一个人需要做的事情太多,他需要亲自加工食材(Model),再将这些材料一一装饰在蛋糕上面,如图 4 所示。如果这时候再告诉他我们的蛋糕还需要添加一些突然增加的装饰和点缀,他可能会面临崩溃。



图 4 MVP 蛋糕加工模型

第三阶段:MVPs

为了解决 MVP 代码复用的问题,大多数的设计方式都是将 View 与 Presenter 改为多对一的模式,使得一个 Presenter 可以为多个 View 服务,而一个 View 也被多个 Presenter 控制。更多的 Presenter 介入也意味着这些 Presenter 为了适应不同的 View 将产生更多的接口。


Presenter 与 View 为多对一的方式,就像一块蛋糕(View),指派给多个厨师(Presenters)在共同加工。而每个厨师可能会处理多块蛋糕,他们同时还要做好手上的装饰品(Model),再亲自将其放在每个蛋糕上。在这期间,厨师们直接接触每块蛋糕时,还加入了很多他们擅长的但是蛋糕不需要的手艺。多个厨师围着一块蛋糕转,蛋糕有了很多他原本不想拥有的东西,而这些厨师们也难以管理,他们直接操控蛋糕,没人能够合理控制他们,如图 5 所示。所以,新闻客户端过渡到了符合 DDD 的 VIPER 模式下。



图 5 MVPs 蛋糕加工模型

第四阶段:符合 DDD 的 VIPER

在符合领域驱动设计的 VIPER 架构设计模式下,一块蛋糕(View)只由一个主厨(Presenter)进行装饰摆放,但是蛋糕上所有的饰品食材,都由这位主厨指派给多个不同的厨师(Interactor)进行加工(Entity)。当这些厨师加工完毕后,再把材料送过来,通知主厨,由主厨亲自进行摆放。


那些负责加工的厨师没有机会再直接接触到蛋糕,蛋糕只有它原本应有的样子,变得更加利于加工制作。更美好的是,以往蛋糕需要每个厨师亲自配送到它需要被送达的地方,现在厨师只需要打个电话,一切配送工作都将由快递员(Router)去完成,如图 6 所示。



图 6 VIPER 蛋糕加工模型

基于 DDD 的短视频架构优化

以网易新闻客户端的视频详情页为例,新闻客户端的视频详情页结构可以抽象为图 7。初期设计的视频详情页,所承载的业务并不多,界面也较为简单,Holder、子页面和适配器等都在 Fragment 类中定义实现。但是随着短视频风口的到来,业务加速迭代,视频的业务需求急剧增加,视频详情页所需要承载的业务也越来越多,Fragment 类从最初的几百行,急速扩张到两千多行。基于旧有的视频详情页设计,加入新的业务,使得维护成本变得越来越高,类也变得越来越大,臃肿膨胀的类已经变为了“面条代码”。



图 7 视频详情页抽象结构


基于 DDD 的思想,新闻客户端实现了一套包含 UseCase 的基础框架,划分出了领域模型,由于视频详情页由多 Fragment 组成,技术团队还加入了共享变量层,使拥有统一生命周期的组件之间能解耦传递数据,重构后的视频详情页整体架构如图 8 所示。



图 8 视频详情页重构后的架构设计图

DDD 的选型与实践

选型背景

新闻客户端的多数业务模块在设计初期的时候,承载的业务都不是很多。当某些业务模块的需求逐渐增加,开发人员为了快速完成迭代工作,代码经常会冲刺堆积在一起,久而久之,业务模块之间的边界变得越来越不清晰,耦合也变得越来越严重。


DDD 的限界上下文可以帮助技术团队定义出清晰的领域模型边界,以达到解耦的目的。VIPER 是符合 DDD 理念的架构模型,VIPER 曾一直流行于 iOS 端,在 Android 端的应用案例非常少,但其实 VIPER 的核心思想是 The Clean Architecture,所以它同样适用于 Android 端,是一个通用的解决方案。

落地难题

在将 DDD 落地的过程中,团队遇到的比较困难的问题大致是两方面,一方面在于优化工作流程,另一方面在于转变编码思想。


在工作流程方面,大多数互联网公司都会有需求评审会,其中会有产品经理、项目经理、软件工程师、UI 交互设计师等参与。但传统形式的需求评审主要关注点还是在整体的业务本身,在事件划分上是非常模糊的,这与确定限界上下文的事件风暴相差一段距离,推动多个团队变更评审方式的难度比较大。所以,开发团队内部在编码阶段开始前,还会进行一次技术评审,由准领域专家们参与到其中,与开发人员共同分析讨论界限上下文。


在编码思想方面,需要团队成员接受 DDD 的思想,转变为领域驱动思维,技术团队在内部进行了一系列相关的分享,通过编程中的“隐喻”,让大家循序渐进地建立对 DDD 的认知,逐步加深了解。

重构效果

在基于 DDD 的架构重构初期,新闻客户端选取了视频详情、视频列表和图集三个业务模块使用 VIPER 重构,对 DDD 进行尝试性探索。


  • 在重构质量上,由于先确定了领域模型,代码整体解耦符合预期,三个模块重构后上线均未产生严重线上问题。

  • 在项目收益上,一方面,DDD 帮助团队加速了迭代的开发效率,另一方面,代码的可维护性也有了比较大的提高,同时,模块错误率也约降低了约 50%。


新闻客户端以半年为一个维度,统计出模块重构前半年和重构后半年的系统故障率(主要指程序开发期间产生的问题),得出了如图 9 所示的信息。数据发现,越是业务变化很频繁的模块(如视频),通过 DDD 获得的模块稳定性收益就越大。



图 9 DDD 重构前后系统故障率统计图

DDD 落地面面观

DDD 从 2003 年被提出来以后,得到了软件学术界的高度认可,但由于国内外开发环境和开发人员思想理念不同等多种影响,导致 DDD 在国内的实践并不是很理想。从 2013 年开始,微服务架构和中台化在国内逐渐盛行,而 DDD 可以作为其指导思想,帮助微服务架构进行清晰的领域和子域划分,DDD 逐渐在企业应用实践上获得理想的成果,这正是 DDD 在国内突然变得流行的最主要原因之一。


对于开发团队来说,实践 DDD 最重要的一步就是通过事件风暴来进行领域分析建模,但这对于带头发起人的领域素养要求较高,需要团队内有一位布道师开路,担任领域专家的职责。


针对移动端来说,目前最合适的、符合 DDD 的架构模型就是 The Clean Architecture,Google 官方推出的安卓蓝图项目也针对 MVP 提供了一套符合 The Clean Architecture 的 MVP-Clean 项目,开发者可以由此为起点进行逐步探索和尝试。


由于国内外软件工程师职业环境和所承受的压力有所不同,在很多突发性的业务需求的冲击下,使得开发者只能对代码做疯狂堆叠,导致开发者不得已放弃 DDD 的设计,而期间发生需求变更就很容易导致风险。


当风险变为危险后,发生各种互相推诿的现象,其问题本质归结起来无非是两方面,一方面是组织结构和环境所影响,另一方面是边界划分不明确。


对于组织和团队方面,前期无需变动,也可以满足向 DDD 转型的条件,也为后期再建设微服务和中台化提供了便利。但需要注意的是,改变团队成员的固有开发思维也是十分重要的一个环节,团队内应该定期组织关于 DDD 的分享,有利于使大家对 DDD 的观念潜移默化。

嘉宾介绍

李云鹏,网易新闻架构技术组工程师,国内首个移动架构模型书籍《移动开发架构设计实战》作者。10 余年互联网行业经验,擅长移动端架构选型、项目重构与插件开发相关工作。曾就职于世界 500 强核心技术实验室,作为第一发明人,申请了 14 项专利和著作权。




QCon北京2020的演讲中,李云鹏老师将从近五年的网易新闻客户端架构模型演进展开讨论,着重介绍新闻团队从旧有架构模型,迁移到以 MVP 为基础的、符合领域驱动设计思想(DDD)的 VIPER 架构时,经历的自我创新与踩坑实践,以帮助开发者了解“重构如何保障模块稳定性”,“如何快速重构业务模块”等痛点问题的解决方案。同时,针对不同类型移动端产品举例介绍架构模型选型策略,进而发散开发者架构设计思维,明确架构问题分析方式,掌握架构选型要点。点击了解详情


2020-05-13 12:4413730
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 395.7 次阅读, 收获喜欢 1983 次。

关注

评论 5 条评论

发布
用户头像
可能做的挺好,但是写的真一般
2022-02-09 14:17
回复
用户头像
很形象~

在符合领域驱动设计的 VIPER 架构设计模式下,一块蛋糕(View)只由一个主厨(Presenter)进行装饰摆放,但是蛋糕上所有的饰品食材,都由这位主厨指派给多个不同的厨师(Interactor)进行加

...

,蛋糕只有它原本应有的样子,变得更加利于加工制作。更美好的是,以往蛋糕需要每个厨师亲自配送到它需要被送达的地方,现在厨师只需要打个电话,一切配送工作都将由快递员(Router)去完成

2021-05-07 13:13
回复
用户头像
我觉得各位不要被另一个评论影响,DDD作为方法论指导架构设计很好,就怕有人犯了教条主义,硬套DDD概念做的垃圾架构然后反过来说DDD有问题,本来就是方法论。你用Java写的代码有bug,就说Java有bug?技术人员多点理性,少点戾气。我从网易人家的实践中发现了人家的确能用DDD带来好处,那就够了,至于什么互联网行业,你以为传统的金融行业对bug的容忍率能比你互联网低?搞笑~踩着A捧B的做法不可取,大家取长补短,提升自我不好么?
2020-05-27 12:29
回复
用户头像
ERP时代的开发策略,就别拿出来祸害互联网行业了。
2020-05-15 16:18
回复
一看就是新手,不懂就少说话!互联网就不用架构了?DDD的理念作为方法论指导很好,具体实践效果全看个人。函数式编程思想理念更早,现在不也是被各种趋之若鹜。
2020-05-27 12:25
回复
没有更多了
发现更多内容

EventBridge 特性介绍|以 IaC 的方式使用 EventBridge

阿里巴巴云原生

交易所多种模式开发、各种源码交易

Geek_56201b

交易所开发 区块链应用开发 软件定制

今天聊一聊合成数据 (Synthetic Data)

澳鹏Appen

人工智能 数据集 数据标注 数据训练 合成数据

ModStartCMS模块化建站系统 v3.7.0 栏目导航开关,页面样式优化

ModStart开源

web前端培训学习需要掌握哪些 Linux 命令

@零度

前端开发

得物App H5秒开优化实战

得物技术

前端 H5 优化 实战 Web H5

【数字底座专题】星环科技春季新品发布周

星环科技

专车数据层架构进化往事:好的架构是进化来的,不是设计来的

勇哥java实战分享

架构

web技术支持| 简单实现Vue第一章:模板编译

anyRTC开发者

Vue 前端 Web 音视频 WebRTC

云风:不加班、不炫技,把复杂的问题简单化

博文视点Broadview

大数据培训关于数据采集面试问题分享

@零度

数据采集 面试问题 大数据开发

低代码极简部署

源字节1号

低代码开发

共探开源生态|Apache Pulsar 社区助力 Apache APISIX Summit Asia 2022

Apache Pulsar

开源 架构 云原生 Apache Pulsar Apache Pulsar 社区

Jira 云产品宕机多日,业界热议上云如何保障数据安全

万事ONES

Atlassian Jira 研发管理工具 项目管理工具 企业研发管理

如何通过云效Codeup高效落地分支模式,提升开发协作率

阿里云云效

云计算 阿里云 版本管理 分支管理 分支模式

平安普惠湖北分公司:齐心抗疫显担当,助力小微迎暖春

科技新消息

去中心化钱包系统开发app,imtoken钱包平台搭建源码

Geek_56201b

#区块链# 源码搭建 去中心化钱包

华为云推出限量NFT云宝,区块链技术为你的数字资产保驾护航

华为云开发者联盟

华为云 NFT 云宝 华为云NFT 华为云数字资产链

GPU和AT的区别在哪里?GPU与AT有哪些区别?

Finovy Cloud

人工智能 GPU服务器 显卡、gpu GPU算力

平安普惠湖北分公司:疫情后来式,复工进行时

科技新消息

新思科技助力Linux基金会开展最新开源普查项目

InfoQ_434670063458

Linux 开源 新思科技

对Indexlookup的理解误区

TiDB 社区干货传送门

java培训:怎样才能写出一个优秀的对外接口

@零度

JAVA开发 对外接口

Docker 实战教程之从入门到提高 (四)

汪子熙

Docker 容器 虚拟化 docker image 4月月更

jackson学习之九:springboot整合(配置文件)

程序员欣宸

4月月更

Apache DolphinScheduler ASF 孵化器毕业一周年,汇报来了!

白鲸开源

Big Data DolphinScheduler workflow Open Source apache 社区

TASKCTL服务端字符界面的应用系统安装

敏捷调度TASKCTL

kettle 元数据 ETL 调度任务 大数据运维

周日直播|OpenMLDB Pulsar Connector,高效打通实时数据到特征工程

Apache Pulsar

开源 架构 云原生 Apache Pulsar 消息中间件

Android C++系列:C++最佳实践2抽象类

轻口味

c++ android 4月月更

从Opentracing、OpenCensus 到 OpenTelemetry,看可观测数据标准演进史

阿里巴巴云原生

TiDB 查询优化及调优系列(一)TiDB 优化器简介

PingCAP

网易新闻App架构重构实践:DDD正走向流行_架构_小智_InfoQ精选文章