Client-Side Rendering

A Tale of Two MVCs

A Tale of Two MVCs

by Yehuda Katz

In the talk "A Tale of Two MVCs" presented by Yehuda Katz at GoGaRuCo 2013, the focus is on the Model-View-Controller (MVC) architectural pattern, particularly in the realm of GUI programming. Katz aims to clarify the various interpretations and implementations of MVC, especially in relation to modern frameworks such as Rails, Ember, and Backbone.

Key points discussed include:

  • Understanding MVC and GUI: Katz emphasizes that many people lack a comprehensive understanding of the different MVC systems and how they apply to GUI programming.
  • Common Steps in GUI Programming: He outlines the basic steps in GUI programming from initial UI bootstrapping, translating user input into actions, updating application states, to notifying the UI of changes. These processes are consistent across different MVC architectures.
  • Issues with Original MVC: The talk identifies three main problems with traditional MVC implementations:
    • Lack of defined patterns for bootstrapping objects effectively.
    • Haphazard storage and management of application state across different widgets, leading to complexity.
    • Challenges in maintaining synchronous states between browser action and application state, especially during page reloads.
  • Critique of Rails: Katz discusses the misconception that Rails deviates from MVC, presenting it instead as a GUI design pattern with similarities to traditional MVC, while also acknowledging that Rails struggles with managing UI hierarchy and state effectively.
  • Advancements in Modern Frameworks: Insights into how frameworks like Ember and Cocoa improve MVC by introducing concepts like mediating and coordinating controllers, which enhance delegation and flexibility in event handling. He illustrates that Ember provides a clearer structure for managing state within its framework.
  • Comparison of Ember and Rails: Katz concludes with an assertion that Ember's architecture separates UI logic effectively while allowing Rails to concentrate on persistence infrastructure, suggesting that the two can complement each other rather than conflict.

Overall, the talk seeks to provide clarity on MVC's evolution, advocating for a clear separation of responsibilities in application design to foster better development practices.

