导语 | 腾讯前端工程师唐霜在 React 我的项目中,尝试应用 DDD 方法论为业务对象建模,其所在团队造成良好的业务沟通标准和业务逻辑积淀流程,构建了更加巩固的业务零碎。作者将多年的积攒摸索经验总结分享进去,从对业务的思考、react 我的项目的特色登程,论述在我的项目中进行的前端 DDD 摸索。欢送浏览和交换。
前言
咱们所处的业务团队服务于腾讯某投资部门,零碎波及的投资相干业务在国内具备典型意义,是笼罩一级市场、二级市场、基金的多品类全流程的投资零碎。其中蕴含了对投资我的项目自身的业务解决,也蕴含了投资流程的工作解决(类比 OA 零碎),还包含了其余大部分零碎须要思考的技术建设(例如基于安全性思考的数据、合同文件、电子签等)。方方面面使得咱们在建设这套零碎时除了要有技术自身之外,还要有对业务的把握能力。尽管技术团队追随产品团队实现技术开发,然而在一些 具体问题上如果对业务不相熟,很难真正解决好开发过程中的某些具体的逻辑代码 。而对于投资零碎而言,精确实现业务是最根本的要求,否则带来的危险可想而知。这也给咱们技术团队带来了微小挑战: 如何在如此简单的零碎中,比拟正当地把握每一个技术细节背地的业务逻辑,确保业务实现的精确呢?
咱们把眼光瞄准了 DDD,它是由 Eric Evans 在_Domain-Driven Design: Tackling Complexity in the Heart of Software(2004)_总结提出的一整套概念和方法论。咱们尝试摸索在前端践行 DDD 的可能性。DDD 的理念,能够帮忙咱们团队正当的造成良好的业务沟通标准和业务逻辑积淀流程;DDD 在技术方面的领导,帮忙咱们构建更加巩固的业务零碎。基于这样的想法,咱们开启了相干的摸索。
什么是 DDD?
Domain-Driven Design 是从理论业务登程,站在解决畛域问题的角度去思考和设计零碎的方法论。它蕴含了两个方面的内容:沟通方法论和研发方法论。
Eric 从另一个角度让咱们技术人员从新思考本人的工作形式,对于面临简单业务的零碎开发的技术人员,不能一上来就开始进行零碎设计和代码实现。他们要做的第一步,应该是和领域专家一道,基于某种大家都能了解的业余语言,构建出一套畛域模型,这也就是咱们所讲的“沟通方法论”。而这个步骤,是咱们当初的很多开发团队齐全没有思考过的事件。首先,这里的“畛域”(Domain)是指一类事物的汇合,比方咱们常讲的金融畛域、通信畛域、数学畛域等等。你能够很显著的感知到,“畛域”意味着“边界”,意味着某些“共性”和某些“个性”。领域专家就是这些特定业务畛域的资深工作者,对该畛域的业务十分理解,是该畛域的里手。要让技术人员和领域专家坐在一起构建出对应业务的畛域模型,必须摒弃他们各自在本人工作范畴内的广义概念,大家找到一种互相都能够了解的概念表达方式,来最终确认各自提出的问题和答复对方都能精确了解。这在咱们看来,就是世界上最高效的沟通形式,没有之一。
咱们能够应用 DSL(畛域专用语言) 来实现这一沟通,领域专家和技术专家基于对立语言构建畛域模型,可想而知,这一模型是无奈间接作为代码运行的。开发人员要做的,是基于该畛域模型,用代码将其精确实现。而 Eric 充分考虑到一个零碎在技术实现时和事实世界存在差别,提出了十分多的技术建模计划(Scheme)和配套的架构理念,让技术专家在和领域专家构建畛域模型时,有意无意地疏导领域专家一起构建出更加合乎前期技术实现的模型架构体系。
图 1 对立语言、畛域模型与代码实现的关系
因而,DDD 不是某种架构而是一种设计 。不同的业务团队,能够基于这种设计实现一种合乎 DDD 的架构,来帮忙本人更好的构建本人的零碎。浏览上书过程中咱们重复意识到,这本书是写给技术人员的看上去都是技术字眼但实际上是在流传工作办法的一本方法论书籍。如果有这样的想法,那么在浏览此书,特地是前面的章节时,就不会越来越凌乱。因为你不再是在这本书中去找到某些具体的实现或架构,而是在思考如何去发现法则。 它不能帮忙你实现某个架构,然而它能够帮忙你思考如何设计这个架构。
当初,回到咱们的业务中,为什么 DDD 正好能够帮忙解决简单业务的零碎设计呢?
业务的技术语义
1)技术上讲,什么是业务?
业务(Business)专指商业活动,是实现企业生产到利益回收的一个环节。它的总和,形成了该企业盈利流动的整个流程。一般而言,咱们所指的业务是企业商业活动中的一个局部,有的甚至小到一个环节,例如“结算”这个环节。业务零碎则是辅助这些商业活动的计算机在线零碎,以信息化的模式治理和决策企业的商业活动(实践上没有业务零碎企业也能运行,但信息化社会没有业务零碎会让企业举步维艰)。
业务模块:是以业务零碎的建设者(领域专家、零碎工程师等)的角度对待业务零碎时,将宏大的业务零碎,依照某个业务流动的边界,进行划分的某个单元。然而技术上,个别一个模块还是粒度比拟大的单元。一般而言,业务模块囊括了零碎对于该业务的所有内容,且和其余业务有显著的界线,实践上,能够在脱离了其余业务模块的状况下独立运行。
业务逻辑:是只用代码实现的实在业务的规定映射。简略说,一个业务中,存在什么逻辑,能够通过在纸上画出不同业务对象之间的分割和束缚,并将这些分割和束缚一条条列进去,造成一个列表,而这列表中的每一条,就是一条规定,这些规定的总和,就是这个业务的全副业务逻辑。既然是一条条的规定,那么咱们就能够在代码层面对规定进行治理。对于前端开发者来说,最相熟的规定治理,莫过于路由治理。
业务零碎:实质上是基于人机交互的管理工具。不同角色在零碎中治理的内容不同,但总体上,是进行基于业务上下游数据进行职权范围内的业务管理流动。
从集体的感官上,业务零碎的开发是最简单的,为什么呢?咱们前端开发对不同的前端利用的开发有非常明显的感知差异。把前端利用演绎为 3 类,别离是业务零碎、通用利用、工具类利用。它们尽管都是前端利用,然而在开发工作中出现进去的关注度却十分不同。
表 1 业务零碎、通用利用和工具类利用的横向比照
因为开发过程中关注的重点的不同,在开发时解决问题的思路也迥异。
图 2 业务零碎、通用利用和工具类利用开发时解决问题的思路比照
这种解决问题的思路差别决定了咱们在开发工作中方法论的差别。而业务零碎波及对象多,分割密,维度广等特色,导致在理论开发时,其复杂度比其余类利用高很多。(但在难度上往往并不高,业务零碎经常不须要实现十分多须要基于非凡技术能力实现的性能。)
总结起来,业务零碎之所以简单的因素,咱们认为次要有以下一些方面:UI 交互的不简单与重复实现的矛盾;特定业务逻辑对原有架构的挑战;可继续保护(长期稳定性)与破坏性共存;模块与模块间数据、事件告诉耦合;业务准确性要求高与需要多变之间的工夫抢夺;前后端耦合。
基于这些因素,咱们须要审慎看待开发的每一个性能。因为对于业务而言,这一性能可能会在 5 年甚至 10 年后还要持续应用,而如果性能不够强壮,或者设计之初没有充分考虑业务倒退后所须要的扩大能力,很有可能在之后的迭代中给本人挖下微小的坑。这是很多开发普适性强的公众类产品的开发者们无奈领会到的。
前端语境下 DDD 的价值主张
1)前端须要 DDD 吗?
这个问题能够细化为,前端须要与业务方领域专家进行沟通吗?在设计零碎或性能时,须要基于沟通构造的畛域模型开展实现模块的搭建吗?咱们须要在前端建模吗?咱们须要在前端分层吗?等等。我无奈给出标准答案,但咱们思考:现在的前端开发,特地是相似和咱们一样的业务零碎我的项目中,如果咱们依然是依照设计稿实现实现,是否能精确满足需要?或者说,对于前端开发者而言,咱们是否有必要掌握业务细节,通过一整套的方法论,在代码中将这些细节治理起来?咱们想从另一个角度为前端开发者描述他的工作场景:
图 3 前端开发人员的沟通域
上图中,咱们形容了前端开发者在整个业务开发过程中所处的地步。彩色圆圈示意在这过程中呈现的角色,彩色实线示意与前端沟通时两个角色的分割,虚线圈示意当问题产生时参加该问题探讨的相干角色和内容。能够看到,前端开发者在与不同的角色进行沟通时,经常须要切换思维和角度,他所要面临的问题,所处的地步,如果没有一套方法论撑持,很难在繁冗的工作中不迷失。
2)前端能够 DDD 吗?
包含 Eric 著述在内的 DD 作品,探讨 DDD 多半是在后端环境下。作为思维武器,DDD 能够帮咱们思考如何去设计业务零碎架构,前端是否只须要基于后端接口输入渲染界面就能够了呢?或者,前端根本无法依照 DDD 进行设计?并不是。
但随着业务的深刻,咱们发现现在的业务零碎(基于 B / S 架构),慢慢的凑近重客户端的方向(相似 C / S 架构中的 C 端)。前端代码中的业务逻辑逐步越来越丰盛,甚至很多逻辑只能由前端实现,后端无能为力,或者前后端一起推动时老本更高。运行在 C 端的前端代码,承载的业务逻辑与 UI 混淆在一起,导致组件或 controller 的代码被撑的越来越大,越来越无奈保护。咱们曾指出过:基于 vue 写的利用,当业务足够简单时,你根本无法辨别 vue 组件中哪些是业务的,哪些是交互的。而这种状况继续上来,便是组件的不可保护,代码的腐化。
作为前端开发者咱们须要思考,如何让咱们的代码组织更强壮,更能体现业务的外围逻辑?基于沟通域的思考,咱们认为,前端所关注的次要内容蕴含:业务模型、数据服务、UI 交互组件体系。
图 4 前端关注的次要内容
前端开发注定不可能只关注业务模型。在前端特地是 web 畛域,除业务之外关注由后端吐出的数据和界面交互是一件必须的事。甚至有这样一种状况,产品需要文档中指出,“用户点击该按钮时,须要弹出二次确认窗口,点击确认后该签订流转为已实现”。在这个形容中,用户点击按钮弹出确认对话框,是否属于业务逻辑呢?从传统后端的角度,当然不属于。然而,如果去掉这个交互逻辑,是否残缺表白这一业务过程呢?这是一个值得思考的问题,前端如果要施行 DDD,不可能照搬后端的实际。前后端要解决的问题大不相同:
表 2 前后端要解决的问题比照
这也就意味着,在前端语境下,咱们关注的内容领域比后端还要大。
图 5 前后端 DDD 领域比照
这些思考反过来促使咱们问本人:对于前端而言,DDD 的价值是什么?前端工程师须要从新扫视本人的这一职位,从视觉交互的实现者陷阱中跳进去,正视本人作为工程师的一面。解决业务我的项目中的关键问题,继续的与后端一道提供稳固牢靠的业务服务,这才是前端工程师的本职。而这些都是咱们从 DDD 中发现的,对于前端而言,施行 DDD 有肉眼可见的益处:稳固的业务知识体系;可传承的代码体系;脱离 UI 的单元测试;跨端开发、多端共用的便捷性;明确的团队分工;需要变更的疾速响应;继续麻利。
这些益处对于须要继续迭代的我的项目团队而言,十分有价值,特地是须要继续撑持业务团队在下线实现更多以前无奈实现的工作的价值,是业务零碎最不可代替的。
前端领域建模
咱们在腾讯投资零碎中践行前端建模曾经超过两年,这期间的收益与之前一股脑通过数据绑定的形式相比,堪称天差地别。在此前的开发中,咱们尽管感觉麻烦,然而到底能把性能堆砌进去。然而随着工夫的推移,一些略微老一点的逻辑就再也不敢动。所谓牵一发而动全身,一点改变,重则可能带来整个业务流程的瘫痪,轻则影响相干模块的正确出现。归根结底,在于咱们的零碎是线性的(能够回头看上文线性思维属于哪种类型的利用),咱们开发团队的常识是不可复制的,一位开发者负责一个业务性能,只有他晓得这业务外面的逻辑到底有哪些;其余开发者即便从新研读代码,也很难梳理出精确全面的业务逻辑。如果须要重构,只能依照代码逻辑缓缓复制。
两年前,咱们开始思考咱们所遇到的大部分业务场景的共性,联合开发中遇到的痛点,咱们急需有一种形式,能够对咱们在利用界面的切换、数据的流转中,有一种把握业务外围逻辑的能力。咱们开始了建模摸索。通过两年的积攒,咱们当初能力将这些经验总结进去。
1)思考繁多业务的外围与边界
咱们在开始用代码建模之前,咱们须要和领域专家(也就是业务方,或者与咱们对接的产品人员)开会讨论某一业务的外围概念有哪些、有哪些可能的事件会产生、它怎么和其余概念产生分割?咱们在记事本上梳理出与要开发的业务产生分割的各个概念以及它们之间的连线,这样咱们根本把握了无关这一业务的大部分常识。
当开始用代码表白这些常识的时候,咱们遇到的首要问题是:哪些是必须的,哪些是与该业务关联但非必须的,咱们该以何种模式表白这些概念?
图 6 前端领域建模的首要问题是划清外围与边界
应用 OOP 的范式进行建模是比拟常见且间接的形式,通过建设不拘一格的 class 来创立一个又一个的对象。要害的问题在于,这些对象的外围是什么,边界又在哪里?这些都是咱们必须在一开始思考分明的。
2)建模办法
DDD 给咱们提供了一些具体的建模计划,例如 ENTITY、VALUE OBJECT、SERVICE、AGGREGATE、REPOSITORY、FACTORY 等等。
图 7 DDD 的建模计划
在建设对象模型时,咱们依据对象在业务中所表白的意义,抉择其中对应的计划来进行建模。例如,咱们为一个投资对象进行建模,首先须要辨别,投资对象的边界在哪里?比方一个投资中咱们可能要走一些审批流程,会产生一些非凡数据,它们是否属于该投资对象的外围?其次,咱们须要理解一个投资它都由哪些资源形成,比方其中有一个字段叫做“投资主体”,投资主体即在投资中作为合同上付钱的那一家公司呈现,它本人自身也具备本人的一些属性,那么投资主体是否是投资这个对象的外围资源呢?咱们应该解决投资主体?是用一个一般的 JS 对象来表白,还是创立一个子模型来治理?相似这样的思考,在咱们的建模过程中经常产生,而且有的时候,咱们并不能一击即中。有的时候须要在迭代更新中进行调整。
在长期摸索实际中,咱们造成了一套规范的建模步骤(如下图)。在这一规范步骤中,咱们形象出了建模的基类,基于这些基类进行 extends,依据每个对象的特色,选择性应用某些建模伎俩。具体如下:
图 8 前端领域建模步骤
在上图中,残缺出现了咱们在代码层面实现畛域模型的所有素材。建模的最小单位是 Attribute 即原子属性。原子属性形容一个字段(Field)或属性(Property)的具体属性,即元数据中的某一项。Meta 是最小的模型,由 Attribute 组成,即对于单个字段的模型。Attribute 和 Meta 不独自存在,它们是颗粒度最小的素材,Attribute 个别不可复用(或者说没必要),Meta 可限制性复用。
在上图中,Model 和本文讲的畛域模型概念稍有不同,Model 是代码层面能够表白残缺业务对象的最小单位,它由 Meta 和其余资源组成。其余资源包含代码层面的办法(概念上的 Factory)、动态属性(概念上的 Value Object)等。另外,和后端模型最大的不同,前端模型必须为 UI 视图层留足空间,咱们为 Model 提供了响应式能力,以便在与 UI 联合时,能够察看到模型内变动,以触发界面的更新。具体从代码层面,咱们用 Mobx 进行举例。
图 9 两个繁难模型
上图中,咱们创立了两个模型 Todo 和 TodoList,其中 TodoList 是对 Todo 的聚合,咱们会在下文讲。这是两个再一般不过的 class 进行建模,当初的问题是:如果咱们在前端应用这两个模型,咱们无奈与咱们已有的 UI 框架配合应用,比方在 react 中或 vue 中应用。有没有一种办法,能够让咱们能够以最小的代价扩大模型的能力呢?咱们应用 mobx 这个库对模型进行革新。
图 10 基于 Mobx 的繁难模型
应用 mobx 提供的装璜器,咱们以起码的改变加强模型。这一改变简直不会对原始模型的原有浏览产生任何烦扰,但却使得该模型是可被察看的。再配合 mobx 的工具,就能够与 UI 框架配合,就能够在利用中无缝对接。
图 11 基于 Mobx 的模型和 UI 对接
与后端框架不同,后端以 Controller 作为入口,对模型和视图进行生产。而前端次要的消费者是 UI 框架,视图是入口。模型须要被实例化为 UI 状态后生产。因而,像 Mobx 这样提供与 UI 框架对接的工具,是比拟正当的设计形式。不过,这曾经超出建模自身的话题,此处只是延长。
咱们回到建模步骤,在前端建模过程中,咱们认为最难的一点在于 如何划定一个模型的边界。很多场景下,一个业务对象的某个逻辑,又依赖于另外一个业务对象的某个信息,跨模型的关系如何去形容?实际中,咱们通过嵌套模型来表白,也就是聚合 Aggregate。通过 Aggregate,咱们把高于业务对象自身的逻辑进行梳理。一般而言,一个聚合根囊括了该业务的所有实体,也就是说,它界定了该业务在外围实体上的边界,该业务只在这些对象之间运行。
但业务的运行不仅只蕴含实体,也蕴含业务的流转逻辑。概念上的畛域事件,以及背地的畛域服务——业务对象在该业务运行过程中发生变化。对于前端而言,与后端对接数据也是很要害的一个点,咱们基于 Respository 计划建设可响应式的接口数据管理计划,创立 Service 单例来在整个利用中响应事件,流转业务对象的状态,从而让界面跟着业务的流转而发生变化。
以上的建模成绩被组织在一个 Module(模块)中,一个模块不是一个繁多文件,它是有特定目录构造的一系列文件组成的性能单元。不过须要留神,这里的 Module 和咱们利用开发中通常讲的 Module 概念上稍有不同,这里的 Module 次要是对畛域模型实现代码实现后组成的组织单元。实质上,它还是建模的一部分,和咱们通常意义上的业务模块(蕴含 UI 等)有所区别。
在 React 我的项目中设计业务模块
作为优良的视图驱动库,react 实现了完满的从数据到视图的映射。比照 vue 的劣势在于,它对数据自身的模式没有要求。在 vue 中,你给定的数据蕴含 getter(Object.defineProperty)或由 Proxy 创立抑或由特定的 class 实例化而来,会导致 vue 失落局部响应式的能力。而react 中没有这层限度。因而,在咱们施行 DDD 的过程中,更有亲和力。
react 尽管解决了数据到视图的映射,却没有在反过来的视图到数据的映射上提供计划。咱们在 react 中,会在 onClick 等基于它内置的合成事件零碎中执行回调函数来实现视图到数据的解决,然而这种解决显然是不利于建模的。因而,在 react 自身之外,咱们创立了一套基于 RxJS 的单例服务来解决来自交互的事件与模型层的绑定。在具体的 react 组件中,咱们只裸露给组件它渲染和交互须要用到的数据(状态)和事件接口。
咱们称这种独立于 react 组件自身之外的体系为“忽视图交互模型”。该模型在撰写时,站在视图层的角度解决业务模型的实例化、批改等,解决来自视图层的交互事件等等,然而在该模型中,没有任何具体的 UI 实现。
如果你还有印象,你可能还记得咱们在前文问到“用户点击按钮弹出确认对话框,是否属于业务逻辑呢?”。在“忽视图交互模型”的设计下,咱们能够将“用户点击按钮弹出对话框”这一交互转化为模型的一个局部,在该模型中,它提供了用户点击动作的接口,而该接口解决时会流转模型内持有的其余具体业务模型,进而达到需要文档中所形容的这一要求。但在具体 UI 中,这个按钮长什么样子、在哪个地位、弹出框款式,都须要在 UI 层(组件中)具体去实现。而在实现时,开发者不须要思考该按钮点击事件的具体成果,只须要调用模型接口即可。
截止到这一步,你会发现,在咱们的业务模块中,还没有任何无关 UI 的具体实现,然而,咱们简直曾经把需要文档中无关业务的实体对象、某些与业务流转相干的交互,都用代码表达出来了。
图 12 我的项目中实现业务模块的具体流程
从需要剖析,基于对立语言建设畛域模型,到具体代码去实现畛域模型,再到建设忽视图交互模型。到这里,咱们无关业务的建模根本实现,但咱们还没有任何的界面成果。对于以前咱们拿起 react 就开撸的开发方式而言,几乎不堪设想,怎么界面都还没有就曾经一大堆代码了?
咱们的零碎蕴含 PC 端、APP 端和微信内嵌 H5,APP 端和 H5 端交互基本相同,稍有些细节差别,但 PC 和挪动端的差别大的就不止一点点。然而,对于某一业务模块而言,两端的交互尽管不同,却在业务逻辑上(蕴含基于业务逻辑的交互逻辑)是必须统一的,否则业务自身就会呈现问题。
上图中虚线的右侧即咱们多端共用的局部。业务模型在各端复用并非齐全为了代码重用,更多的是为了保障业务的一致性。而虚线的左侧,则是咱们常见的 react 组件体系的编写。对于不同端,咱们的组件很少有能复用的,基本上各自一套,但却可能保障在业务逻辑上没有出入。一旦业务(数据和交互)被业务模型标准下来,对于不同端的视图层,就是换皮操作(有点夸大)。
这一体系给了咱们很多设想的空间,尽管咱们团队目前并没有再深刻,然而从集体的角度而言,咱们能够在这一模式根底上,做到更容易的业务单元测试、基于后端输入的对立 UI DSL、业务模块服务化、小程序等等。
基于 DDD 的前端利用架构分层
分层也是 DDD 的重要思维。在 Eric 原书中,他对系统分层做了梳理,同时,在行业中,后续继承者们对该分层进行了扩大,次要包含:依赖倒置四层架构、六边形架构、Clean Architecture 等。四层架构典型示意形容如图:
图 13 遵循 DDD 的零碎分层架构示意图
前端与后端不同的是,前端是胖 UI 瘦数据,前端不须要去思考数据的存储读写所带来的一系列问题。然而,在整体的分层划分上,前后端具备一致性。
图 14 遵循 DDD 的前端利用分层示意图
咱们以前的前端利用开发也有分层的思维。然而分层实际很强劲,大部分状况下还是把所有逻辑杂糅到 react 组件和状态治理中。这也是咱们以前的代码在开发完一段时间后可维护性就极速变弱的起因。而依照 DDD 的设计,咱们将代码分层组织和治理。其中的核心层是畛域层,这一层决定了整个利用的大部分代码逻辑,尽管在管制层和 UI 层也有一些逻辑,然而它们多是解决交互的,与具体的业务无关。它残缺的形容了这一零碎所反馈的业务风貌。也正因如此,在多端复用(例如 PC 和 APP 两端写同一个业务)时,只须要重写 UI 层,而不须要再写其余层。
结语
简单的业务零碎无论前端还是后端,都面临着微小的挑战。除了零碎自身的性能之外,最大的危险在于业务逻辑须要被精确实现的同时,给研发团队的实现工夫却很缓和。紧随而来的零碎的代码在长时间迭代中,越来越难以保护。DDD 从沟通和研发两个角度为咱们提供方法论,是面对简单业务时的思维工具。它启发了咱们,让咱们在工作形式上不再鲁莽的马上开始写代码,而是先与业务方深刻沟通、掌握业务的常识网络,基于对立语言进行领域建模,之后再来思考如何在 react 中联合,开发整个利用。
在前端语境下,因为前端关注的内容的异质性,咱们不可能间接照搬后端的 DDD 实际,不得不摸索前端 DDD 的非凡路径。基于 DDD 的设计,咱们的架构剖离出不同的分层,在畛域层和管制层完完全全形容了业务需要。因而,能够不思考 UI 层就能够进行业务编程,并施行无效的业务单元测试。而对于跨端复用而言,就是换 UI 壳的解决,外在的业务逻辑代码可间接复用。
以上是咱们在前端 DDD 话题下的摸索。当然,DDD 不是惟一的抉择,它给了咱们启发,也容许咱们在它的根底上继续颠覆。欢送在评论区分享交换。
| 由浅入深读透 vue 源码:diff 算法
| 优雅应答故障:QQ 音乐怎么做高可用架构体系?
| QQ 浏览器是如何晋升搜寻相关性的?
| 从 Linux 零拷贝深刻理解 Linux-I/O
技术盲盒:前端 | 后端 |AI 与算法| 运维 | 工程师文化
公众号后盾回复“DDD”,取得更多 DDD 浏览资料。