要么TDD死,要么后端MVC死

其实这个观点我已经在“单元测试中的“单元‘如何定义?”中谈及,大概标题比较极端,吸引不少人兴趣,我再有逻辑的分析一下。

首先,我们必须有一个共认的上下文前提,这个前提应该是Bob大叔的“干净的架构”,图如下所示:

外圈依赖内圈,内圈不可依赖外圈,内圈代表业务,外圈代表技术架构。内圈核心业务实体应该不依赖外圈的UI或数据库。

Bob大叔将MVC模式中控制器Controller归于次外圈,而一般对业务实体和用例的单元测试也是和控制器Controller同处一个次外圈中,这里就造成了一个混淆,很多人在MVC控制器中实现对业务用例的单元测试,单元测试是TDD主张的测试优先的做法,而Ruby的MVC框架Rails创建者DHH就非常反对这种做法,认为这两者混淆在一起,破坏了MVC设计原本对控制器的定义。

问题根源我认为其实只是将处于同一个层次的两种目的的功能混合在一起了。

下面的问题是,为什么这两者容易混淆在一起?是不是可能MVC过于强势或死板呢?我个人很早注意到这个问题,所以,提出MVC模式已死,建议以DCI等架构,当然包括后来的DDD/CQRS来替代,这也是符合2012年Bob大叔提出的干净架构的目标。从今天观点来看,这样才能真正避免MVC模式和单元测试的冲突。

作为Ruby的MVC框架Rails创建者DHH之所以提出TDD已死,其中一个原因可能是他已经深刻感受到MVC与TDD的矛盾之处,这种矛盾如同对象和关系数据库,已经到了水火不容,不可调和地步,也许只有宣布一方死亡才可保持另外一方的存在。

其实随着后端RESTful架构和微服务的兴起,SpringMVC这种昔日MVC框架其实已经褪化成了REST框架,只有控制器,没有视图View,当然你可以将REST输出的JSON数据格式看成是视图View,不过这确实够牵强,JSON数据最多是视图的数据部分,如果你了解前端MVC框架AngularJS,可以明白,在AngularJS中,后端的JSON数据属于MVC的Model定义,Html的DOM才是真正视图。

在未来可能的情况下,Rails这种Ruby MVC框架也应该学习SpringMVC,顺潮流而下台阶,即使打着MVC框架名头,实际变成一个RESTful服务的控制器或处理器,该控制器已经不是协调Model和View之间的调节者(Mediator模式),而是处理前端POST/GET/DEL/PUT等动作的响应处理器Handler,在这里再实现对领域业务层的单元测试,恐怕就不会再引起别人的误会了。

从另外一个角度看,MVC模式主要是针对前端的,是向前优先的框架,而TDD单元测试是针对后端核心业务的测试,是向后优先的,就像两个人,一个人成天感慨过去(老年人),一个人经常憧憬未来(年轻人),两人在一起会有不匹配阻抗的啊。

所以,我提出“要么TDD死,要么后端MVC死”,其实只是为说明后端REST架构已经是主流现实,后端MVC框架其实已经发展到头了,MVC框架让出空间,才会有TDD单元测试生存的空间,当然如果你明白这个道理,两者共存也是可以,不过需要处理ORM框架那种超凡技巧和智慧。

本来架构很简单,搞的人多了,架构就复杂了,后端其实只要一个RESTful接口+ 业务,其他MVC框架 或ORM都是属于干净架构的次要部分,何必以次充主呢?

Bob大叔刚写了一篇“框架的边界”,谈到是使用框架要注意其边界,也就是说其有效范围,文中特别提到Rails, or Spring, or JSF, or Hibernate。他认为这些框架开始费尽口舌请君入瓮,一旦你将你的代码与框架绑定,你可能就被绑架了。

Bob大叔的意见是:将这些架构要置于你的架构设计之后。并不是不用它们,而是要纳入你的架构设计中,有条件的使用,而不是无条件的服从。

Bob大叔的道理是明白的,但是可能在急切的描述中口不择言,之前一个版本有性别歧视嫌疑,然后做了道歉,结果还被认为不真诚,甚至有人说,虽然你说的有道理,但是你这个不光彩的事情可能掩盖了你的道理。见:
https://gist.github.com/unclebob/2abcce451bafeab421f2

不管怎么说,再有道理的话在fans面前都可能被一点错误全部掩盖,理性有时会完全输给狂热的感性。

总之,Bob大叔的关于框架边界的观点我是赞同的,框架作为将无形的设计有形化,只要有形,就有所缺,因此框架是随着设计思想发展不断发展的,MVC框架或ORM框架在其历史上是有过简化作用,可以从当初Hibernate和Rails的狂热中可以看出,Jdon论坛也曾经上演过我一人独斗众fans的场面。

[该贴被banq于2014-05-12 10:21修改过]