The Real MVC in Web Apps

I recently stumbled across Peter Michaux‘s article MVC Architecture for JavaScript Applications. I’ve followed Peter’s writing in the past and was surprised I’d missed that and several others you can find on his site, including the more recent Smalltalk MVC Translated to JavaScript. (While you don’t have to go read them now, I recommend you do so.) Peter focuses his attention on JavaScript applications. I’m going to extend this a bit and tie in a bunch of other things I’ve been reading to show that 1) the current trend appears to be heading back in this direction (albeit under different names) and 2) that MVC need not focus entirely on the client side.

First, though, I should establish that I think the use of “MVC” to describe server-side frameworks is a complete misnomer. (I’ve written about this previously.) The “MVC” claimed by Rails, ASP.NET MVC, and others is based on Model 2. While visually related, the reality is that they have very little in common since “Views” are mere templates and not the component object originally used in Smalltalk’s MVC (see Peter’s articles above).

Background

About a year ago, I was building SPAs with AngularJS when I came across this post from David Nolen: The Future of JavaScript MVCs. I’d heard and enjoyed David talking about Clojure at LambdaJam earlier in the year, so I paid attention. At that time, I’d not yet heard of React, but I quickly started looking into it. React claimed to offer a paradigm that more closely matched functional programming; an attractive feature for an F# developer such as myself.

At that time, I still hadn’t really thought through exactly why I was building apps the way I was building them. Clients liked the perceived responsiveness in SPAs, and that was sufficient reason, despite some of the complexity involved. Also, AngularJS had, to that point, been a perfect fit for many of the forms-over-data style apps I was writing, and though I was uncertain about mixing binding logic into my markup, I’d started accepting it as the way forward.

When Facebook announced Flux, I was a bit more excited, as I liked the single-direction flow, though I thought some of the pieces in their pattern were a bit much. Around that time, I also found mithril, which looked like a nice mix of AngularJS and React with potentially better performance and a smaller footprint. Nevertheless, I stayed the course with AngularJS, as I needed a really good reason to switch frameworks.

A few months later, we picked up Kendo UI in order to use some of its components in our app. At that point, and combined with some limitations I ran into with Angular, as well as its learning curve preventing other team members from quickly contributing to our front-end app, we made a decision to start moving away from Angular. We have not made a furious effort to do so, and we have not selected a new framework. We may not.

As I was investigating potential target frameworks, I stumbled across the Futurice article Reactive MVC and the Virtual DOM and shortly thereafter was told about Reflux. While almost entirely the same thing, the former espoused that it was roughly equivalent to MVC, while the latter claimed to “eschew” MVC. Which was true?

I then stumbled upon Peter’s articles, as noted above. Indeed, both Reflux and the Futurice MVI approaches are rough equivalents of the classic Smalltalk MVC with the additional constraints of each component communicating only in one direction. In the Futurice case, MVI also makes all pieces observable, not just the model and simplifies the Controller to a UI event to domain event mapper, more or less.

Model-View-Controller and OOP

Hold on a second. Isn’t MVC an OOP pattern? Wasn’t React + Flux supposed to offer a functional paradigm? Isn’t Om, David Nolen’s ClojureScript wrapper of React, supposed to show that even more?

Well, here we have to define OOP. Smash Company recently posted an article about OOP that tries to deconstruct the definition and shows that OOP is a term that can mean many different things. I’m not trying to be pedantic, but I also know we will not be able to move forward without a clear definition. For the purposes of this post, we will adopt Alan Kay’s definition:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP.

If we focus on OOP as a pattern focused on components communicating via message passing and allow that MVC is an OOP pattern, then indeed, Reflux and MVI are both really just slight variations of MVC. In particular, the use of RxJS in the MVI approach makes this most clear.

Cool. So roughly 30 years after MVC was a good idea for building UIs, we find we agree. This reminds me of Bret Victor’s “Future of Programming” talk:

Is that it, then? Pick a “real” MVC framework or set of libraries and rewrite everything? If you want a “real” MVC framework, do you need to pick one of the above? What about Peter’s Maria, which resulted from his work on trying to uncover the “real” MVC in JavaScript?

First of all, you are asking two different things. AngularJS and Ember both offer models that can fit this approach okay, and both can work with React (last I checked, though I’ve never used Ember and am not certain as to this claim). Alas, it’s just not that simple.

Components

I neglected to mention that while looking through all these options, I ran into a little problem: most approaches assert that some specific component library is the “right” one to use. In particular, the virtual DOM approaches appear to be popular. However, where does that leave Web Components, component toolkits like Kendo UI, or DOM manipulation libraries like D3.js? How can you mix these into your virtual DOM library? For that matter, is a virtual DOM really necessary? I found Twitter’s Flight after beginning to prototype a similar idea and then finding a link to Flight in an article about CSP in JavaScript.

