在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
谈谈你的DDD落地经验?
谈谈你对DDD的理解?
如何保证RPC代码不会腐烂,升级能力强?
微服务如何拆分?
微服务爆炸,如何解决?
你们的项目,DDD是怎么落地实操的?
所以,这里尼恩给大家做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”。
也一并把这个题目以及参考答案,收入咱们的 《尼恩Java面试宝典PDF》V130版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取
除了本文,尼恩输出了一个 《从0到1,带大家精通DDD》系列,帮助大家彻底掌握DDD,地址是:
《阿里DDD大佬:从0到1,带大家精通DDD》
《阿里大佬:DDD 落地两大步骤,以及Repository核心模式》
《阿里大佬:DDD 领域层,该如何设计?》
《极兔面试:微服务爆炸,如何解决?Uber 是怎么解决2200个微服务爆炸的?》
《阿里大佬:DDD中Interface层、Application层的设计规范》
《字节面试:请说一下DDD的流程,用电商系统为场景》
《DDD如何落地:去哪儿的DDD架构实操之路》
《DDD落地:从腾讯视频DDD重构之路,看DDD极大价值》
《DDD落地:从美团抽奖平台,看DDD在大厂如何落地?》
《美团面试:微服务如何拆分?原则是什么?》
《DDD神药:去哪儿结合DDD,实现架构大调优》
大家可以先看前面的文章,再来看本篇,效果更佳。
另外,尼恩会结合一个工业级的DDD实操项目,在第34章视频《DDD的学习圣经》中,给大家彻底介绍一下DDD的实操、COLA 框架、DDD的面试题。
DDD现在非常火爆,是有其巨大生产价值,经济价值的, 绝不仅仅是一套概念那么简单。
DDD的绝大价值,具体请参见以下视频:
从腾讯视频DDD重构案例,看看DDD极大价值
作者:小智
嘉宾:李云鹏,网易新闻架构技术组工程师,国内首个移动架构模型书籍《移动开发架构设计实战》作者。10 余年互联网行业经验,擅长移动端架构选型、项目重构与插件开发相关工作。曾就职于世界 500 强核心技术实验室,作为第一发明人,申请了 14 项专利和著作权。
当前,大多数移动开发团队将 MVP 作为业务层的核心架构模式,并在此基础上实现了客户端的组件化、插件化、容器化等。然而,作为业务层核心的 MVP 架构模式仍存在诸多问题。在领域驱动设计(DDD)思想的指导下,网易新闻 App 对其架构进行了全面重构,取得了良好的重构质量和项目收益。那么,移动端架构与网站架构有何不同?网易新闻客户端的架构演变过程是怎样的?为何选择 DDD 思想来指导重构?在 DDD 实践中应注意哪些方面?
传统的网站架构,通过超文本传输协议,使得浏览器能够便捷地将我们想要访问的页面展示出来。每个网站由多个页面组成,这些页面都属于 Web Server 的一部分,如图 1 所示。
图 1 Website 与 Server 抽象模型
网站在大多数情况下无需关注离线化,而主要关注负载均衡、高并发和多级缓存等场景,以实时响应大规模流量。
相较之下,移动端 App 需先让用户进行安装,然后才能访问页面。移动端的高并发网络请求等场景通常交由 Server 端处理,而移动端更关注处理用户交互和界面变化,如图 2 所示。
图 2 Application 与 Server 抽象模型
所以移动端和网站端的核心关注点不同,也就造成了二者在架构上的历史演进的不同。举一个简单的例子,最初起源于 .NET Framework 3.0 的模型 / 视图 / 视图模型(MVVM)思想,从 WPF 应用过渡到 http://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 的模式中,技术团队将 View 抽象为一块蛋糕,蛋糕上需添加奶油、柠檬、樱桃(Model)等食材。负责输送材料的加工厂(Static Method)派遣工人(而非厨师)将材料直接运送并摆放在蛋糕上,如图 3 所示。这样蛋糕便具备了所有应有的食材成分。然而,食材摆放随意,使得蛋糕显得混乱,如果再继续这样堆砌食材,它就不再像是一块蛋糕了。
图 3 Static Method 蛋糕加工模型
因此,随着时间推移和业务迭代加速,这种失去封装、多态和继承的面向对象特性的工具类设计难以应对业务变化。因此,转向流行的 MVP 模式成为必然趋势。
传统的 MVP 模式中,一个 View 由一个 Presenter 管理,在这种模式下产生了代码复用的难题。
由于 Presenter 与 View 通过接口协议绑定,一个 Presenter 中的业务逻辑通常只能为一个 View 服务。因此,代码复用性较差,容易产生大量冗余代码。
Presenter 与 View 为一对一的方式,就像一块蛋糕(View),指派给一个厨师(Presenter)去制作,但是厨师一个人需要做的事情太多,他需要亲自加工食材(Model),再将这些材料一一装饰在蛋糕上面,如图 4 所示。如果这时候再告诉他我们的蛋糕还需要添加一些突然增加的装饰和点缀,他可能会面临崩溃。
图 4 MVP 蛋糕加工模型
为解决 MVP 代码复用问题,许多设计方法将 View 与 Presenter 改为多对一模式,即一个 Presenter 可为多个 View 服务,而一个 View 也可被多个 Presenter 控制。这意味着更多 Presenter 参与其中,会产生更多适应不同 View 的接口。
Presenter 与 View 为多对一的方式,就像一块蛋糕(View),指派给多个厨师(Presenters)在共同加工。而每个厨师可能会处理多块蛋糕,他们同时还要做好手上的装饰品(Model),再亲自将其放在每个蛋糕上。在这期间,厨师们直接接触每块蛋糕时,还加入了很多他们擅长的但是蛋糕不需要的手艺。多个厨师围着一块蛋糕转,蛋糕有了很多他原本不想拥有的东西,而这些厨师们也难以管理,他们直接操控蛋糕,没人能够合理控制他们,如图 5 所示。因此,新闻客户端逐步过渡到符合 DDD 的 VIPER 模式。
图 5 MVPs 蛋糕加工模型
在符合领域驱动设计的 VIPER 架构设计模式下,一块蛋糕(View)只由一个主厨(Presenter)进行装饰摆放,但是蛋糕上所有的饰品食材,都由这位主厨指派给多个不同的厨师(Interactor)进行加工(Entity)。当这些厨师加工完毕后,再把材料送过来,通知主厨,由主厨亲自进行摆放。
那些负责加工的厨师没有机会再直接接触到蛋糕,蛋糕只有它原本应有的样子,变得更加利于加工制作。更美好的是,以往蛋糕需要每个厨师亲自配送到它需要被送达的地方,现在厨师只需要打个电话,一切配送工作都将由快递员(Router)去完成,如图 6 所示。
图 6 VIPER 蛋糕加工模型
通过这种分工合作的模式,VIPER 架构不仅提高了工作效率,还降低了出错率。每个环节都有专门的负责人,使得整个流程更加顺畅。此外,这种架构还有助于后续的迭代和升级,因为各个模块之间的耦合度较低,便于独立开发和更新。
以网易新闻客户端的视频详情页为例,新闻客户端的视频详情页结构可以抽象为图 7。初期设计的视频详情页,所承载的业务并不多,界面也较为简单,Holder、子页面和适配器等都在 Fragment 类中定义实现。但是随着短视频风口的到来,业务加速迭代,视频的业务需求急剧增加,视频详情页所需要承载的业务也越来越多,Fragment 类从最初的几百行,急速扩张到两千多行。基于旧有的视频详情页设计,加入新的业务,使得维护成本变得越来越高,类也变得越来越大,臃肿膨胀的类已经变为了“面条代码”。
图 7 视频详情页抽象结构
基于 DDD 的思想,新闻客户端实现了一套包含 UseCase 的基础框架,划分出了领域模型,由于视频详情页由多 Fragment 组成,技术团队还加入了共享变量层,使拥有统一生命周期的组件之间能解耦传递数据,重构后的视频详情页整体架构如图 8 所示。
图 8 视频详情页重构后的架构设计图
新闻客户端的多数业务模块在设计初期的时候,多数业务模块所承载的业务量并不大。但随着需求的逐渐增加,为了快速迭代,开发人员往往将代码堆积在一起,导致业务模块间的边界变得模糊,耦合度也越来越高。
为了解决这个问题,DDD 的限界上下文为技术团队提供了明确领域模型边界和实现解耦的方法。VIPER 是一种符合 DDD 理念的架构模型,尽管最初在 iOS 端流行,但其核心思想与 The Clean Architecture 相似,因此同样适用于 Android 端,成为一种通用解决方案。
在将 DDD 落地的过程中,团队遇到的比较困难的问题大致是两方面,一方面在于优化工作流程,另一方面在于转变编码思想。
在工作流程方面,传统需求评审涉及产品经理、项目经理、软件工程师、UI 交互设计师等,但关注点往往集中在整体业务,事件划分不够明确。要推动多个团队改变评审方式以确定限界上下文的事件风暴,具有一定的困难。因此,开发团队在编码阶段开始前,进行内部技术评审,邀请准领域专家与开发人员共同分析讨论界限上下文。
在编码思维方面,团队成员需要接受 DDD 思想,转变为领域驱动思维。技术团队组织了一系列相关分享,通过编程中的“隐喻”,让大家逐步建立对 DDD 的认识,加深理解。
在基于 DDD 的架构重构初期,新闻客户端选择了视频详情、视频列表和图集三个业务模块进行 VIPER 重构,以探索 DDD 的实际应用。
新闻客户端以半年为周期,统计模块重构前半年与重构后半年的系统故障率(主要指开发期间产生的问题),如图 9 所示。数据表明,业务变化频繁的模块(如视频)通过 DDD 获得的模块稳定性收益更为显著。
通过采用 DDD 理念进行架构重构,新闻客户端在快速迭代和需求变化的过程中,实现了更高的灵活性、可扩展性和代码质量。通过明确领域模型边界、优化工作流程和转变编码思维,团队成功应对了落地难题,为客户端在竞争激烈的市场中奠定了基础。
图 9 DDD 重构前后系统故障率统计图
自从2003年领域驱动设计(DDD)概念提出以来,它在软件学术界赢得了广泛认可。然而,受到国内外开发环境、开发者观念等多种因素影响,DDD在国内的实际应用并未完全达到预期效果。从2013年开始,微服务架构和中台化在国内逐渐兴起,DDD作为指导原则,有助于明确划分领域和子领域,因此在企业应用实践中取得了良好成果,成为国内突然流行的主要原因之一。
对开发团队而言,实施DDD的关键环节是通过事件风暴进行领域分析建模,这对领军人物的领域素养要求较高,需要承担领域专家的职责。
针对移动端领域,The Clean Architecture 是目前最适合、符合 DDD 理念的架构模型。Google 官方推出的安卓蓝图项目为 MVP 提供了一套符合 The Clean Architecture 的 MVP-Clean 项目,开发者可以借此展开逐步探索和尝试。
由于国内外软件工程师的职业环境和所承受的压力有所不同,在面对突发业务需求冲击时,开发者往往只能疯狂堆叠代码,导致被迫放弃 DDD 设计。需求变更时,容易出现风险。
在风险加剧的情况下,各种推诿责任的现象频发,问题本质归咎于组织结构、环境因素以及边界划分不明确。
针对组织和团队层面,初期无需大规模调整,即可满足 DDD 转型需求,并为后续微服务和中台化建设提供便利。但需注意,改变团队成员固有的开发思维至关重要,团队内应定期举办 DDD 相关分享,使大家对 DDD 观念逐渐认同。
总之,在国内软件开发环境中,实践 DDD 具有重要意义。遵循 DDD 原则,开发者能够更好地应对业务需求变化,提高代码质量和系统稳定性。在组织及团队层面,推动 DDD 转型有助于微服务架构和中台化的实施,提升整体开发效率。为实现这些目标,团队需共同努力,培养领域素养,明确边界划分,并改变固有思维,为 DDD 实践奠定坚实基础。
DDD架构如何落地,是是非常常见的面试题。
以上的内容,如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,并且在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
最终,让面试官爱到 “不能自已、口水直流”。offer, 也就来了。
当然,关于DDD,尼恩即将给大家发布一波视频 《第34章:DDD的学习圣经》, 帮助大家彻底穿透DDD。
……完整版尼恩技术圣经PDF集群,请找尼恩领取
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