41

从 Clean-Architecture 谈架构原理及其应

 5 years ago
source link: http://dockone.io/article/8493?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【编者的话】前些日子和团队的小伙伴们分享了自己对架构的理解,当时准备的比较仓促,讲的也比较粗糙,很多点并没有表达清楚;欣慰的是大家的反馈都比较积极,证明分享的内容是有参考价值的。

这篇博客主要把分享的内容进行整理重塑,并补充一些当时没有表达的细节,期望能够给更多人更多的启发。

团队小王的疑惑

圣诞那天晚上我值班,时间已经很晚了,发现团队小王同学还没回,好奇心驱使于是走过去聊了几句:原来他等着与另一个系统同步发版,辛苦却无恙。不过我好为人师的臭毛病(主要对新同学)发作,于是各种问东问西。作为过来人我太清楚职场新人的处境了,疑惑多,如果性格偏内向还不喜欢表达困惑。了解后发现小王果然存在疑惑——如何通过架构设计降低业务代码修改的难度,并抛给我一篇 博文链接 ,同时声称链接是我们组另一个技术大佬推荐的。

在打发了小王同学后,抱着“不知就要学”的心态,我打开了他给我的那个链接,主要讲的是用 Go 语言实现一种架构,内容可谓又臭又长。当天夜里无眠,我一边盯着监控面板一边理解那篇博客的内容。到第二天调班休息的时候,心里还放不下那篇博客,于是困了睡,睡醒了就打开电脑读几段,感觉到困了再继续睡,最后终于理解了文章的主旨思想及其实践的含义。

这件事情触发了我几点思考:1)小王同学的疑惑是否值得探索并解答,即“如何通过架构设计降低业务代码修改的难度”;2)能够通过架构设计降低业务代码修改(对应功能的增删改)的难度吗?3)小王做的项目的架构最初是我参与设计的,如果让我对架构进行二次设计,我是否会采用其他的架构方案?4)这个项目使用了团队内部的研发模板进行初始化,而模板的原型我也是参与制定者之一,那么这个模板所包含的架构设计是最佳实践吗?5)如果让我二次设计研发模板,会改成另一种设计吗?

通过思考得到上面问题的答案后,结合经验我又进一步深化琢磨了几个问题:a)团队应该采取什么样的方式来减少职场新人的困惑,并尽可能降低这种困惑给团队和当事人引起的不适感?b)职场新人应该如何提升自己从而拥有架构的能力?c)团队里的知识应该采取什么样的方式进行传递继承?d)对于一些高阶的方法论,如何让实践经验欠缺的同事吸收消化并运用到实际工作中?

问题需要一个一个地解决。

Clean-Architectrue(整洁架构)

过去对架构思考的比较少,不过最近两个月因为工作上遇到的一些问题我一直在琢磨“分层”的技巧,并尝试论证“分层”的好处,同时细化“分层”所应遵循的原则,目标是提升自己的效率,优化团队成员的合作效率。顺着小王同学给的链接了解到整洁架构(The Clean Architectrue)的理论,算是找到了“分层”的理论支持,真可谓一个意外的收获了,

Clean-Architectrue的整体思想

Y7nM7fn.jpg!web

Clean-Architectrue架构图,摘自The Clean Architecture

分层思想

上图显示了整洁架构的示意图,其中每个同心圆层代表了软件开发中的不同层,越是靠近中心,同心圆层所代表的东西越抽象。可以这么理解,内部的圆层定义的是规则,外部的圆层定义的是实现机制。

整洁架构试图聚合这样几个特点:1)框架无关,即架构设计不依赖任何一个既有的开发框架,因此理论上可以使用任何框架来实践整洁架构。2)业务规则的可测性,即可以方便地测试业务逻辑所涉及的代码,不依赖UI、数据库或者 Web 服务等外部元素。3)功能实现不依赖 UI 的实现细节,比如同样一套后台系统可以用在 Web 应用,也可以用在 App 原生应用。4)业务逻辑不依赖数据库的实现细节,可以把数据保存在 Oracle、SQL Server、Mongo、MySQL 等任意一种数据库中,同时业务逻辑不需要做任何改变。5)总结起来就是:业务逻辑对外界实现完全没有依赖,任你外面的实现细节如何改变,核心业务逻辑不需修改。

依赖规则

为了实现上面所讲的特点,只需要遵循依赖规则:只允许外部圆层的代码依赖内部圆层的代码,反之则禁止。换言之,内部同心圆层的代码不知道任何外部同心圆层的代码,比如内层的代码一律禁止引用外层声明的函数、类、变量或其他任何元素。