Right. Well, that certainly complicates things. Must you choose? What if you find some components in various libraries that would really work well for you? Must you really give up the ones not in your selected library? While you might appreciate using a consistent library or approach, I would argue you do not have to give up this flexibility.

OOP Revisited

So many people think OOP is directly at odds with FP. This is simply false. F#’s MailboxProcessor is an example of an agent, or actor-like, construct that maintains its own internal state and responds to messages. It may send its own messages or emit events to allow others to respond to its own processing. Doesn’t that sound familiar? If not, scroll back up to Alan Kay’s description of OOP.

In short, OOP is a strategy for designing in the large. This works exceedingly well with FP in the middle / small. Further, this sort of encapsulation means that you can create View abstractions around any and all of the above component libraries without worrying about them working directly with one another. Want Kendo UI, React, and Flight components to render side by side? No problem. You merely need a common abstraction or protocol through which they can communicate back to a common dispatcher and model. RxJS, as used in the MVI example above, is a good example of a library that may be useful. You can also use Maria or a handful of other libraries.

Fortunately, I’m not the only one thinking about things this way, at least in the sense of framework lock-in versus libraries. Andy Walpole just posted his thoughts in 2015: The End of the Monolithic JavaScript Framework. He does a great job showing the rise and fall in appreciation for monolithic frameworks. Chris Love has long been advocating for micro-libraries over frameworks. These don’t progress necessarily in the direction of MVC, and I don’t think I’ve yet covered why MVC could really be beneficial….

Distributed, Reactive MVC

If I recall correctly, Peter notes that the model is not the thing retrieved from a server request. I agree with the literal meaning of this statement, though I think he probably meant a bit more. (Apologies if I interpreted your meaning incorrectly, Peter.) Until I started writing this article, I was in full agreement with Peter’s assertion. You see, in classic, Smalltalk MVC, you had only a client. Actually, that’s not quite right. You had a world, an environment. The whole thing is available to you interactively. In such a programming environment, anything can be a Model, and if you read Peter’s article linked above, you would have caught that this was baked into Smalltalk intentionally. It’s reasonable to conclude then that the Model was the encapsulation of the domain.

We’re not talking about Smalltalk anymore, but the world of browser-based applications. Very little data is available within a browser-based application unless all the data is passed along with the markup. I’ve built apps that way in the past, and I bet you have done so, as well. However, we have learned new tricks that allow us to lazily retrieve data when and as necessary using tools like XMLHttpRequest. We have introduced distribution.

Distribution

If you watched Bret Victor’s video, you may remember seeing mention of the Actor model. When you think of the Actor model, what’s the first programming language you think of? I hope you said Erlang, mostly because of its history. I cannot remember where I first heard the story, but Joe Armstrong said that they created Erlang based on the ideas of Smalltalk while waiting on their first Smalltalk machine to arrive. In the end, they liked what they had built in the interim and never used the Smalltalk machine. (If anyone can find a reference to this, I’ll add it here.)

Here we can make a connection between Erlang and Smalltalk, between the Actor model and OOP. We can therefore confidently assert that OOP can involve distribution in its model (again following the definition from Kay above). Since MVC is an OOP pattern, we can further assert that MVC allows for distribution.

This raises a new question: even if all the players in MVC can be distributed, which should be distributed? This is a tough one. Let’s think together about how we build UIs. Using the desktop metaphor I raised in my previous post, we see that we could place multiple Views side-by-side within a single window. Following the Windows Store example, we could make the case for each View running in its own window. Also, depending on your definition of View, you may define something as small as a TextBox as a View, as it certainly qualifies as a component. You may therefore have any number of combinations. In most scenarios, though, I think we can safely assume that these will always exist on the client.

Let’s move on to Controllers. We could probably put these anywhere, but if we want to stick with a minimal definition like that used in the MVI pattern described in the Futurice article, I would argue that Controllers would best live close to their observed Views. You can certainly publish raw UI events across a network connection to be translated elsewhere, but this will mean you are primarily using RPC and may have to pass a lot of UI related context along with the event. I do like the idea of Controller as Intent, and I don’t like RPC much (more on this in just a bit), so I would argue the Controller / Intent also fits best in the client side. (Note: if you are building networked, client-side apps over a transport protocol, my argument falls flat, and I think you have more options as to where you might run this piece of the pattern.)

So far, I’ve stuck with my statement above and followed along quite nicely (I think) with Peter’s argument about MVC being a client-side pattern. However, we now need to discuss Models. Following the MVI terminology, Models receive Intent do some work and publish their revised state. I think we need some examples to understand distribution of Models.

