Action-Domain-Responder(译)

原文:https://herbertograca.com/2018/09/03/action-domain-responder/

这篇文章是软件架构编年史()的一部分,这部编年史由一系列关于软件架构的文章组成。在这一系列文章中,我将写下我对软件架构的学习和思考,以及我是如何运用这些知识的。如果你阅读了这个系列中之前的文章,本篇文章的的内容将更有意义。

MVC 诞生于 1979 年,它诞生于使用 CLI 用户界面的桌面应用上下文中,它暗示如果用户外部因素导致数据库变化,那么 UI 就应该自动地变化。同样的模式也可以完美地应用在稍后出现的 GUI 桌面应用上。

然而,它却和 Web 应用一直在磨合中,因为大多数 Web 应用不会用 UI 变化来作为服务端发生的变化的后果,它们总是从 UI 发起对服务端的调用来更新界面。

前面我已经介绍过 MVC 及其变种(),而这篇文章将介绍另一个变种:由 Paul M. Jones 提出的 Action-Domain-Responder

2014 – Action-Domain-Responder

ADR 模式由 Paul M. Jones 于 2014 年提出,其思想和 RMR 一致,就是将 MVC 应用于 Web REST API 上下文。ADR 最早的解释相当简单明了,我实在想不出更好的说法了,所以我简单地将其中部分内容复制/粘贴到了这里,并增加了一些评论。

Action

是连接 DomainResponder 的逻辑。它使用收集自 HTTP 请求的输入调用 Domain,然后使用构建 HTTP 响应所需的数据调用 Responder

你可以在这里找到 Action 的例子。

Domain

是组成应用核心的领域逻辑的入口,它根据需要修改状态并保存。 它可能是事务脚本、服务层、应用服务或者其它类似的概念。

你可以在这里找到 Domain 入口的例子。

Responder

是基于自 Action 接收的数据创建 HTTP 响应的展现逻辑。它处理状态码、标头与 Cookie、内容、格式与转换、模板与视图,等等。

你可以在这里找到 Responder 的例子。

它如何工作

  1. Web 处理程序收到 HTTP 请求并派发给 Action;
  2. Action 调用 Domain,从 HTTP 请求里收集任何需要的输入给 Domain;
  3. 然后 Action 使用创建 HTTP 响应所需的数据(通常是 HTTP 请求和 Domain结果,如果有的话)调用 Responder;
  4. Responder 使用 Action 提供给它的数据构造 HTTP 响应;
  5. Action 将 HTTP 响应返回给发送 HTTP 响应的 Web 处理程序。

Responder 基于对领域响应的解析和理解来构造 HTTP 响应,而领域响应又依赖操作方法的用例。这意味着每个操作方法都需要一个特定的 Responder。如果我们将所有资源方法放到同一个控制器中,我们就需要实例化全部 Responder 并注入到控制器中,而我们在一次 HTTP 请求中只会使用一个 Responder,这显然不是最优的方案。解决方法是每个控制器只有一个方法,这种控制器和它唯一的操作方法就是 ADR 所说的 Action。

既然 Action 只有一个方法,方法名就可以使用通用的 runexecute、或是 PHP 中的 __invoke,让这个类变成可以调用的。然而,由于其思想是将 MVC 模式应用到 HTTP REST API 上下文,Action(控制器)名称会被映射为 HTTP 请求方法,因此我们将得到名为 GetPostPutDelete...的 Action,清楚地表明了每个 HTTP 请求类型调用的控制器。作为一种组织形式,一个资源的所有 Action 应该被一起放以该资源命名的文件夹下。

与 ADR 混为一谈

Anthony Ferrara 对比了 ADR 和 RMR ,认为“它们是同样的模式,只是细节有所调整”。

