原文:https://herbertograca.com/2018/09/03/action-domain-responder/
這篇文章是軟件架構(gòu)編年史(譯)的一部分忠荞,這部編年史由一系列關(guān)于軟件架構(gòu)的文章組成。在這一系列文章中,我將寫下我對(duì)軟件架構(gòu)的學(xué)習(xí)和思考,以及我是如何運(yùn)用這些知識(shí)的蛉顽。如果你閱讀了這個(gè)系列中之前的文章缨睡,本篇文章的的內(nèi)容將更有意義。
MVC 誕生于 1979 年幅虑,它誕生于使用 CLI 用戶界面的桌面應(yīng)用上下文中,它暗示如果用戶外部因素導(dǎo)致數(shù)據(jù)庫(kù)變化顾犹,那么 UI 就應(yīng)該自動(dòng)地變化倒庵。同樣的模式也可以完美地應(yīng)用在稍后出現(xiàn)的 GUI 桌面應(yīng)用上。
然而炫刷,它卻和 Web 應(yīng)用一直在磨合中擎宝,因?yàn)榇蠖鄶?shù) Web 應(yīng)用不會(huì)用 UI 變化來作為服務(wù)端發(fā)生的變化的后果,它們總是從 UI 發(fā)起對(duì)服務(wù)端的調(diào)用來更新界面浑玛。
前面我已經(jīng)介紹過 MVC 及其變種(譯)绍申,而這篇文章將介紹另一個(gè)變種:由 Paul M. Jones 提出的 Action-Domain-Responder。
2014 – Action-Domain-Responder
ADR 模式由 Paul M. Jones 于 2014 年提出,其思想和 RMR 一致极阅,就是將 MVC 應(yīng)用于 Web REST API 上下文胃碾。ADR 最早的解釋相當(dāng)簡(jiǎn)單明了,我實(shí)在想不出更好的說法了筋搏,所以我簡(jiǎn)單地將其中部分內(nèi)容復(fù)制/粘貼到了這里仆百,并增加了一些評(píng)論。
Action
是連接 Domain 和 Responder 的邏輯奔脐。它使用收集自 HTTP 請(qǐng)求的輸入調(diào)用 Domain儒旬,然后使用構(gòu)建 HTTP 響應(yīng)所需的數(shù)據(jù)調(diào)用 Responder 。
你可以在這里找到 Action 的例子帖族。
Domain
是組成應(yīng)用核心的領(lǐng)域邏輯的入口栈源,它根據(jù)需要修改狀態(tài)并保存。 它可能是事務(wù)腳本竖般、服務(wù)層甚垦、應(yīng)用服務(wù)或者其它類似的概念。
你可以在這里找到 Domain 入口的例子涣雕。
Responder
是基于自 Action 接收的數(shù)據(jù)創(chuàng)建 HTTP 響應(yīng)的展現(xiàn)邏輯艰亮。它處理狀態(tài)碼、標(biāo)頭與 Cookie挣郭、內(nèi)容迄埃、格式與轉(zhuǎn)換、模板與視圖兑障,等等侄非。
你可以在這里找到 Responder 的例子。
它如何工作
- Web 處理程序收到 HTTP 請(qǐng)求并派發(fā)給 Action流译;
- Action 調(diào)用 Domain逞怨,從 HTTP 請(qǐng)求里收集任何需要的輸入給 Domain;
- 然后 Action 使用創(chuàng)建 HTTP 響應(yīng)所需的數(shù)據(jù)(通常是 HTTP 請(qǐng)求和 Domain結(jié)果福澡,如果有的話)調(diào)用 Responder叠赦;
- Responder 使用 Action 提供給它的數(shù)據(jù)構(gòu)造 HTTP 響應(yīng);
- Action 將 HTTP 響應(yīng)返回給發(fā)送 HTTP 響應(yīng)的 Web 處理程序革砸。
Responder 基于對(duì)領(lǐng)域響應(yīng)的解析和理解來構(gòu)造 HTTP 響應(yīng)除秀,而領(lǐng)域響應(yīng)又依賴操作方法的用例。這意味著每個(gè)操作方法都需要一個(gè)特定的 Responder算利。如果我們將所有資源方法放到同一個(gè)控制器中册踩,我們就需要實(shí)例化全部 Responder 并注入到控制器中,而我們?cè)谝淮?HTTP 請(qǐng)求中只會(huì)使用一個(gè) Responder笔时,這顯然不是最優(yōu)的方案棍好。解決方法是每個(gè)控制器只有一個(gè)方法仗岸,這種控制器和它唯一的操作方法就是 ADR 所說的 Action允耿。
既然 Action 只有一個(gè)方法借笙,方法名就可以使用通用的 run
、execute
较锡、或是 PHP 中的 __invoke
业稼,讓這個(gè)類變成可以調(diào)用的。然而蚂蕴,由于其思想是將 MVC 模式應(yīng)用到 HTTP REST API 上下文低散,Action(控制器)名稱會(huì)被映射為 HTTP 請(qǐng)求方法,因此我們將得到名為 Get
骡楼、Post
熔号、Put
、Delete
...的 Action鸟整,清楚地表明了每個(gè) HTTP 請(qǐng)求類型調(diào)用的控制器引镊。作為一種組織形式,一個(gè)資源的所有 Action 應(yīng)該被一起放以該資源命名的文件夾下篮条。
與 ADR 混為一談
Anthony Ferrara 對(duì)比了 ADR 和 RMR 弟头,認(rèn)為“它們是同樣的模式,只是細(xì)節(jié)有所調(diào)整”涉茧。
我不同意這個(gè)觀點(diǎn)赴恨。實(shí)際上我認(rèn)為 Anthony Ferrara 對(duì)它的理解是錯(cuò)誤的(他很聰明,只知識(shí)淵博伴栓,但人總有犯錯(cuò)的時(shí)候):
- “Resource==Domain”
RMR 中的 Resource 并非 Domain伦连,而是領(lǐng)域?qū)ο螅穷I(lǐng)域?qū)嶓w钳垮,但 ADR 中的 Domain 與全部領(lǐng)域?qū)ο笥嘘P(guān)除师,所有的實(shí)際和它們的關(guān)系作為一個(gè)整體; - “Representation==Responder”
RMR 中的 Representation 是發(fā)回給客戶端的響應(yīng)扔枫,但 ADR 中的 Responder 是一個(gè)對(duì)象汛聚,它的職責(zé)是基于給定內(nèi)容和給定模板構(gòu)造響應(yīng)。 - “它和 RMR 一樣與 HTTP 耦合在一起短荐,很難創(chuàng)建非 HTTP 界面”
既然 ADR 將 Domain 看作是一個(gè)整體而不是一個(gè)實(shí)體倚舀,Action 也不在領(lǐng)域?qū)ο髢?nèi)部,那么 Action 只會(huì)要求領(lǐng)域?qū)ο髨?zhí)行一些業(yè)務(wù)邏輯忍宋。所以 Domain 沒有與 UI 耦合痕貌,我們可以創(chuàng)建一個(gè)CLI 命令,使用領(lǐng)域?qū)ο髨?zhí)行一些任務(wù)糠排。
我對(duì)這種模式的看法
在我看來舵稠,本文撰寫之時(shí),ADR 是 MVC 在 HTTP 請(qǐng)求/響應(yīng)范式上的最佳應(yīng)用,因?yàn)樗逦貙?HTTP 請(qǐng)求和響應(yīng)對(duì)應(yīng)到了 Domain 請(qǐng)求和響應(yīng)哺徊,同時(shí)仍然保持了 Domain 和展現(xiàn)層之間完全的解耦室琢。
HTTP 請(qǐng)求方法(期望對(duì)資源進(jìn)行的操作)被明確地連接到接收 HTTP 請(qǐng)求的代碼,因?yàn)槊總€(gè) HTTP 方法都直接映射到一個(gè)控制方法的名字落追。這樣做還有一個(gè)額外的好處盈滴,那就是產(chǎn)生了清晰、明確和可預(yù)測(cè)的代碼組織結(jié)構(gòu)轿钠,而不是具有大量操作的控制器巢钓,這些操作通常是不相關(guān)的,命名糟糕疗垛,不可預(yù)測(cè)症汹,而且常常執(zhí)行非常類似的操作。換句話說贷腕,它避免了混亂的意大利面式的控制器和操作烈菌。
還有,它也非常好地解耦了交互自身的代碼(調(diào)用領(lǐng)域)和理解交互結(jié)果(領(lǐng)域響應(yīng))并轉(zhuǎn)換給客戶端的代碼花履。
然而芽世,有一些問題仍然需要注意:
- 該模式專為 REST API 而設(shè)計(jì),因此诡壁,在這種形式下它還沒有完善到可以用于 HTML 界面的 Web 應(yīng)用中(例如济瓢,該如何命名創(chuàng)建資源之前展示表單的操作?)妹卿;
- 一個(gè)控制器只有一個(gè)方法讓這種模式更啰嗦旺矾,因?yàn)椋e個(gè)例子夺克,相較于一個(gè)擁有四個(gè)操作(公有方法)的控制器(類)箕宙,我們擁有的是四個(gè)控制器和四個(gè)操作。
- 為每個(gè)操作創(chuàng)建 Responder 也會(huì)讓這種模式更啰嗦铺纽。如果將領(lǐng)域響應(yīng)轉(zhuǎn)換成 HTTP 響應(yīng)的邏輯很簡(jiǎn)單柬帕,我們應(yīng)該思考一下是否值得使用 Responder。不用 Responder 意味著我們可以在每個(gè)控制器中擁有多個(gè)方法狡门,每個(gè)方法依然與一個(gè) HTTP 方法對(duì)應(yīng)陷寝。
關(guān)于第二點(diǎn)和第三點(diǎn), Paul M. Jones 自己也承認(rèn)并同意有些情況下使用簡(jiǎn)化的模式是可以接受的,盡管不那么優(yōu)雅其馏,但足以應(yīng)對(duì)手頭上的上下文凤跑。
關(guān)于第一點(diǎn),我認(rèn)為該模式可以輕松地進(jìn)行擴(kuò)展叛复,就能完全應(yīng)用于 HTML 界面:我們可以模擬一些 REST API 沒有的額外的 HTTP 方法仔引,專門處理 HTML 請(qǐng)求扔仓。例如,我們可以在一個(gè) REST API 中使用 PUT
或 POST
來創(chuàng)建和/或更新資源咖耘,而這就是該資源所需的全部方法翘簇,可是對(duì)于 HTML 界面來說,我們?cè)诎l(fā)送 PUT
或 POST
之前需要一個(gè)表單鲤看,但是沒有 HTTP 方法專門供客戶端請(qǐng)求創(chuàng)建資源或編輯的表單缘揪。然而耍群,我們可以使用一個(gè)帶有 create
或 edit
標(biāo)頭的 GET
請(qǐng)求來模擬它义桂,前端控制器可以解析該請(qǐng)求并轉(zhuǎn)發(fā)給對(duì)應(yīng)的名為 Create
或 Edit
的操作,然后這些操作將回復(fù)對(duì)應(yīng)的 HTML 表單蹈垢。然而慷吊,對(duì)于創(chuàng)建額外的自定義 HTTP 方法,我們要非常小心和克制…否則可能導(dǎo)致產(chǎn)生過多的自定義 HTTP 方法和一大堆綁定到意大利面條式的操作的自定義 HTTP 方法2芴А溉瓶!因此,小心謹(jǐn)慎地采納最后這個(gè)建議谤民。
引用來源
2014 – Paul M. Jones – Action Domain Responder
2014 – Paul M. Jones – Action-Domain-Responder (Vimeo)
2014 – Paul M. Jones – The Template Is Not The View: A Brief Introduction to ADR(Youtube)
2014 – Paul M. Jones – Action-Domain-Responder: A Refinement of MVC (slides)
2014 – Anthony Ferrara – Alternatives To MVC
2018 – Paul M. Jones – Model View Controller and “Model 2”
2018 – Paul M. Jones – Comparing “Model 2” MVC to ADR
2018 – Paul M. Jones – Tradeoffs in ADR
2018 – Paul M. Jones – Objections to ADR