Most single-player games can keep Model state quite close to its respective View and Controller. The state is most likely coordinated with a more global app state, as well (similar to the $scope hierarchy in Angular, actually.) However, once you start getting into distributed, multi-player games, you begin to see patterns like Flux (compared with Doom 3 in the linked video).

React Flux

What I find interesting is that in the case of Flux, the Intent (or Action in Flux / Reflux terms) communicates with the distributed part of the program. The Model (or Store in Flux / Reflux) remains a purely client-side concern. I won’t say this is wrong; I just wonder if this is really the right idea.

A few months ago, I was trying to visually conceptualize a similar idea. My original drawing had different terms related to my company’s product, but the general idea reduces nicely to a distributed, reactive form of the MVI variant of MVC:

I think either of the above are relatively good approaches. I’m partial to my drawing, but I certainly don’t think either are wrong. Nevertheless, I would like to dive a bit deeper into the Model and see where that leads.

In Search of the Model

What is the Model, really? I don’t know that I’ve ever seen a really clear explanation. The early MVC Models were simple, UI related concepts, but we seem to have grown the complexity of our apps far beyond that level of simplicity. The DDD and CQRS architectures provide some interesting ideas for defining a Model. Do these correlate with the Model in MVC? I don’t know for sure. In any case, you won’t typically represent either of these directly over HTTP.

Constraining this back to the topic of web-based applications. Web applications run over HTTP by definition. Anyone familiar with the HTTP 0.9 definition? What magic words do you happen to see there?

HTTP is a protocol with the lightness and speed necessary for a distributed collaborative hypermedia information system. It is a generic stateless object-oriented protocol, which may be used for many similar tasks such as name servers, and distributed object-oriented systems, by extending the commands, or “methods”, used.

Well, how about that? Maybe we are onto something! The only problem is that, in spite of the numerous clones of simple web API libraries, HTTP is quite a complex beast. Have you seen the state machine model?

ForGET HTTP State Machine Diagram for HTTP

Sure, you can avoid this diagram and roll your own way, but you lose a lot of the benefits of a standard protocol along the way, including a lot of the options available to understand what to do if something changes unexpectedly.

Most importantly, though, the diagram emphasizes the importance of leveraging HTTP resources as the Model for your application. Fortunately, tools like web machine and, for .NET, Freya exist to ease the challenges of trying to build the model yourself for each application. (I’ll be posting more on Freya later, but if you are interested in a sample, you can find the TodoBackend implementation on GitHub.) Even better, web machine correctly models REST, putting to … rest — excuse the pun — all the arguments and letting you focus on following the protocol. If you want to learn more, I recommend Sean Cribbskeynote from RestFest:

As I noted above, I plan to delve deeper into this specific concept further in the near future, so I’ll leave it for now.

Conclusion

I set out to show that MVC, the “real” MVC is in fact a pattern to which a lot of libraries and people are converging, whether they realize it or not. I think I’ve shown that, as well as that by adopting a more general pattern approach, you can leverage many of these component libraries’ styles in the same application, side-by-side. I’m curious to know your thoughts and whether anyone else has or is trying something similar. What have you found? Are you finding that such an approach works well?

I also set out to show that MVC can include an aspect of distribution. I think I highlighted this, as well, primarily in showing some of the original design goals of HTTP and aspects of OOP, and therefore MVC, that enable distribution. I don’t think I have a firm handle on exactly how to make this work, but I’m actively pursuing several aspects of distribution, both in terms of Model proxies in the client and building a web machine port for F# in Freya. What do you think? Is the Model proxy a good idea? Again, has anyone tried this approach and found it successful? Do you prefer the approach taken by Flux instead?

Please join the conversation! I’m very curious to know others’ opinions, including yours!

9 thoughts on “The Real MVC in Web Apps

  1. Looks like I forgot to discuss the lack of a client-side router. I will have to add that explanation in the next article on REST and the web machine model.

    Like

  2. Reading through this again, I appear to have neglected several other items, the most obvious of which is that I lost the thread of how to make the Resource -> Model proxy observable and reactive. I’ll explore this in future posts, as well.

    Like

  3. This reminds me a lot of event systems in game programming. (See http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/) I think MVI is a good explination of what we (the web community) are ultimately trying to do with web applications. I’ve been doing a lot of reading and thinking of OOP (late-bound message passing) and the “model” in how I develop applications and I think you’re spot on. The high-level is great for OOP, where we can use coarse-grained, agent-based, communication. I’m not a fan of how the “M” and “C” in MVC are currently used and am interested to see what your next few posts are going to cover!

    Like

Comments are closed.