我不同意这个观点。实际上我认为 Anthony Ferrara 对它的理解是错误的(他很聪明,只知识渊博,但人总有犯错的时候):

  1. Resource==Domain
    RMR 中的 Resource 并非 Domain,而是领域对象,是领域实体,但 ADR 中的 Domain 与全部领域对象有关,所有的实际和它们的关系作为一个整体;
  2. Representation==Responder
    RMR 中的 Representation 是发回给客户端的响应,但 ADR 中的 Responder 是一个对象,它的职责是基于给定内容和给定模板构造响应。
  3. 它和 RMR 一样与 HTTP 耦合在一起,很难创建非 HTTP 界面
    既然 ADR 将 Domain 看作是一个整体而不是一个实体,Action 也不在领域对象内部,那么 Action 只会要求领域对象执行一些业务逻辑。所以 Domain 没有与 UI 耦合,我们可以创建一个CLI 命令,使用领域对象执行一些任务。

我对这种模式的看法

在我看来,本文撰写之时,ADR 是 MVC 在 HTTP 请求/响应范式上的最佳应用,因为它清晰地将 HTTP 请求和响应对应到了 Domain 请求和响应,同时仍然保持了 Domain 和展现层之间完全的解耦。

HTTP 请求方法(期望对资源进行的操作)被明确地连接到接收 HTTP 请求的代码,因为每个 HTTP 方法都直接映射到一个控制方法的名字。这样做还有一个额外的好处,那就是产生了清晰、明确和可预测的代码组织结构,而不是具有大量操作的控制器,这些操作通常是不相关的,命名糟糕,不可预测,而且常常执行非常类似的操作。换句话说,它避免了混乱的意大利面式的控制器和操作。

还有,它也非常好地解耦了交互自身的代码(调用领域)和理解交互结果(领域响应)并转换给客户端的代码。

然而,有一些问题仍然需要注意:

  1. 该模式专为 REST API 而设计,因此,在这种形式下它还没有完善到可以用于 HTML 界面的 Web 应用中(例如,该如何命名创建资源之前展示表单的操作?);
  2. 一个控制器只有一个方法让这种模式更啰嗦,因为,举个例子,相较于一个拥有四个操作(公有方法)的控制器(类),我们拥有的是四个控制器和四个操作。
  3. 为每个操作创建 Responder 也会让这种模式更啰嗦。如果将领域响应转换成 HTTP 响应的逻辑很简单,我们应该思考一下是否值得使用 Responder。不用 Responder 意味着我们可以在每个控制器中拥有多个方法,每个方法依然与一个 HTTP 方法对应。

关于第二点和第三点, Paul M. Jones 自己也承认并同意有些情况下使用简化的模式是可以接受的,尽管不那么优雅,但足以应对手头上的上下文。

关于第一点,我认为该模式可以轻松地进行扩展,就能完全应用于 HTML 界面:我们可以模拟一些 REST API 没有的额外的 HTTP 方法,专门处理 HTML 请求。例如,我们可以在一个 REST API 中使用 PUTPOST 来创建和/或更新资源,而这就是该资源所需的全部方法,可是对于 HTML 界面来说,我们在发送 PUTPOST 之前需要一个表单,但是没有 HTTP 方法专门供客户端请求创建资源或编辑的表单。然而,我们可以使用一个带有 createedit 标头的 GET 请求来模拟它,前端控制器可以解析该请求并转发给对应的名为 CreateEdit 的操作,然后这些操作将回复对应的 HTML 表单。然而,对于创建额外的自定义 HTTP 方法,我们要非常小心和克制…否则可能导致产生过多的自定义 HTTP 方法和一大堆绑定到意大利面条式的操作的自定义 HTTP 方法!!因此,小心谨慎地采纳最后这个建议

引用来源

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,507评论 6 13
  • 在iOS开发中经常会涉及到触摸事件。本想自己总结一下,但是遇到了这篇文章,感觉总结的已经很到位,特此转载。作者:L...
    WQ_UESTC阅读 5,873评论 4 26
  • 1 几年前,坐过一次从北京到上海的火车,无座,十多个小时,站着,直到上海。那时,就发誓,只要我活着,永远不会再坐无...
    陈清伟阅读 33,282评论 1 1
  • OpenOffice组件docx/doc转html 准备工作 CentOS 安装OpenOffice资源准备ope...
    人形大叔阅读 2,669评论 0 1
  • 男朋友月入6000,每月只留1500其他都给他父母,想让他买个衣服什么的,他就说没钱,说自己也不够,说把钱给家里。...
    小馋猫的傻傻狗阅读 302评论 2 0