Day 21 「事有经重缓急」Clean Architecture 简易入门

古语有云:「岁有凶穰;故谷有贵贱;令有缓急;故物有轻重。」旨在告诉後人,做任何事情,一定要先搞清楚状况,把事情的优先顺序排好再开始动手,方为上策。时间有限,但要做的事就是那麽多,「重要的事先做」讲起来容易,做起来真的很难。

接下来的几天,我们会聊一个近年来,後端工程师或多或少都听过的主题:「Clean Architecture」。

Clean Architecture 出现後,对笔者的工作影响非常巨大。笔者不算是架构很强的工程师,但正因如此,笔者发现在做後端工作时,只要照着 Clean Architecture 的规划走,就几乎不会出什麽乱子。而且在结构乾净了以後,测试写起来特别好写,重构起来也比较有个依据。对此,笔者如获至宝!

因此笔者在本系列文章中,才特别想要来「野人献曝」一下,不求能教大家太多专门的知识,但期望有多点人能感受到它的好处,而加以研究,使大家工作能更顺利,那我目的就达到了。

简介

Clean Architecture 是 Uncle Bob 在 2017 年出的一本关於「架构」的书。在这之前,出现过很多经典的「系统架构」设计,如 Hexagonal Architecture, Onion Architecture 等。Uncle Bob 认为,这些架构虽各有千秋,但都有一些相似之处:

  1. 与框架分离:框架虽好用,但它就是个工具,把这些工具隔离於你产品的核心逻辑之外,才不会被这些工具限制了你的开发。
  2. 可测试:系统核心逻辑可以独立於 UI、资料库、网路等「细节」。这些细节可被任意抽换,以方便我们测试核心逻辑。
  3. 独立於资料库:把 Oracle 换掉,换成 MySQL、Mongo、任何其它资料储存工具,都不会破坏系统本身。事实上你的业务逻辑根本就不知道、也不在乎资料存在哪。

分层

Uncle Bob 认为,前述这些很棒的架构设计之所以很棒,乃因它们都具备上面的特性。於是综合这些优点,加上自己的见解,Uncle Bob 在 2017 年提出了「Clean Architecture」这个架构。

Clean Architecture 把系统分成四层,这四层不是由上而下排列,而是由外而内排。 排列的依据为:「越细节的越外面,越核心的越里面。」这四层分别为:

Entities

核心业务、重要资料所在地。不管你在什麽场景,做什麽操作,在你的问题领域中不论有没有系统帮忙,你都必须得做的事。

Use Cases

这一层包围在 Entity 之外,定义着对於 Entity 逻辑与资料的使用时机与操作顺序,是一个管「自动化」的家伙。

Interface Adapters

系统的边界,对外取得请求内容,转换成内层认识的样貌、并寻找适当的 Use Case 来执行任务,最後再转换成外界认识的样貌送出去。同时,系统对外发出的请求,也会在这一层转换。

因为位处系统边界,做的事大多都是一些转换的工作,所以命名为 Interface Adapter。

Frameworks & Drivers

系统中最细节的部分,大部分都是 I/O,举凡网路、档案、资料库、人机介面等,都属於此范围。

那麽什麽叫核心,又什麽叫细节呢?实际操作时,其实就是:「离 I/O 越近的越细节,在外层;离 I/O 越远的越核心,在内层。」

Uncle Bob 说:「Entity 与 Use Case 是你生意能经营的主要推手,必须慎重对待。I/O 虽然有重要,但是是重要的细节,应该要可以简单被抽换。」

这里留个思考题:「Data driven 与 UI driven 的开发方式,与 Clean Architecture 搭配使用的话,会发生什麽事呢?」

三大原则

Clean Architecture 讨论了很多设计上的注意事项,而简言之可以归纳出三大原则:

  1. 分层原则:如上一节所述,所有系统元件应该要分门别类安置,不要乱放。
  2. 依赖原则:依赖应该要尽量「由外而内」,不能「由内而外」,否则依赖关系会乱掉,造成不必要的耦合。
  3. 跨层原则:外层只能认识同一层或往内一层的元件,不能认识「两层以外」的任何元件。

