From MVC to Modern Web Frameworks

From MVC to Modern Web Frameworks

Model-View-Controller (MVC) is one of the most widespread and influential patterns in software architecture. Despite rumors of MVC’s death, it remains a useful pattern to understand. In this post, rather than reining in the various definitions of MVC, I will highlight how it evolved into the modern web frameworks we know and love today.

Inception

In 1978, Trygve Reenskaugh and Adele Goldberg coined the name “Model-View-Controller”. Trygve believed MVC and its variants formed a Pattern Language, a shared language for people to talk about problems and their solutions. The concept of a software Pattern Language also influenced the Gang of Four to write the Design Patterns book. If MVC were included in the Design Patterns book, we would probably find it under the Behavioral Pattern section, hanging out with patterns like Mediator and Observer.

Here’s Trygve’s retrospective paper in 2003, MVC, Its Past and Present. Trygve enumerates eleven patterns that form the MVC Pattern Language. Only two have stuck around in a significant way: Model/Editor Separation and Input/Output Separation, and I argue that these are essentially the same:

Variants where the input goes through the View are also common.
“The View and Controller roles may be played by the same object when they are very tightly coupled” — Trygve Reenskaugh (a point which has been endlessly debated in the community)

Native MVC

In the decades that followed, MVC outgrew Smalltalk and powered applications for Mac OS and Windows as those systems grew and evolved rapidly. Later, when smartphones began to appear, native mobile apps for Windows Mobile and early versions of iOS also relied on MVC and its variants. At Microsoft, MVC later evolved into MVVM, which powers projects like WPF, Silverlight, and Xamarin.

Web MVC

In the early 2000’s, several important web frameworks adopted the Pattern Language of MVC: Spring, Ruby on Rails, PHP, and ASP.net. These frameworks added a new responsibility for the Controller: handling the initial HTTP request. It went something like this:

Controllers respond to HTTP requests

The Controller is now the entrypoint into the application, rather than the View. The responsibility of the View also changed: instead of presenting something to the user directly and handling input, its job was to assemble a bundle of HTML, JS, and CSS for the browser to render. The HTML/JS would contain logic like button click handlers that would dispatch an action back to the controller via an XMLHttpRequest. Notice that there is no significant presence of the MVC pattern within the browser. That would soon change with the advent of Modern Web Frameworks.

Modern Web Frameworks

As the browser wars settled, we finally got to have nice things: XMLHttpRequest, stable DOM API, ES6, etc. With this increased power and flexibility, companies began building more and more complicated web apps (sometimes called Single Page Apps- SPA), as opposed to related sets of simple web pages. Modern Web Frameworks help organize this increasing client-side complexity and keep application development predictable and productive.

Often, these frameworks introduced an extra build step to create static bundles of HTML, JS, and CSS, for direct hosting via a simple “View Controller” (usually a handler at / or /index.html). The static nature of these resources means we can set cache-control headers and rely on CDNs to help serve them with lower latency. These Single Page Apps include logic (in JS) for making HTTP API requests against a set of resources served by “API Controllers”, which usually respond with JSON:

That brings us to today: React, Vue , and Angular are the most popular Modern Web Frameworks. How similar are these frameworks’ organizing patterns to MVC? All have some sort of “View”, so any comparison will need to be done at the next layer: state (Model), mediating logic (Controller), and synchronization.

I’m about to describe these frameworks by the most common ways to use them. I’m sure someone somewhere insists on pairing Data-Binding with React, Flux with Angular, and french fries with peanut butter.

Vue is the most straight-forward: its docs clearly state that Vue is an implementation of MVVM. Angular is also MVVM-ish by default. However, React was at Facebook, using a pattern they call "Flux":

Chris Hemsworth creating React at Facebook
Actual Flux diagram; all the arrows on the right-side point clockwise.

Flux is all about one-way data flow. Recall that the Model in MVC represents the persisted data that will be rendered by the View. Flux splits the responsibilities of the MVC Model; it uses Actions/APIs for business logic, and the “Store” for handling state. You can think of the Store as one monolithic Passive Model for the entire app.

Why does Flux embrace one-way data flow? The reasoning goes like this: as an application grows in complexity, it becomes increasingly difficult to manage state changes with view updates, especially if those changes are coming from different sources. As opposed to Data-Binding, where the View observes a mutable instance of the ViewModel for property changes, React creates a new View as a function of immutable State/props. The View never needs to worry about local state changes. The app can only change by creating a new Model instance in the state tree. When React wants to update the app, it replaces a part of its state tree with a new object, which triggers the creation of new View(s).

While one-way data flow is a powerful concept, it is not a free lunch box filled with silver bullets, nor does it elevate React/Flux onto a different power level versus Angular/Vue.

The MVC tree of life

I hope this has been useful. If not useful, then interesting. If not interesting… well that’s in the past and the past is immutable; perhaps you should replace yourself with a new functionally generated immutable instance that makes better decisions about which articles to read =).