多形态MVC式Web架构的分类

ioij1064 8年前
   <h2><strong>关键要点</strong></h2>    <ul>     <li> <p>MVC已成为每一代软件开发人员所最早接触到的软件开发原则之一。</p> </li>     <li> <p>MVC应被视为一种通用的架构原则和方法。</p> </li>     <li> <p>MVC三元组件的语义随架构环境的不同而变化。</p> </li>     <li> <p>可将基于HTTP的Web MVC(WMVC)分成三个不同的类别:sWMVC、dWMVC和pWMVC。</p> </li>     <li> <p>随着近期技术的进步,异步且实时观察变化的“事件循环”可用于WMVC应用的实现。</p> </li>    </ul>    <h2><strong>引言</strong></h2>    <p>MVC( 模型-视图-控制器 ,Model-View-Controller)最初用于设计和实现狭义的桌面图形用户界面(GUI)应用开发。经典的MVC与面向对象的程序开发方法一样,已成为每一代软件开发人员所最早接触到的软件开发原则之一。虽然MVC对当今的工业界有着如此重要的影响,但是在日益互联计算的时代,很明显MVC的内涵已 迷失了其精准性 。这在过去20年间对于WUI(Web图形界面,Web Graphical Interface)开发领域尤其是如此。基于以上原因,本文意在对MVC的起源做概要地阐述之后,进而深入地探讨基于Web的MVC的演进和变化。</p>    <p>在WUI的应用场景下,原始MVC及MVC三元组中成员对象的历史意义和内涵在不断地演变和转变形态。出于消除任何概念上的混淆的考虑,在本文的探讨中将使用“WMVC”一词表示基于Web的MVC式架构模式。大多数技术平台的WMVC特性正处于不断地改进之中,这包括Microsoft的 ASP.NET MVC 、PHP的 Symfony 、Python的 Django 、Ruby的 Merb 、Java的 JSR 371 等。驱动这些平台改进的原则,很大程度上在于JavaScript已可由客户端浏览器运行。此外,不少新的网络协议充实了服务器-客户间的通信。</p>    <p>自JavaScript被 XMLHttpRequest 赋予新生以来,它就成为WUI开发中情有独钟的技术。在过去的数年内,就有超过二十种 基于WMVC的JavaScript应用萌芽发展,其中包括 Dojo 、 Angular 、 Ember 、 Backbone 和 React 等。即使不是全部的也是绝大部分的框架都是侧重于客户端组件的交互,对于WMVC视图和控制器的组成对象而言尤其是如此。自然而言,WUI开发方法上的革新再一次引发了针对MVC亦或WMVC的大量讨论。这些讨论时常是十分激烈的,通常侧重于某个特定的方面,或基于某个特定的环境。进而使得MVC(WMVC)衍生出一些主要差异在于控制器对象的变体,其中包括 MVA 、 MVP 、 MVVM 、 Flux 、 Redux 和SAM等。这些MVC变体时常被统称为“MV*”,其中的“*”表示了各变体间的差异主要在于视图和控制器间的交互方式。通常并不将表示现实世界视图对象的模型组件整体地列入考虑中,或仅是在Web应用方案中将模型作为MVC三元组关系里的被动参与者。</p>    <p>自MVC概念于四十多年前被提出以来,MVC模型很有可能已经历了最重要的改变。在本文的探讨中,我们对MVC模型做了一个宽泛的定义,这个定义中涵盖了驻留内存的模型对象(例如 记录集对象 ),还有用于支撑对象的 SoR (主数据记录系统,System of Record)中的源数据、源文档、源文件和原始信号,以及所有把它们同步和聚集到一起的过程。模型数据仓库的存储形式已从小型 软盘 发展到 RDBMS 和 MMDBMS (多模数据库管理系统,multi-model database management system)。早期模型数据仓库是与驻留内存的模型对象共处一处的,这些模型对象是独立存在于各个用户桌面之上的。现在模型数据仓库使用宽带连接、分布式或基于云的系统,其部署可远离域对象。存储在这种外部环境中的数据,可以被企业生态系统的多个系统修改,也可以被消费者应用的数以千计的用户修改。这种使用场景上的根本差异,已经在根本上地改变了模型对象的行为,进而改变了模型对象与MVC三元组中的另两个成员间的通信和交互。</p>    <h2><strong>原型MVC</strong></h2>    <p>oMVC (原型MVC,Original MVC)是由挪威计算机科学家Trygve Reenskaug于1978年 提出 ,当时他工作于著名的 Xerox PARC 研究中心 Smalltalk 团队中。在oMVC的概念初步成型后,最初被实现为 Smalltalk-80类库 的一部分,用于桌面GUI的建立。</p>    <p>在那个时代,桌面应用远非当前这样是日常家居中的常见物品,每个应用需要独立运行于一台机器上,而每台机器的存储是有限的并且是各自独立的。正如在图1中所示,对oMVC模型对象的任何操作都完全由用户行为通过控制器所触发,MVC三元组在受控的环境中进行通信、同步并保持状态。控制器的主要作用是维持用户和系统之间的联系(参见图1)。控制器实现对相关GUI组件的部署,并在屏幕上将这些GUI组件展现给用户。在 Win95出现之前 的年代中,这是一个具有挑战性的任务,因为当时 MS-DOS 依然是占据主要地位的操作系统。在oMVC模型中,一旦用户产生了某种动作,例如菜单选取、在输入框中输入、按钮点击等,控制器就会转化这些动作为对应的改变消息,向模型传递这些消息,并对消息进行处理。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/72b7cbd6fd503fae99a43edeef7e26f8.jpg"></p>    <p>图1展示了独立桌面环境中的oMVC。图中显示了作为模型的组成部分的本地软盘数据存储。oMVC中控制器和视图具有成对出现的关系,但是模型的改变并不在两者间直接产生通信。控制器和视图知道模型的状态,但是反之并非如此。</p>    <p>一旦模型从控制器获取了变更通知,它并不直接通过调用去更新所涉及的视图。在模型-视图的关系中,变更通知的获取是通过每个独立的参与者之间的相互注册实现。一旦一方发生了变更,就会生成一个事件,对方就会采取相应的动作作为响应。在图1中,视图是附属于模型的,对模型进行观察。一旦控制器触发了模型变更事件,视图必须去确保自身显示也按需更新,以相应地反映出模型的状态。更新模型的通知信号也可由视图自身生成。这种视图与模型之间的 观察 (或订阅)和通知关系,有助于模型和视图间的解耦,使得多个视图可隶属于同一模型,进而可提供不同的表示。</p>    <p>在图1中值得注意的是,控制器并非直接地改变视图。在Smalltalk社区及其它后续的桌面GUI应用库中,通常将视图和控制器看成一对耦合的对象。视图使用具有特定控制器类型的实例实现预期的响应。为创建所期望的行为,控制器还可以在不同情景中 策略性地 、动态地切换类型。这样的视图-控制器对可以嵌套于 复合 层次结构中(如图2A所示)。在该层次结构中的父辈之间、子女之间及父子之间,视图-控制器组件都可以进行交互和通信。很多情况下,层次结构中的每个独立视图-控制器的子女组件仅处理部分的模型对象。此外,从oMVC组合的角度看,模型也可以使用层次结构的形式进行组织,这样 MVC三元组作为一个整体 构成 层次结构中的父子关系 ,如图2B所示。</p>    <p>(点击放大图像)</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/dfd8d07fbd53cd6a52c1b70dc4790e42.jpg"></p>    <p>图2左图是oMVC的复合表示,右图是 PAC (显示-抽象-控制,Presentation-Abstraction-Control)的表示。</p>    <p>总而言之,oMVC设计范例是由一系列的 GoF设计模式 所组成,尤其是其中包括了 观察者模式 、 策略模式 和 复合模式 。在早期的桌面应用库中,oMVC域架构和模式的设计意图得以保持,即模型组件由应用域对象和数据存储所组成,其中存储多是本地的或是受限的。在oMVC三元组类的行为中,应用域行为的维护和广播起着核心的作用。oMVC的一个关键假设是模型的稳定性,该假设在上世纪七十和八十年代的桌面应用情景中显然是正确的。但是在WMVC领域,模型时常发生改变通常是一种常态。直至近些年,向用户实时广播变更(类似于oMVC所实现的)所需的技术基础才得以实现。</p>    <h2><strong>WMVC的分类</strong></h2>    <p>具有讽刺意味的是,虽然直到上世纪九十年代,尤其是在Win95出现之后的年代,桌面计算机开始进入普通百姓家并得到普及,但是传统的桌面应用却因为因特网互联的Web应用开始统治了业界而逐渐退居幕后。不同于安装并于运行于终端用户计算机上的桌面应用,Web应用是宿主于远离用户的服务器上,创建了客户-服务器的关系。在本文的其后内容中,我们将浏览器(browser)和客户(client)这两个概念互换使用。根据浏览器和服务器相对于WMVC三元组对象的部署位置和执行方式的不同,可将WMVC明确地分组为:</p>    <ol>     <li> <p>服务器端WMVC (Server-side WMVC,sWMVC):所有WMVC的组件位于服务器上,并在服务器上执行。</p> </li>     <li> <p>双重WMVC(Dual WMVC,dWMVC):WMVC组件分布于浏览器和服务器之间。通信可由客户或者服务器端发起。</p> </li>     <li> <p>点对点WMVC(Peer-to-Peer WMVC,pWMVC):这种架构中没有集中式服务器。所有WMVC组件位于客户端,在客户端执行。pWMVC可具有自己的沙箱SoR。</p> </li>    </ol>    <p>服务器端WMV(sWMVC)</p>    <p>在sWMVC模型中,用户使用浏览器作为瘦客户,通过无状态的请求-响应HTTP协议访问应用(如图3所示)。客户向服务器发送HTTP请求或输入内容,接收并显示整个更新的Web页面(或是其它的文档)。一旦页面被加载以后,各页面组件间就很少有交互了,页面成为静止的。</p>    <p>在这种瘦客户-服务器范式的sWMVC架构中,应用SoR版本库外部化为一种集中式环境。它与应用服务器内存中的域对象是相互分离的。服务器和数据版本库都是远离用户浏览器部署的(如图3所示)。SoR存储常常是由一个或多个关系数据库这样的数据源所组成。鉴于数据已经外部化了,带外进程或不同的用户都可对数据进行更新。数据的变更只会从控制器流向模型(参见图3);当SoR中数据被不同的用户或系统改变时,并不向应用服务器发送入站数据变更通知。不同于oMVC,模型及其所关联的视图间不再有任何的直接联系和必要的同步。由于视图不再反映模型的状态,这就需要用户手动地发起新的HTTP请求去同步和刷新视图。因此sWMC中的“s”,也可指代这种WMVC范例的静态(static)或是陈旧(stale)的本质特性。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e3510ab14fff173b7bf2280d1ceeb0a7.jpg"></p>    <p>图3sWMVC范例的一种表示。图中显示了由服务器驻留内存的域对象模型(M)外部化而得到的SoR。不同于oMVC,控制器不再与用户交互,而是用于协调模型与视图间的通信。模型数据的变更是单向流动到外部数据存储中的。</p>    <p>WMVC的模型可简述为一种分层架构(如图4所示)。架构的最顶层模型表示了在视图及其相关的控制器这个关系对之间的契约关系。为满足该契约,在处理栈中可包含数个可变的业务逻辑和数据访问组件。该契约用于所有与视图相关信息的连接,其中的信息可能直接来自本地SoR,或是数据云,或是其它真实世界中的数据源,例如探测器和数据提供系统。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/05d5e39808a6ef69475f262ad38f793d.jpg"></p>    <p>图4WMVC模型的示意图。图中显示WMVC的组成包括了多个架构层次,以及来自本地或远程可访问环境中的可变数据源。</p>    <p>相对于在oWMVC中的意义和内容(如图3所示)而言,三元组对象间的关系和通信发生了根本上的转变。与控制器在用户和oMVC系统(图1)间所起的作用不同,sWMVC的控制器承担了在更高层级上协调视图和模型的角色(参加图3)。视图和模型间的通信通过控制器发生。用户与视图进行交互,将模型与模型视图关联的行为逻辑包含在控制器中,由控制器负责管理输入、更新模型并产生适当的输出。</p>    <p>Web应用的架构环境与桌面应用的有所不同,对于 将MVC引入 到 早期Web应用架构 的设计中,虽然这在本质上忽视了MVC的原生结构。但是这种引入反而接收了MVC模型的基本理念,即将这三个相互作用的、多变责任的对象类进行相互分离是十分重要的。该做法将oMVC范例提升为UI设计中在更加通用层次上的架构原则,增加了对任意类型UI应用的灵活性和可维护性。</p>    <p>sWMVC中通常包含有多种设计模型,其中也包括在oMVC中所使用的设计原则。例如,各个用例的 控制器 原则和行为可能都是不同的,包括 中介者模式 、 调度者模式 、使用 状态模式 和 模板方法模式 的 策略模式代理 等。但是在oMVC中,观察者模式在触发三个对象类型间的通信中起着关键的作用,尤其是当该模式与视图做相关的更新时。在sWMVC实现中,观察者的作用被缩减了。这样导致了sWMVC的静态和陈腐的特征。直至近些年,技术的进展才使得交互式和富Web用户体验成为可能。</p>    <p>双重WMVC(dWMVC)</p>    <p>在Web时代的早期,由于浏览器的普遍使用,为使现有的桌面交互应用支持Web做出了不懈的努力。这些工作的一个成果体现为 Microsoft的Outlook Web项目所研发的 一种基于Web的新组件,该组件允许客户端脚本发布异步HTTP请求到服务器。该工作引导了 XMLHttpRequest (XHR)协议的建立和标准化。该协议是至上世纪末为止最具有革命性的工作。XMLHttpRequest随后成为 Ajax 技术的基本原则。</p>    <p>当前,基于这种技术的架构使得dMWVC的视图-控制器对可以动态分布,并可安装到用户桌面、移动设备或其它设备的浏览器上。浏览器组件与服务器上模型间的异步通信,用于同步给定的或者所有的视图元素的dWMVC三元对象组状态,进而提供给用户对现实世界或模型对象的最新认知。这种技术的引入使得XHR切实地丰富了客户应用,为浏览器用户带来了交互式的用户体验。</p>    <p>XHR技术还引发了 单页应用 (SPA)开发技术的发展。类似于oMVC应用,SPA是驻留在单一页面上的Web网站,提供了无缝的浏览体验。对于用户而言,SPA看上去在从一个页面到另一个页面时并没有任何页面重载,对不同页面渲染所需的资源是在所需时由幕后的服务器动态且异步加载的。SPA和这种dWMVC应用的交互行为的组合使此类范式划分为了 富互联网应用 (RIA)的最新条目。</p>    <p>就XHR协议自身而言,从客户端的视图-控制器对象到服务器端模型间的通信依然仅是单向的通信,需要由视图发起某种类型的 轮询机制 去探测模型上的更改。轮询是一种资源密集型操作,因而成为影响性能的一个关注点。类似于图1所示的oMVC,在理想情况下模型中的任何更改应会被实时发布到、或是广播到所有相关的视图-控制器组件中。图5显示了dWMVC架构的视图和模型间的双向异步同步(通过控制器)。该机制提供了完全动态的状态同步,这种同步基于dWMVC组件间的订阅-发布机制。一些近期的技术发展,其中包括 服务器发送事件 (Server-Sent Events,SSE)、 WebSocket 和入站数据库通知技术等,使得该同步操作成为可能。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/c4644a032a127aa4d5216615d7d6e348.jpg"></p>    <p>图5dWMVC的示意图。图中显示了三元组对象在客户和服务器间的分布。由SoR发起的入站更改通知导致了模型和视图间的实时双向更新。</p>    <p>SSE机制作为 HTML5 的组成部分,允许服务器端组件异步启动,并将数据从服务器端组件实时推送到浏览器。客户端组件可以使用SSE发起请求,向服务器申请建立一个非传统的HTTP连接。一旦客户端组件接收到所发起的请求,它继续对随后的服务器响应进行监听。与此同时,服务器保持同一客户-服务器初始连接是活跃的。一旦新的数据可用,服务器无需客户的额外请求,就立刻通过该初始连接将这些数据推送给客户。这样,SEE给出了一种从服务器到客户浏览器的解决方案,该解决方法使用了异步的发布-订阅事件通知。</p>    <p>WebSocket是一种提供浏览器和服务器间全双工连接的通信协议。当客户发送初始请求到服务器时,WebSocket通知服务器该HTTP连接可能会升级为全双工的TCP/IP WebSocket连接,通知使用了一种特殊的HTTP报头。一旦WebSocket连接被建立,就可在需要时用于在浏览器和服务器间相互发送数据。</p>    <p>使用SSE和WebSocket通信协议,服务器可以异步地将模型驻留内存的更改发布到浏览器。但是正如前面所讨论过的,模型架构的分层可能会跨越服务器的边界,并可能包括应用服务器之外的外部SoR(参见图4)。由于SoR中的数据记录可被其他应用或用户修改,因此每当这样的带外更改发生时,SoR应具有 变化数据捕获 (change data capture,CDC)机制去探测并捕获更改,实时地发起并推送入站数据更改到应用服务器,实现端到端的双向发布-订阅通信模型(如图5和图6所示)。在图6中,dWMVC轮毂的中心表示了共享的SoR和CDC数据源。数据的实时同步由数据源和相关驻留内存域模型对象之间的双向交互所维持。在图6中,每个轮辐间的微型dWMV表示了一个独立的业务应用(集成的企业生态系统中),或是一个独立的应用用户。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/58d67605dd7437e8d970432aed00497e.jpg"></p>    <p>图6dWMVC轮图。图中显示了由SoR发起的入站更改通知,并显示所引发的模型和所有视图间的实时双向更新情况(以微型WMVC展示)。</p>    <p>基于Web的架构栈中加入了XHR-Ajax、SSE和WebSocket等技术,还有数据库厂商提供了数据库入站通信能力,所有这些一起使得传统oMVC中视图和模型间的双向交互通信在dWMVC中得到了复兴。越来越多的数据库厂商,包括 PostgreSQL 、 Oracle 等传统关系数据库厂商和 RethinkDB 、 Cassandra 等 NoSQL 数据库厂商,已经实现或者规划去提供CDC机制,以改进应用服务器的入站推送通知。</p>    <p>点对点WMVC(pWMVC)</p>    <p>对于上面所论及的两种WMVC,它们的架构语义都是基于 客户-服务器范例 的,即用户浏览器向服务器发送HTTP请求,取回服务器端的内容,服务器则回应以包含请求信息的一个或更多的响应。这类方法中,服务器负责存储,并发送所有内容及对所有请求的响应。但是这样的集中式方法可能会导致性能上的瓶颈。因为为了支持所有可能的请求负载,需要对服务器基础设施的资源进行适当地扩展和复制。当前由于高频度实时内容发布需求在数量上日益增长,这类方法会产生问题,尤其是在(意料之外的)高通量负载的期间。最优的系统无疑是那种能以去中心化的方式支持高质量终端用户体验的系统,这样的系统可以使用户用最短的路由和时间获取到数据。借助于 点对点 (Peer-to-peer,P2P)数据交换及通信,终端用户可以从其它的用户那里交互、检索和接收内容。无疑,这样的架构绕开或降低了集中式服务器的负载和潜在瓶颈问题。</p>    <p>传统的点对点系统需要用户显式地安装专用的桌面应用或插件。在 WebRTC (Web实时通信,Web Real-Time Communication)协议被标准化并被浏览器支持之前,浏览器本身并不具备使对等系统的工作直接相互通信的能力。现在WebRTC标准已被大多数的浏览器所支持。它是一种API定义,提供了无服务器的浏览器到浏览器(浏览器P2P)直接数据交换和通信范例。WebRTC对Web应用引入了点到点的解决方案,它允许Web浏览器打开到其它浏览器的直接通信通道,无需对每个Web请求-响应的集中式服务场景进行处理(参见图7和图8)。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/caaccd62feb4bcd7cd9eae31c575c9fc.jpg"></p>    <p>图7pWMVC如图所示,图中显示了位于客户端(浏览器)内、或由客户端所控制的所有三元组组件。</p>    <p>在pWMVC模型中,所有的三元组组件位于并执行于客户端及相关终端用户的沙箱中(如图7所示)。沙箱中包括用户本地或基于云的SoR存储,这样的存储可以被pWMVC应用访问。在图8中,轮辐间的每个微型pWMVC都代表了一种独立用户浏览器环境。与图6相比,为便于持续进行的Web通信,pWMVC轮图中并未涉及集中式服务器和SoR架构。互联浏览器间的微型pWMVC组件状态可使用WebRTC通信协议保持同步。</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/057a1575010dece28a38af35aa561356.jpg"></p>    <p>图8pWMVC轮图。图中显示了由独立用户浏览器所发起的直接无服务器P2P变更通知,以及该通知如何导致其它的对等端视图进行实时更新。图中“M”指代内存中模型对象和用户SoR。</p>    <h2><strong>总结</strong></h2>    <p>MVC本身应该被视为是一种无需考虑任何语义的设计原则或方法论。MVC的简要内涵在于,任何类型的UI应用都可被分解为三套相互作用的对象类。在MVC所应用的场景中,应该审查该三元对象类型组的行为。为更好地理解MVC概念,在对架构和特定域程序库进行实际设计和实现应用时,应使用适当的命名注释。</p>    <p>WMVC可看成一种独特的原理图,用于在无状态HTTP域的场景中基于MVC方法论的开发。WMVC可区分sWMVC、dWMVC和pWMVC这三种不同的类别。这些类别在机制上不同于oMVC,即原型MVC。考虑到标准化的网络协议、由特定数据库技术所提供的专用入站通信等这样的最新技术发展,基于观察更改的MVC式“事件循环”可以满足基于Web应用的需求。这使得WMVC当前可为浏览器用户实现具有完全交互的实时丰富WUI体验。</p>    <p> </p>    <p> </p>    <p>来自:http://www.infoq.com/cn/articles/mvc-web-architecture-polymorphism</p>    <p> </p>