依赖原则的挑战

来思考一下。依赖原则说不能由内往外依赖,那当 Use Case 需要操作 Entity,但 Entity 资料存放在 DB 里时,应该怎麽办呢?」里,就会需要呼叫呼叫物件导向设计的 DIP:Dependency Inversion Principle 登场救援了。

DIP 说,业务逻辑不该依赖资料实作,而应使两方共同依赖一个抽像介面。

在上面的场景,内层的 Use Case 不该认识外层的 Repository,所以这时 Use Case 应该要定义一个 Repository 抽象介面,而安心的把这个介面的实作放在 Interface Adapters 层,这样就可以又符合 Clean Architecture 与 DIP,又符合业务需求了。

跨层原则的重要性

说到跨层原则,有时候遵守起来挺麻烦的。举例来说,照 Clean Architecture 的规划,Adapter 层只可以认识 Use Case 层的物件,它对最内层的 Entity 应该要一无所知。所以,就算你要回传给前端的物件格式与 Entity 几乎一模一样,也不可以直接回传,要嘛在 Use Case 要转一下格式,要嘛要套用 CQRS 模式,跳过 Use Case 层,真接与同一层的 Repository 合作,让它回传一个客制化的物件。

为什麽这麽麻烦?这时又要请出另一个物件导向设计原则的 SRP:Single Responsibility Principle 了。Entity 身为系统的核心逻辑,理应只关心「领域模型」中最重要的运算与资料,不应该管外层表现的需求。会这样设计,为的就是使核心业务不被外层的细节干扰。你如果为了一时方便,让负责与前端网页沟通的 Adapter 去直接存取 Entity,那以後网页要拿什麽值,要改什麽格式,核心的 Entity 就不得不改了。

注意到了吗?「最内层的 Entity 因为最外层的网页需求,而需要修改」耶!这不是反模式,什麽才是反模式?所以,跨层原则虽麻烦,但好好遵守,架构才会乾净!

Kuma 的使用心得

使用 Clean Architecture 以後,元件的安排有了依据,核心运算逻辑不会依赖於细节的资料库或是网页,整个系统不论整洁度或是灵活性都比以前好很多。重点是,安排工作时,知道什麽要先做,什麽可以晚点决定,不会进退失据。总之至少目前为止,用起来挺不错的!

谜之声:「事有轻重缓急,月有阴晴圆缺 XD」


图片截自 Wikipedia

Reference

  1. Rober C. Martin, Clean Architecture: A Craftsman's Guide to Software Structure, Prentice Hall, 2017
  2. The Clean Architecture:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
tags: ithelp2021

<<:  [FGL] 程序开发(3) - 输入用的INPUT系指令与DIALOG

>>:  Day7 Html常用标签_2

Day22:今天来聊一下如何用Ghost Eye来取得Web Server资讯

通过执行Web Server Footprinting,我们可以收集到有价值的系统资讯 例如帐户资讯...

【第 27 个第一次】 抓住每个企业识别展示的机会 ! 快速导览网站浏览时视觉上需要准备的基本资讯

Day 27 - 这是一个三方合作的温馨(瘟腥)故事 设计师 : 架站的资料记得拿 Favicon,...

day23 stateFlow状态流,又是没梗的一天

前面我们讲到如何应coroutine的flow和liveData合作,但android其实还推出了另...

[JS] You Don't Know JavaScript [this & Object Prototypes] - Object [下]

前言 在Object [上]中我们介绍了物件的宣告、型态、拷贝等等特性,接下来我们继续介绍物件中都有...

Day30 用python写UI-Canvas(二)

最後一天~~~ 压轴当然是要最好玩的东西啦,讲完今天的内容,大家就可以在介面上自由的创作了! ♠♣今...