为什么我再也不使用MVC框架了?

banq 16-02-16
                   

该文通过作者亲身设计思考经历,从遭遇MVC使用问题开始,然后引入函数式Reactive响应式编程,重新设计了前端架构,最后颠覆了基于OO范式的传统MVC模式。

以下是原文大意翻译:

这些天我的最糟糕工作是为前端开发人员设计API,下面对话是不可避免的:
前端开发人员: 这个屏幕有数据元素x,y,z... 你能创建一个API输出格式是 {x: , y:, z: }吗?
我:Ok

其实毫无争议的是,无数个API项目都是和经常变化的屏幕绑定的。你会有很多针对不同平台或终端设备的不同API,甚至Sam Newman将其总结为BFF模式,该模式建议为每个类型的设备或平台开发对应特殊的API,甚至为APP的不同版本。Netflix的Daniel Jacobson将其解释为Experience APIs,无语。

几个月以前,我开始一段思考旅程,思考为什么我们会最终变成这样,我们还能够做些什么?这段思考旅程将我引向了应用架构中最教条的模式:MVC。当我再游历了reactive响应式编程和函数编程等技术思想以后,我开始聚焦简单和业界擅长的偷偷膨胀的过程,我相信你会感兴趣我的发现。

每个屏幕都在使用的模式是MVC,也就是Mode-View-Controller,MVC在没有Web时已经被发明出来,其软件架构是很棒的,起初,厚客户端是直接和一个数据库交互访问通讯,数十年以后,使用了MVC,用于构建OmniChannel 应用

Angular2即将发布,也许现在是重新评估MVC用途的最好时候,重新评估MVC框架为应用架构带来的价值。

90年代后期,MVC模式被Struts采用到基于Http的Web应用中,今天它是所有应用的基石。

MVC如此光彩照人,以至于react.js只能使用委婉的方式介绍它们的框架似乎偏离了MVC主流:“React is just the View in MVC”.

当我去年开始使用React时,我感觉有些非常不同之处,如果你改变某个地方的一部分数据,那么立即,而且没有视图View和模型Model之间明显的交互,整个UI就改变了,这就是说,我很失望于React的编程模型,变得孤独起来,因为从MVC角度来看,React模型将MVC分离的几个关注混淆在同一个组件中。

作为一个服务器端后端的API设计者,我得出结论,肯定没有好的办法将API调用和React前端协调在一起,精确地说,因为React只注重视图View,竟然没有MVC中的控制器Controller。

Facebook一直拒绝在框架层次弥补这个“缺憾”,React团队首先引入了Flux模式。因为对Facebook的失望, Dan Abramov甚至推荐了另外一个模式:Redux,某种程度上走上了正确方向,但是也没有提供合适方式来连接API和前端。

Google在这方面一直似乎很擅长,不断推出从GWT, Android SDK到Angular。但是Angular 1并不是基于组件的概念,而是使用定制的逻辑将控制器和页面各种元素连接在一起,由此带来了复杂性。那么基于组件的Angular2会更加简单一些吗?也不是非常简单,Angular2核心包有180个语法,整个框架将近有500个语法,都是基于HTML5和CSS3,谁有时间学习和掌握这样一个框架用来构建一个Web应用呢?那么Angular3会如何呢?

在使用了React和预见了即将发布的Angular2,我感觉沮丧:这些框架有组织地强迫我使用BFF模式,每个后端API都必须匹配一种屏幕的数据集。

我开始诅咒了,再也不使用React Angular 和任何MVC框架来开发Web应用,并且试图在视图和底层API之间发现一个更好的衔接方式。

我真正喜欢React是它的模型和视图之间关系,React并不是基于模板的事实,视图自己没有办法请求后端的数据,你只能将后端数据传递给视图。

当你研究React久了以后,你会意识到React在一系列纯函数中解耦了视图,这就是JSX语法:
<V params={M}/>
和如下纯函数没有任何区别:
V = f( M )

我使用自己的Gliiph作为案例说明这种JSX的函数意义,它是使用如下函数构建的:


这个函数是如下模型作为参数输入:


当你意识到普通Javascript函数也能很好实现时,你会疑问为什么还用使用React的这种方式呢?

是因为virtual-dom虚拟Dom?如果你喜欢你可以需要它,但是不肯定更多人会喜欢,还有其他可选如:https://github.com/Matt-Esch/virtual-dom

是因为GraphQL? 不是真的,GraphQL只不过是一种声明式的方式来创建一个视图模型。强迫变形模型去匹配视图本身就是一个问题, 而不是解决方案。它不过是被前端工程师因为来自视图需求
强行编写的,希望从客户端获得它们想要的,根本不会考虑其他任何设计。

GraphQL团队好像在JSX语法后面迷失了,微妙的改变是函数隔离了模型与视图,不像模板性语言或被前端工程编写的查询语言(GraphQL),函数并不需要模型强迫去适应视图:

当视图从一个函数(而不是一个模板或查询)创建后,你能转换需要的模型到视图进行渲染,不需要任何额外的代价比如变形扭曲你的模型。

