Submitted articles

When MVC isn't enough. Advice for GWT Developers too young (or too old) to remember swing

Author: Brendan Lawlor
Company: DeCare Systems Ireland
e: BLawlor@decaresystems.ie
twitter: @brendanlawlor
blog:http://blog.decaresystems.ie

Rich Clients have been around for a long time – a long time before GWT and well before the advent of Web Applications themselves. But GWT follows a pattern that has long existed in Rich Client development, which can be best described as component-based and event-driven. This entails small elements (Widgets) like text boxes and buttons communicating with each other by emitting events when UI activity is detected. In theory, developers need only combine these widgets and channel their events, in order to build more sophisticated applications. This is the same approach as was used in desktop application development in Java (Swing, SWT etc). One clear advantage of using GWT is that it is a return to a well-respected GUI architecture and away from the page-based, request-response architecture behind Struts and other similar web frameworks. But this advantage carries with it a built-in risk: a return to the architectural problems of the past – problems that were never completely solved and have been forgotten and neglected over the last 10 years, thanks to an understandable industry focus on web development.

The Number One Problem in Rich Client Architecture

So what are these architectural problems? The principal one is coupling: The problem that plagued Swing programmers who wanted to build component-based systems of even moderate complexity was that their code tended to become irreversibly coupled, a spaghetti-code of interlaced method calls. This prevented code reuse and also made maintaining or improving the code more difficult – the typical brittle code effect of tight coupling. But why did this happen? The Model View Controller (MVC) design pattern was supposed to address the problem of coupling, and is still considered the de facto approach for building GUIs. But MVC is not enough, as I will show below. For now, let’s look at how MVC works. In brief, the Model holds any data related to the GUI, and perhaps does some simple validation; the View is entirely responsible for displaying the Model’s data; and the controller manages much of the communication between the two. The most important thing to note about MVC is that the Model is never coupled to the View or the Controller. Typically the Observer Pattern is used to keep the Model completely in the dark about how its data is being rendered. In this way, a completely new View on the data can be constructed without requiring any change to the Model.

The Limitations of MVC

When building a moderately complex GUI application consisting of several Views, we try to use MVC to build business-specific Views from business-agnostic Widgets. We code Models and Controllers for each View to conform to the MVC pattern, creating business-specific MVC Components. Finally we write code to navigate from View to View, building up an overall flow of control and data that Characterises the new application. Even if we employ this pattern, coupling can still be introduced in any of three ways:

  • Containment: If one Component’s View incorporates (visually contains) another’s, the first cannot be subsequently used with also using the second.
  • Event Names and/or Type: If an event type belonging to one MVC Component is passed unchanged (via whatever route) into another MVC Component, the second component is coupled to the first and cannot be reused without also pulling in the first.
  • Event Payloads: Events can contain data. If the data in an event received by a component belongs to another component, then the first component is coupled to the second and cannot be reused without the owner of that event data, and the first component cannot change its event without having unforeseen consequences on other components.

This is a confusing situation for developers who have been taught the importance and power of MVC. The fact is that MVC is a powerful and important pattern; it is an excellent pattern to follow when developing a GUI widget. But it does not address the composition of UI Widgets into a business-specific View. It does not address the architecture of a GUI application composed of multiple MVC components. In order to connect a number of MVC components together into a network of co-operating but decoupled instances, we need something else besides plain MVC.

Why do we still think MVC is enough?

Firstly, we assume that Widgets and Components are the same thing. Because Widgets such as combo boxes and buttons are often build along MVC lines, we equate them with the businessspecific units (Components) that we build from them. We equate the dumb UI event and data such as ‘button clicked’, with our Components’ business-specific event and data such as ‘employee selected from list for editing’. But Widgets and Components are different creatures and can’t be treated in the same way. Widgets have no business knowledge; they provide basic common services with universal application. Business components encapsulate narrow data and behaviour that is specific to a particular aspect of the application. This data and behaviour is liable to change as development goes on and needs to be hidden from the rest of the application. When Widgets spread around a codebase, they spread like wealth. Components, by contrast, spread like cancer.