00:00:20.320 If there's anyone who doesn't need an introduction here, it's Yehuda Katz. He's pretty much in the core team of every project I care about or has been at one time. Yehuda kept wondering what he should talk about today because there are so many things to discuss. This talk idea is actually something that Sarah May and I came up with, and Yehuda thought it sounded fun. So, I present to you Yehuda Katz with a tale of two MVCs.
00:00:53.760 Thank you! Am I on? Looks like I'm not mirrored after all those technical difficulties. What is going on? Ah, okay, seems good.
00:01:08.640 Today, I'm going to talk about MVC broadly, focusing on GUI programming. I apologize in advance if this ends up being dry; I'm doing my best to avoid that, but I think I will fail. So, just a warning ahead of time. This talk is an attempt to unpack what people mean when they say MVC and what people mean when they say GUI. There is going to be a lot of technical detail, and hopefully people will find that useful or interesting. One of the motivations for me giving this talk was a quote from DHH: "Current JavaScript solutions suffer from double MVC; you need both server and client-side MVC stacks."
00:01:45.600 The thing that frustrates me about this quote, and many others who know anything about MVC, is the amount of unpacking you have to do just to understand it. After you break it down, you realize that the entire sentence becomes nonsensical because it uses the word MVC twice to mean different things in different contexts unless you're doing it really poorly, which I'll address later. I looked at this and realized that it resonated with many people. It seems that a lot of people don’t have a good understanding of what the Rails model of GUI programming is, what the Ember model is, or what the Backbone model is, and what the Smalltalk model was.
00:02:25.520 I see many references to the Smalltalk model but not providing much value in those discussions. So, I wanted to unpack what it is to do GUI programming and how these different models work. First, I don't want to talk about MVC at all because 'MVC' is kind of a meaningless term. Instead, I want to present a unified model of GUI programming that I think can help understand pretty much all these different systems.
00:02:51.040 To understand GUI programming, there are several steps. Step one is that when the user comes into the system for the very first time, they want to look at a screen. You have to bootstrap whatever objects you have and put them into the state that will be useful later. Once you've done that, you need to take those objects and use them to draw the initial UI.
00:03:14.720 As the user starts clicking around, you want to take what the user actually did—like a mouse down at position (12, 57)—and convert that into user intent. This becomes especially important when you want to support touches, clicks, and maybe keyboard events, which all may mean the same thing. You really want to have a part of the system that states that the user touched position (12, 15) or that the user was focused on it and hit enter. Next, you say, 'Okay, the user clicked log out; I would like to log them out.' So, you update the application state. There’s a notion of application state that may not be persisted anywhere; it’s just what the application is doing right now. After updating the application state, if I delete a todo, I want to actually remove it from the system.
00:04:31.360 Then, I want to say, 'Okay, UI, just for your information, there's some new stuff for you to draw.' Something in the system needs to acknowledge that there’s something new to draw. These are basically all the things that happen, regardless of whether you're using MVC, MVVM, MVP, Smalltalk, NVC, Rails MVC, Django MVC, Ember, or Backbone. All these systems have places for each of these states, so I want us to think about the whole story in terms of these steps.
00:05:07.919 The first attempts people made in the 70s and again in the 2000s with the web were to do everything manually. The easiest way to understand this is with jQuery. By 'all manual,' I mean bootstrapping objects is simple; you handle it when the DOM becomes ready. Drawing the initial UI follows the same principle; this is also when raw input translates into user intent, and that too happens in the event handler.
00:06:11.360 Updating the application state would happen in the event handler, and the application state would just be global variables. Updating domain objects? You guessed it—event handlers. Saving them would involve some Ajax thing in an event handler and persistence ad hoc. Notifying the UI of changes? I don’t have to notify anything; I just do everything in the event handler. This straightforward system works really well for extremely simple tasks.
00:06:49.280 This is essentially how GUI programming was both in the 70s and also in 2005. Yes, there was some weird gap there. Now, in the 80s and around 2008, a realization emerged that having your view logic - the logic that draws things and the logic that deals with domain objects - in the same place is a bad idea. This led to the concept of separated presentation. Two easy ways to understand this are Backbone and Smalltalk.
00:07:26.400 How many people have used Backbone? A lot of people! Awesome. And Smalltalk? Probably many fewer have written Smalltalk MVC, so I will stick with Backbone for now. The basic structure looks like this: you have a UI—like a graphics context. The UI receives raw input. The view takes the raw input and figures out what to do with it, updating the model. Then the model notifies the view. Importantly, this is an observer relationship, so the model doesn’t know anything about the view—it just notifies the view.
00:08:47.200 The view then updates the UI based on the changes in the model. This forms the Backbone model, which is simple and straightforward. What I will call separated presentation for this talk separates the presentation and the model. Importantly, the view has access to the model, but the model does not have access to the view. Nonetheless, the model must notify the view somehow, often relying on a system of observers.
00:09:29.520 Now, separated presentation is quite similar to MVC; in fact, this concept came from MVC. The small tweak is this: in separated presentation, we define a view and a model, while MVC states that raw input goes to one object, the controller, and the drawing happens in the view.
00:10:01.440 This differentiates it from previous designs where there was a single object managing both events and UI updates. In MVC, a controller object receives raw input, while the view draws. Importantly, in Smalltalk MVC, the view and controller have access to each other as instance variables, which is heavily utilized due to the observer pattern not functioning well in more complex situations.
00:10:29.920 As a practical matter, the separated presentation model, where there's one object, and the MVC model are roughly equivalent, aside from code organization. In MVC, the code that updates the UI resides in the view, while the code that receives events sits in the controller. Access to each other creates coupling, meaning they often talk about the same thing.
00:11:14.280 When we talk about MVC, we need to consider how we bootstrap objects into the system. MVC does not explain this process; it merely states that you bootstrap objects however you want—that's someone else's problem. The view draws the initial UI, while the controller translates raw input into user intent.
00:11:43.200 You will notice a pattern here with MVC regarding the next steps: updating application state, updating domain objects, notifying the UI - all of these responsibilities fall on the controller. Application state and domain objects are managed using ad hoc objects that provide no clear answer in the system on how they should be structured.
00:12:08.960 In my view, there are three very important problems with the original MVC pattern. First, how do you bootstrap objects properly? While you could create patterns, many developers implement various patterns for bootstrapping as they navigate a particular screen hierarchy. Without a built-in pattern in MVC, effectively managing hierarchy proves challenging.
00:12:48.000 Second, application state in these systems is often stored haphazardly. While you could create an object to manage it, the actual MVC system results in every widget having its own MVC, which complicates matters. Third, if you continuously reload pages, maintaining focus and allowing undo features becomes problematic as the browser state and Rails application state are not synchronized.
00:13:16.160 Next, I want to highlight some critiques regarding Rails. A common belief is that Rails has nothing to do with MVC, that it's a completely distinct pattern. For me, the terminology does not matter, but it’s crucial to assert that Rails offers a GUI design pattern that shares a lot in common with other GUI design patterns.
00:14:01.760 When talking about a simple Rails pattern diagram, the browser acts as a black box. The view provides the browser with some HTML, which the browser uses to populate the UI context. When a user interacts with the interface, the browser translates the raw input into a semantic event based on the pre-computed logic when rendering the UI.
00:14:39.520 When the user clicks something, the browser sends an HTTP request, effectively packaging the transition request or form submission and sending it to the server. The controller handles the request, updating the model. Subsequently, the controller retrieves new content from the view and communicates it back to the browser, re-rendering the UI. The critical aspect to realize here is that this follows the UI pattern established previously, though some peculiarities emerge due to the reliance on pre-computation.
00:15:15.600 One approach Rails adopts is the router and controller cooperation for bootstrapping. This ability removes some of the ad-hoc organization you might otherwise see. The browser interprets raw input and translates it into user intent before communicating with the controller, again emphasizing that the structure remains consistent with earlier patterns.
00:15:54.560 However, there are still some limitations to note: Rails lacks a clear method of managing UI hierarchy. The framework often uses a single massive granulated object for entire pages, requiring redundant re-rendering whenever changes occur, leading to a sense of coupling similar to that in Smalltalk MVC.
00:16:37.440 Also, when it comes to user interactions, Rails provides limited ways to manage the browser's state effectively. You end up prioritizing either the browser state or the Rails application state, leading to latency issues, especially when the browser state is thousands of miles away.
00:17:25.760 Because browser limitations often restrict interaction methods to those the browser can package into semantic events, many developers struggle with maintaining a flexible interaction design. Developers frequently resort to leveraging JavaScript to help manage interactions, leading them to a common pattern of creating Ajax requests, but they still face the limitations of full-page updates.
00:18:10.080 Now that we've looked at earlier MVC systems and discussion patterns, we also need to move forward to contemporary GUI systems. I aim to highlight enhancements made by the Smalltalk and Cocoa environments, which have not yet been reflected in Rails. For example, in basic MVC, the controller directly updates the model and subsequently notifies the view.
00:18:53.360 However, this approach creates issues when the model lacks the necessary information to operate correctly. A common example involves determining visual representation conditions — for instance, needing to show a number in red at specific intervals. The model should not handle display specifics, so an intermediary presentation model manages those transient visual states.
00:19:35.040 This process leads to the establishment of a presentation model that separates view preparation from drawing, retaining the previous distinctions between event handling and drawing. In practical application, this allows for greater testing of UI logic without combining all components into a single block.
00:20:29.919 Now, let’s not forget the elephant in the room: application-wide events like logout. It doesn't fit nicely into MVC's traditional flow, as it requires context on a higher level than a single view offers. In Cocoa's enhancement of these design patterns, we see a model where the view directly manages local events, while higher-order coordinating controllers handle broad application-related events.
00:21:16.960 The Cocoa architecture introduces two complementary concepts: mediating controllers, which manage local state within a specific view context, and coordinating controllers, which oversee broader application states. This promotes better delegation and flexibility in how events are routed and handled throughout the application.
00:22:06.240 For instance, if a user action doesn't fit the local managing controller's scope, it can broadcast that event higher within the controller hierarchy, allowing for centralized handling at an appropriate application level. This design encourages modularity and allows distinct processes to coexist fluidly within an application.
00:22:55.680 To illustrate, think of a larger application like iTunes: clicking on an item doesn't invoke its own functionality but instead sends a centralized event up the hierarchy until it reaches the primary controller, which can then handle tasks like playing a selected song or other application-specific queries.
00:23:45.120 Despite the clarity this pattern brings, there are still limitations tucked within this design. Developers must navigate complex observer systems, decide how to bootstrap fully, and address inherent limitations imposed by browser URL handling. With this foundation laid, it is fitting to finally look at Ember.
00:24:32.640 Ember presents a diagram that resembles the Cocoa model, albeit with a few distinctions. Importantly, Ember avoids terms like mediating and coordinating controllers, using more unambiguous terminology. In effect, what Cocoa refers to as a mediating controller aligns with Ember's notion of a controller, whereas a coordinating controller resembles the route.
00:25:15.680 Ember grounds itself in a more formal state machine where bootstrapping objects occurs within routes. It resolves the chaos of having disparate objects tied together while maintaining simple and clear structures that align elegantly.
00:25:58.960 In consecutive transitions within UI, event interactions find unfettered pathways to upwardly send information gleaned from nested templates to higher-order controllers. Essentially, if an action triggers an event, it propels the message through the controller toward its predefined route, seamlessly aligning actions with state updates in an accessible manner.
00:26:43.919 The heart of Ember’s framework lies in this organizational clarity. The client side bears the heavy lifting of UI responsibilities, origination, and state management, which resolves issues prevalent in the Rails architecture where the server primarily handled baseline requests. Rails becomes strictly a persistence infrastructure, handling only the relevant components.
00:27:30.400 Moreover, the interplay of Rails within its persistence model shouldn't overshadow the significance of maintaining a single clearer MVC definition. In ideal implementation scenarios, dual MVC representations occur only when frameworks improperly intertwine, such as layers where one adopts Rails and simultaneously stacks on Backbone or a similar library.
00:28:15.600 In conclusion, I hope I've provided a fresh viewpoint on how the Ember architecture truly separates UI logic whilst maintaining a robust collaboration with Rails. The cleanup of how responsibilities are delineated paves clearer development trajectories, clearly stating where UI management exists while protecting Rails' domain primarily around persistence.
00:29:03.840 In reviewing where we have arrived, bear in mind that the client can maintain all logical control; Rails just needs to focus on persistent storage. The best practices come from a clear understanding of which component handles specific tasks, directing efforts into creating a unified, wholly managed application design. Therefore, while a regular server-side MVC exists, it should not conflict or overlap haphazardly with client-side rendering and UI concerns.
00:30:10.240 Thank you very much!