首页
浅谈MVC的误区以及与三层架构的区别

MVC 作为 web 研发的一种常见架构模式,几乎无人不知。但是很多程序员对 MVC 的理解似乎都有些偏差,特别是在一个复杂系统的设计中。最常见的就是在单体架构中,把 MVC 当成三层架构(3-Tier Architecture)来用。虽然这种错用,不会导致系统出现问题,然而系统内部的代码组织方式确是很不科学的。长此以往,随着复杂的业务和代码的堆积,很可能让系统变成一个大泥球

MVC架构

基本思想

MVC 的概念首先由 Trygve Reenskaug 引入,他提出将其作为开发桌面应用程序 GUI 的一种方式。今天,MVC 模式用于现代 Web 应用程序,因为它允许应用程序具有可扩展性、可维护性和易于扩展性。

MVC 把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

  • 模型(Model) - 用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。
  • 视图(View) - 能够实现数据有目的的显示(理论上,这不是必需的)
  • 控制器(Controller) - 起到不同层面间的组织作用,用于控制应用程序的流程。

MVC 模式强调的是模型、视图和控制器的分离。这种关注点分离(SoC)的思想可以让我们更轻松地管理和更改任何一方,而不会相互干扰。

常见误区

现代的很多 web 框架都实现了 MVC 的模式,比如:Spring、Ruby on Rails、Laravel、Phoneix,甚至一些前端的框架像 Ember、Backbone等等。虽然框架都包含这个功能,但是却没有告诉开发者如何合理地使用。所以在实际的场景中,会存在很多对于 MVC 的误区。像 MartinFowler 在 Model View Controller 这篇文章就提到 MVC 是被周围引用最多(也是最错误引用)的模式之一。

  • 首先存在的一个误区就是把 MVC 当成三层架构来用,这个在文章一开始就讲到。
  • 对于模型这个概念理解不是很透彻,认为模型就是与数据库打交道的类。
  • 同样,对于控制器的作用也不是很了解,甚至会出现在一个控制器里面调用另一个控制器的写法。

以上这些误区对于大型的应用来说,都是很致命的。在分析为什么之前,我们先了解一下三层架构。

三层架构

img

三层架构(3-Tier Architecture)是一种典型的架构模式(Architectural pattern)。它是一种完善的软件应用架构,它将应用程序组织成三个逻辑和物理计算层:表示层或用户界面;处理数据的应用层;以及数据层,其中存储和管理与应用程序相关的数据。三层架构的主要好处是,因为每一层都在自己的基础设施上运行,每一层都可以由单独的开发团队同时开发,并且可以根据需要更新或扩展,而不会影响其他层。

在 web 应用中,通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的目的即为了“高内聚,低耦合”的思想。

  • 表现层(UI)- 通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。
  • 业务逻辑层(BLL)- 针对具体问题的操作,数据业务逻辑处理。是表示层和数据访问层之间的桥梁。在DDD的概念中,也叫领域逻辑层(Domain Layer)
  • 数据层(DAL) - 数据层包括数据持久化机制(数据库服务器、文件共享等)和封装持久化机制并公开数据的数据访问层。

实际使用中,可以会根据系统的复杂度变成N层架构,比如阿里巴巴Java手册里的分层模型。

二者区别

虽然它们都是三层的,好像差不多,但是如果深入了解本质会发现它们是完全不同的东西,关注的点也完全不一样,甚至都不能在一起进行比较。首先,MVC 它是一种表示层模式(Presentation Patterns),跟它一起对比的应该是 MVP、MVVM 这些。整个 MVC 可以存在于三层架构的表示层上,Model 充当的是一个视图模型的角色。三层架构是一种架构模式(Architecture Pattern),它站在一个更宏观的角度来指导系统设计。像UI、BLL、DAL甚至可以在物理层面进行隔离。

三层架构中唯一与 MVC 相交的部分是“业务层”。MVC 中的“模型”和三层架构中的“业务层”都在努力实现相同的目标,所以随着 MVC 的发展,模型变得越来越模糊,这也是很多开发者不太理解的地方。

比如,我们项目之前用的 codeigniter 框架,它把模型解释为跟数据库打交道的PHP类。这样就会导致一个问题,就是最开始提到的,开发人员把 MVC 当成三层架构来用。最终系统的代码结构就是View作为界面,Controller作为业务逻辑,Model作为数据库访问。这样做既不是 MVC 也不是三层架构。当业务发展到一定程度,系统中会有很多通用的业务逻辑,结果变成前面提到的在一个控制器中实例化另一个控制器,并调用里面的方法这种很奇葩的写法。如果碰到了需要进行事务处理的逻辑,因为框架的事务开启是在Model里面的,又把一部分业务逻辑写到Model里。这种四不像的做法最终会导致业务逻辑分散在控制器和模型中,通用的业务逻辑也没法进行复用。

所以,一些做的比较好的 MVC 框架对待模型的描述都是很谨慎的。比如 Laravel 框架在它的文件夹结构中故意没有设置Models目录。它的解释如下:

image-20211214170358343

Phoenix框架把整个应用分为web目录和业务域目录。web目录包含控制器和视图,业务域目录则是系统的业务模型。这种做法,直接在框架层面让开发者避免了 MVC 和三层架构混淆的问题。

1D7DC538-0A25-47A5-AC53-942107142C49

关于模型

虽然 MVC 中的“模型”变得模糊,但基本上也是以下的三种。

  1. 领域模型: 这些代表“业务”关心的“事物”——业务领域。这些类保存数据以及对这些数据进行操作的所有过程,以执行业务规则。域模型通常与数据库中的表相关联。这似乎适合三层架构的“业务层”。
  2. 视图模型: 这些类用于将域模型中的数据转换为更适合视图的内容。这不适用于三层层架构中的任何地方,因为视图模型不实现业务逻辑,也不提供任何类型的服务或数据访问。
  3. 业务模型: 在复杂的应用程序中,需要将领域模型与业务逻辑分离。业务模型包含数据和操作该数据以实现业务规则的过程,而领域模型被归为“属性包”——只保存数据但不包含任何行为的对象。领域模型成为数据库和应用程序之间数据传输对象的另一种形式。

martinfowler 的两篇文章中也提到了关于 MVC 中的模型的理解。

Whenever we use terms from MVC there's inevitably the question of what is the model. In classic Smalltalk MVC the model was a Domain Model. In general the model in an MVC discussion these days means the interface to the domain layer; which could be a classic domain model, or a Service Layer, Transaction Scripts, Table Modules or any other representation of the domain. Indeed if there is no separate domain layer the model could well be the interface to a database.
每当我们使用 MVC 中的术语时,不可避免地会出现模型是什么的问题。 在经典的 Smalltalk MVC 中,模型是域模型。一般来说,现在 MVC 讨论中的模型意味着域层的接口; 它可以是经典的域模型,也可以是服务层、事务脚本、表模块或域的任何其他表示。 事实上,如果没有单独的领域层,模型很可能是数据库的接口。


参考:

  1. 10 Common Software Architectural Patterns in a nutshell
  2. Multitier architecture
  3. Presentation Patterns : MVC, MVP, PM, MVVM
  4. Architecture Presentation Patterns: MVC vs MVP vs MVVM
  5. Three Tier Architecture vs MVC Architecture
  6. 深入理解MVC