其实上面的规则还隐含着另一个规则:当有数据传递时,只允许外部圆层接受内部圆层的数据格式,反之则不允许。这也是为避免外部的代码逻辑影响内部的代码逻辑(思考一下为什么)。

整洁架构中的术语介绍

  • 实体(Entities)层:主要封装企业范围的业务规则,可以认为是代码要实现的核心业务规则,比如电商里涉及到的购物规则。
  • 用例(Use Cases)层:主要封装特定于应用的业务规则,比如电商里普通用户购物与管理员管理货物信息,二者所涉及的用例是不一样的。
  • 接口适配(Interface Adapters)层:主要包含各种适配器,负责把从实体层和用例层流出来的数据转换成为更外面一层需要的数据格式(比如转换成为适合数据库保存的格式,或者适合 Web 页面展示的格式)。
  • 框架与驱动(Frameworks and Drivers)层:这是最外面的一层,是很多工具(或库)的具体实现细节所在层,比如 Web 框架(考虑一个应用可以做成网页版应用,也可以做成单机版应用)、数据库工具包(考虑各种 ORM 的实现,以及各种数据库的驱动依赖包实现)等。

值得说明的是,虽然整洁架构的示意图中只显示了四层结构,但是可以根据业务规模调节层数,也就是说层数不是关键,关键在于分层思想以及依赖规则。

一个关键点——控制反转

依赖规则里规定“只能外层的代码依赖内层代码”,但是在实际代码中势必会出现这种场景:外层的代码调用内层的代码处理数据,数据经过内层代码作用后需要再反传给外层进行其他处理(比如把处理好的数据保存到数据库,保存到数据库的逻辑在外层)。Clean-Architectrue架构图的右下角给出了一种示意,那么如何让这种场景满足依赖规则呢?答案就是控制反转。

对于 Go 语言来说,大体的实现是在内层定义一个 Interface 类型的数据,内层代码通过操作 Interface 的方法来实现业务逻辑,在外层实现这个 Interface 类型所包含的方法,从而达到反向控制的目的。其实这是一种设计模式,有点类似工厂方法模式或者抽象工厂模式,不是 Go 的专属,其他语言(比如 Java)均可以实现。

知己知彼以后,我设计的架构有问题吗

在领悟了整洁架构的思想后,我又把小王同学给我的又臭又长的博客读了两遍,然后思考我做的项目架构以及参与制定的开发模板是否有必要修改,假如要修改的话如何修改?如果不需要修改的话理由是什么?

经过一番分析后,我认为我做的那个项目架构没有问题(不排除一些细节存在疏漏),不需要修改。我是从具体的业务复杂度来考虑的,改进版的 MVC 架构已经足以应对。1)假如按照整洁架构的思路把项目重构一遍,并没有办法降低需求的增删改造成的开发量,该头疼的地方依然要头疼;2)项目采用小巧精悍的 MVC 架构也能够满足快速敏捷的要求,而整洁架构的定位是大型的企业级应用或应用生态,反而不适用于小复杂度的项目开发;3)重构是一件劳民伤财的事情,轻易不能做。这么分析以后,我参与自研的开发模板已经足够大部分项目使用,至少架构层没有修改的必要(其他的层面或许有改进的必要,比如通用依赖库的使用方法、测试用例编写规范等)。

虽然思考到最后发现不需要做什么,但这次思考的意义在于,让我意识到架构这件事情,并开始思考架构的含义及意义。那么,架构是什么呢?

架构是什么

架构是什么呢,可能这个问题和“什么是最好的编程语言”一样,不同的架构师会有不同的观点。在 2018 年上半年的时候,我参与过一次雷达峰会,对其中一位讲师抛出来的观点比较认同(当时没有太大感触,思考完上面的问题后才开始反刍这些观点):1)架构为业务而生;2)架构聚焦于解耦;3)架构意味着沟通与协作;4)架构需要为时间买单(可以理解为架构的进化)。

我们可以先看几个流行的架构图来体会一下。

几个流行的架构图

Kubernetes 的架构图:

ZBnA7jr.jpg!web

相信每个了解云技术栈的开发者都见过上面的 Kubernetes 架构图,至少听过其中部分组件的名称。这张图很清楚地描述了 Kubernetes 包含的各个组件及其协同运作的方式,这不仅仅给 Kubernetes 项目的开发者一些开发方向的指引,同时也让 Kubernetes 的使用者能够方便地讨论各种问题。

Istio 架构图:

fAnEZbe.jpg!web