举例来说,如果一个视图显示模型中一个值v,并且还需要显示一个图像显示器用来表达这个值v是很好 好或坏几个程度,这里没有任何理由将显示器显示的这几个程度数据也放入你的模型:因为函数应该可以简单地使用模型中的值v计算出显示器的这个几个程度值。

直接将这种计算嵌入视图也不是好主意,但是将视图-模型view-model 作为一个纯函数也许并不困难,当你需要一个明确的view-model时,也没有什么特别理由需要使用GraphQL:

V = f( vm(M) )

这种函数式的方式有几个好处:首先,就像React,它允许你解耦你的视图到组件,他们创建的自然接口允许你以不同技术渲染你的视图,函数实现能够潜在地增强我们实现响应式设计。

我并不奇怪,以后几个与人们开始将HTML5主题作为基于组件的Javascript函数传递,而自己在做我的网站项目时,我弄一个模板,立即使用javascript函数将他包装起来,我再也不使用WordPress,在同样的努力付出情况下,我能够得到HTML5和CSS3的优点。

这种方式重新定义了设计者和开发者之间的关系,任何人能够编写这些javascript函数,尤其是模板设计者,不需要有任何绑定语法需要学习,没有JSX,没有Angular 模板,只是普通的Javascript函数。

有趣的是,从一个reactive流程角度看,这些函数能够部署到任何地方:后端或客户端前端。

但是更重要的是,这些方式允许视图定义与模型之间最小的联系,将决定权留给模型,以便其以最好的方式将数据带给视图,不像缓存 懒加载或混合管理orchestration,一致性是在模型的可控范围内,不像模板或GraphQL,再也不需要一种从视图直接发出请求的方式。

现在我们使用函数包装Html5+CSS3的方式解耦了视图与模型,那么下一步是如何创建一个模型,控制器应该是什么样,为了回答这个问题,让我们回到MVC。


(此处略去原作者分析MVC原理,直接得出结论)

如何整合MVC中的Action控制器到一个响应式reactive流程中,这里引入了Dr. Lamport发明的“Temporal Logic of Actions”,简称TLA+,在TLA+中,Action是一种纯函数:

data’ = A (data)

上面的A代表Action,那么,一个reactive MVC应该如下:

V = f( M.present( A(data) )

上面V代表视图View,而M代表Model,这个表达式意思是:当一个Action被触发时,它会计算一个来自集合或输入比如用户的输入的数据集,这些数据集将会被呈现给模型,由模型决定是否更新自己状态,一旦更新完成,视图将渲染新的模型状态,这样Reactive循环结束。模型持久或从数据库获得数据等流程不应该由前端工程师完成,这是后端工程师的职责所在。

Action应该是一个纯函数,没有任何状态或副作用。

一个Reactive MVC模型是有趣的,因为除了模型以外,其他都是纯函数。

(此处略去作者举例,直接查看他使用纯函数实现MVC各个部件的案例贴图)


这里是作者编写的开源库包:JavaJavascript,这是使用 WebSocket, Polling 和 Queuing实现浏览器和服务器交互的案例。

现在我们已经引入了一个新的模式,作为MVC的替换,也就是SAM模型(State-Action-Model),这是一个reactive 函数式的模型,其根源来自React.js和TLA+

SAM模式能够使用下面表达式表达:

V = S( vm( M.present( A(data) ) ), nap(M))

这个公式意思是:在一个action A动作实施以后,系统的V将被计算出来。这个关于模型的一个纯函数。

在SAM中,A和VM(View-Model)以及nap(next-action predicate预测)和S(代表状态)是而且必须是纯函数,使用SAM,我们普遍称谓状态(系统的属性值)是限制在模型这个范围内,改变这些值的逻辑是封装在模型中,无法从模型以外能够看到的。

一旦状态表现被创建,nap()代表下一个action的预测将是一个回调调用。以它自己方式渲染给用户。



该文还介绍了SAM的函数组合方式.....

SAM模式完成改变了前端架构范式,基于TLAA+,业务逻辑将被清晰地分解为:
Actions作为纯函数
CRUD操作在模型内部 (banq注:类型DDD领域模型)
控制自动Action的状态State。

使用SAM模式,微服务正好自然适合在模型之后,诸如Hivepod.io之类框架正好插入此位置,非常完美。

自此,几十年的面向对象消失了,如果没有reactive或函数式方式我几乎不能够思考。一旦使用SAM,我就能够聚精会神地设计API和服务,再也不必一个屏幕一个API服务了。

本贴是大意翻译,原文请参考:
Why I No Longer Use MVC Frameworks

[该贴被banq于2016-02-16 13:09修改过]

                   

7
lileiqx
2016-02-16 13:52

这种SAM模型很适合做响应式开发
[该贴被lileiqx于2016-02-16 17:08修改过]

SpeedVan
2016-02-21 16:50

对react的定位跟我想法一样。

但我还得评价:函数能力还是很弱啊
[该贴被SpeedVan于2016-02-21 16:51修改过]