Secondly, we tend to assume that UI Widgets combine easily, like Lego blocks, to make what we have described above as Components. And we also tend to assume that these Components combine easily to make Applications. It’s a seductive idea, but it is based on a false comparison. To make a big and complex Lego construction, you simply compose it of lots of small Lego blocks. But Lego’s simplicity of combination is due to the fact that the blocks themselves are simple, monolithic and indivisible components. MVC units (Widgets or Components), by their very definition, are not. Their underlying complexity cannot even be hidden through encapsulation because to meaningfully combine two MVC units, we must combine View with View and Controller with Controller and sometimes even Model with Model. Treating an MVC Unit as a Lego block ignores the famous advice that things should be made as simple as possible, but no simpler.

hMVC and PAC

This problem has received some attention in the past. Some patterns exist to allow many MVC Components (sometimes called MVC Triads) to work together in an application. Examples of these are hMVC (hierarchical MVC) and PAC (presentation-abstraction-control). What these patterns recommend is that MVC Components communicate with each other only through their Controllers. The MVC Triads are arranged into a hierarchy and events are routed along the tree of Controllers thus formed. The hierarchy typically mirrors the visual containment hierarchy of the Triads’ Views. These patterns are improvements when compared to an ad hoc architecture, but both patterns still allow the MVC components to get coupled to each other in the three ways described above. They still route events and event data, by whatever path, from one component into another. They don’t address containment-related coupling at all.

MVC for GWT: The Application Layer

For those who are about to embrace GWT, or indeed any component-based, event driven frameworks, I’d like to propose an alternative architecture, which targets the three sources of coupling listed above. This architecture calls for an extra layer – the Application Layer – through which all communication between Components passes. Event names and payload data coming from Components are converted into Application events, routed to their intended destination Component by Application Layer code, and converted back into the destination Component events. This is the job of the AMVC Application layer – to mediate between co-operating components. Moreover, Component Views are never combined. At most they share a containing View that also belongs to the Application Layer.

This architecture retains the hMVC idea that Components should communicate with the outside world only through their Controllers, and it specifies that each Controller should define its own events and declare which of these events it can emit and which it can absorb. But we dispense with hMVC’s hierarchy and replace it instead with a simpler flat structure – the Application Layer – to which all Components attach their controller. The Application Layer is simply a clearing house for event traffic. It captures emitted Controller events, translates them into Application Events (including their payload) and routes them to other Controllers, translating them back into one of that Controller’s events.

For example, imagine two Components called SearchCustomers and ListOrders. An event from SearchCustomers called SearchCustomers. CustomerFound and containing a CustomerDetails DTO might be translated into an Application event called ListSelectedCustomerOrders with a payload of customerId. It might then be routed to the ListOrders Component, and translated into a ListOrders. ListByCustomerId event before being injected into that destination Controller. The Application Layer might take the additional steps of hiding the View of the SearchCustomers Component and making visible that of the ListOrders Component.

The communicating Controllers remain blissfully unaware of each other. Only the Application Layer code is aware of the connection. This is precisely the kind of content that should be found in application code: The stitching together of independent business components to provide an overall process flow. The Components can still be used in other applications.

It is a built-in assumption in this architecture that one MVC component will never contain another. Components should be identified, during the UI requirements phase, based on this assumption. If two widgets need to be shown at the same time, they should be siblings in the same parent (application level) container. This removes the need for complicated containment hierarchies, and related event-passing hierarchies.

The entire proposition can be summarised in the following diagram.

The entire proposition can be summarised in the following diagram

Implementation notes: The Application Layer is implemented as an ApplicationController class. A decoupled event-based interface is used by the Application Layer to access Controllers’ emitted events. But for ease of development, a simple method-invocation interface is preferred when injecting events back into a Controller.

The simplicity of the architecture is hopefully clear, and it is due to the straightforward approach taken: identify the sources of coupling and eliminate them.

 

Sponsors

This web site, with its corresponding services, are provided because of the support of our sponsors who help in a number of vital ways. All the sponsors believe in the Open Source ethos and willingly offer their support. Please recognise this support by considering them when you require services in their field of expertise. Finally without the commitment from the community this would not be possible, so thank you to you all.

Founders

Cake Solutions

OpenCredo

Skills Matter - Open Source and Agile Training & Events

Sponsors

FDM Group

Hays - Recruiting experts worldwide