业界提到 Service Mesh 的时候必提 Istio,它的架构如上图所示。可以注意到在 Istio 的架构中,所有的流量都通过 Proxy 进行接管,而 Proxy 是作为一个独立的进程与业务进程运行在同一个实例中(多指 Kubernetes 里的 Pod),那么对这个 Sidecar 就有比较多的要求了,比如要轻量级、高效、便于配置部署等等。由架构图我们也可以反推得出,在微服务化改造的过程中,应该控制自己业务的复杂度不能拆的太细碎,否则边界成本会变得非常大。

VXLAN虚拟网络架构图:

ZjUVRfe.jpg!web

这是我之前的一篇博客的示例图,描述了 VXLAN 的虚拟网络架构,这张图让很多讨论变得十分容易。

架构存在的意义

  1. 就像前面描述的,架构存在的主要意义是为了解决某个业务问题,最终都可以对应到某个具体的技术方案;换句话说,世上没有通用的架构方案,只有适合某个业务难题或某个领域难题的架构方案。面对架构的问题,我们不能生搬硬套,如果想得到适用的架构,必须要深入了解业务的细节,唯一的捷径是承认这个事实,没有其他捷径。
  2. 在把握了业务的所有细节后,接下来做的一件事情是解耦,把业务细节分类分组,拆分成不同的模块,继而放到不同的层里。
  3. 定义好了模块的边界和不同层的边界,接下来就可以派发工作了。有了详细完整的架构设计,大家协同开发也变得简单顺利;首先大家沟通的时候能够十分清楚彼此要表达的问题细节,其次各个模块开发完到功能对接的时候彼此也能快速理清楚关注的点(比如必须要实现哪些个方法、函数或接口)。
  4. 最后在大家的共同努力下,项目终于完成了,但是不要以为到此结束。根据实践经验来看,所有的项目都会有新需求,或需求更改,这个时候很大概率会对原有的架构造成冲击。架构上该如何应对呢?理论上只要业务没有彻底改变,在原有的架构上做调整就可以满足需求了。因此原有架构的资料(所有图文文档、音视频等)就变得很重要,甚至一些决策的细节参考标准有时候也能起到关键的作用(写文档是给架构方案舔砖加瓦的一种方式)。

架构师养成方法

架构设计,往往要求开发者拥有宏观的视角,在实践经验不足、业务了解不够全面的情况下,是谈不上做架构的。不过,大部分开发者虽然做不了大的架构,但是可以尝试做一些微小的架构设计,从而建立自己的信心,然后 1)努力刻苦锻炼自己的实战经验(下苦功夫了解所在技术栈里的框架细节、依赖库细节、中间件细节,等等),2)同时积极主动地了解、思考尽可能多的业务细节。如此坚持下去,成为牛 x 哄哄的架构师指日可待。

领域驱动设计(DDD)

领域驱动设计(DDD)是一种实用的架构实践方式,它是一个成体系的方法论,这本书 详细地介绍了这个实践。文本无法通过一个小节的文字详述它,仅通过它的两个前提和两个实践方法先大体了解一下它的概念。

DDD 的两个前提:1)在⼤大多数软件项⽬目中,主要的焦点应该是领域和领域逻辑; 2)复杂的领域设计应该基于模型。这两个前提条件把领域驱动所关注的点变成了模型的创建,因此这本书 很大篇幅的内容都是在讲模型相关的内容。

DDD 的两个实践:1)迭代开发;2)开发⼈人员与领域专家具有密切的关系 。第一条侧重技术上的持续性,第二条侧重业务上的理解。

小结

个人小结

从小就认定了“好好学习”的道理,所以一直非常努力刻苦地对待学业,不过那时候除了有一个好成绩外并未感觉到对自己的影响,甚至在硕士学位结业的时候我还在怀疑自己刻苦那么多年的意义。参加了工作后,一切与钱挂钩,那种努力却看不见成长的感觉挺折磨人的,这一度让我感到迷茫,并且持续了好久好久。终于有一天,就好像从量变到了质变(我印象中是读瑞·达利欧的《原则》这本书那段时期,那段时期发生比较多的事情),一切都渐渐变得清晰明朗。我发现很多工作中需要的知识已经在课堂上学习过,生活中的很多道理也已经在课堂上学习过。我知道了自己所知道的,从而加以运用;同时也意识到自己所不知道的,从而有的放矢地学习精进自己的知识储备。

本文小结

本文简单介绍了 Clean-Architecture 的思想,并通过几个示例介绍了架构原理及其应用实践,最后尝试着解答了职场新人对架构的一些疑问,并给出了一些精进才能的建议。最后小结部分感慨了一下自己的职业生涯,给自己的未来定了一个基调——向着另一个更高的未知层冲刺。

原文链接: https://jingwei.link/2018/12/3 ... .html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK