Tuesday, August 6, 2013

Presentation Layer MVC explained differently

The Model-View-Controller (MVC) pattern has been around a long time.  One thing I've noticed is that recent adoptions of the words MVC by the web community have blurred the original meaning.  Models have  become something that live only on the server-side, which causes one not to apply the design pattern for the client-side.

The wikipedia has a good write-up on MVC here.  I've clipped a section:

In addition to dividing the application into three kinds of components, the MVC design defines the interactions between them.[5]
  • controller can send commands to its associated view to change the view's presentation of the model (e.g., by scrolling through a document). It can also send commands to the model to update the model's state (e.g., editing a document).
  • model notifies its associated views and controllers when there has been a change in its state. This notification allows the views to produce updated output, and the controllers to change the available set of commands. A passive implementation of MVC omits these notifications, because the application does not require them or the software platform does not support them.[6]
  • view requests from the model the information that it needs to generate an output representation to the user.

To me, things start with the model.  Models exist anywhere there is stateful (mutable) information.  Note that the concept of event notification isn't strictly required - one can have a passive implementation of MVC.

In the above definition (and in the original MVC intent), a controller was anything that sent commands to a model in order to change its state.  It could also tell the view to change the presentation.

Views generate the output representation of a model to the user.  Note that a view can also accept commands from a controller in order to alter the presentation.  The implication is that views aren't required to be stateless.  They have their own internal models!

***

Where things have gotten messy is the attempt to simplistically map MVC onto web applications.  Models live on the server, views live on the client, and controllers are split between the two.  That is mostly okay until one runs into complex user interfaces.

The other large bit of messiness arises from the lack of composition in the original MVC description.  Controllers and views are associated with one and only one model.  There is no guidance on how one structures interactions between MVC components.

In my prior post, I described the composition of several UI components into what is essentially a view.  Search and select are simply navigation aids.  The interactions are complex enough to warrant decomposition into multiple cooperating objects.  Those objects mostly follow the MVC design pattern, but they are doing so in the context of the presentation layer.

***

A few concepts have become more clear to me.  One is to think hard about the models necessary to support the presentation layer.  Model boundaries often result from the need to obtain information asynchronously from the server and the submodel is nothing more than the server response.

Explicitly handle the cases where the asynchronous response is stale with respect to new model state.  In customer navigation example, the user can continue to modify the search criteria while the list of possible matches is being retrieved.

Models process events.  Those events can be commands from a controller, raised by other models, be the response to an AJAX request or a timer event.

One should try to have loop-free event chains between models to avoid infinite loops.  Ideally one does not want model A subscribed to model B and vice-versa.  This isn't always possible but should be aspired to.

MVC components should be standalone.  The event processing paradigm can tend to couple together components.  In the customer navigation example, one could design the "possible customer list" component to ask the search input component for its current criteria.  That makes it harder to isolate those components when testing.

I've found that adding a simpler listener proxy isolates the components.  A listener proxy acts on behalf of a component so that the component itself never registers as a listener.  The proxy listens to the event and then issues a command to the component (e.g. "queue new search criteria").



Using MVC/MVVM/MVB in JavaScript for Rich Application Interfaces.

Rich Applications Interfaces make heavy use of JavaScript to create the desired user experience.  Structuring and organizing that Javascript code is a bit of art and we are now seeing the emergence of "frameworks" such as Backbone.js to assist.  It is somewhat odd that it has taken this long to mature this area.

The underlying issue I think is that the web community started using Javascript for "glitz" rather than rich UI's.  As a result, much of the teachings and tutorials focus on using JavaScript to make your web-page look attractive.  For example, one has all sorts of animations and slideshows, fading effects, etc.  Each of those "glitzy" examples tended to be a local effect and required no coordination with other elements on the same page.

As an example of a rich UI, consider a screen which is used by customer support to locate a particular customer.  Part of the screen consists of "Live Search" where the system is searching for a customer.  As the representative types, AJAX queries are sent to the backend server to obtain a list of possible candidates.  The representative selects one customer from the possible matches and expects the screen to display enough information to verify they are working with correct customer.

In this example, we have multiple coordinated entities on the screen.  These are the user input for the search, the possible customer list, along with a detailed view of the selected customer.  Let's visit each entity and describe how it is expected to work.

Search Input

The search input is a text input with validation.  As the user types, the input is validated and a possible auto-complete suggested.

One thing that is important in the UI is that user input is not blocked waiting for AJAX responses.  So our input cannot spin-wait for a response.  It also has to account for the AJAX call being asynchronous and possibly having a large latency.

This suggests a simple MVC for the user input.  The user input keeps tracks of what the user types and provides validation.

There is a model in this UI component which contains what the user typed and its validity.  It generates events when the underlying model changes.

Possible Customer List MVC

The possible customer list component could be thought of as a service (or an actor) whose job is to retrieve a list according to the current search criteria. It might generate an AJAX request each time the search input changes.  

This component has a model which is comprised of:
  • A (conceptual) queue of input search criteria
  • The result set
  • A flag indicating an outstanding AJAX request
In MVC terms, control over the model happens by pushing a search criteria to the front of the queue.  The result set is what is viewed.

JavaScript programming is inherently single-threaded.  One often shifts to message-driven design concepts in order to capture behavior.  In the case of this component, it responds to two messages (aka events).  One message is the arrival of new search criteria.  The other message is an AJAX response.

The arrival of a new search criteria message results in the following behavior:
  • The criteria is pushed onto the queue
  • If the queue is not-empty and there is no outstanding AJAX request, send an AJAX request for the search criteria at the head of the queue.
  • Mark the result set as invalid.  Notify any listeners if there is a result state change
The AJAX response message processing is:
  • Clear the outstanding AJAX request flag
  • If the search queue is empty, we know that the search was the most up to date.  Then:
    • Update the result set and mark it as valid
    • Notify all listeners
  • Else:
    • Discard the response and leave the result set as invalid
    • Drain the queue until the last element is reached
    • Submit an AJAX request for the last element
Note that this is a simplified version.  One often uses timers to notice when the user has stopped typing to avoid sending excessive AJAX requests.  Timer expiry is handled as another form of message arrival.

Note as well that this MVC does not touch the user presentation.  It is purely a behind-the-scenes service.

Presentation of the Possible Customer List

This MVC is wrapped around a "select 1 of N" component.  The MVC listens to changes in result set status and updates the underlying component.  It provides a view of the current selection.  It notifies listeners when the current selection changes.

Presentation of the Selected Customer

This is an MVC as well.  It responds to changes in the current selection as well as AJAX responses.  Like the possible customer list, it needs to address changes in the selected customer while it has an outstanding AJAX request.

Next post: aligning this design with published MVC/MVVM/MVB principals.