This post introduces the Presentation Model (or MVVM) architectural pattern as one approach for creating "better" large-scale Flex applications and shows how the Mate Framework can be leveraged to not only achieve this pattern but increase separation of concerns above that of a "standard" Flex application.

Background

One of the great advantages Flex is that it makes creating applications very quick and easy by allowing ad-hoc mixing of MXML and ActionScript. This is ideal for rapid prototyping and can be all that is required for small applications. It also means anyone new to the technology can easily get started and relatively quickly put together quite rich applications. However, this architectural pattern, termed Autonomous View, does not scale well for larger, real-world projects. Fortunately, there are a numerous other architectural patterns - most famously(?) Model-View-Controller (MVC) - that attempt to directly address these concerns. Furthermore, various frameworks have been developed for Flex that specifically target these patterns. For example, both Cairngorm and PureMVC were designed for developing applications using the MVC architecture. There are many articles on the web considering the merits of different architectural patterns (and related Flex-specific frameworks). This article focuses on the Presentation Model architectural pattern, and a specific implementation of it using the Mate Framework, as a particularly useful approach to application development in Flex. Note that Mate is not designed to target any particular architectural pattern and that I am merely introducing its use with the Presentation Model pattern as one of many possible approaches, not a definitive or necessarily best one.

Presentation Model (or Model-View-ViewModel)

Presentation Model is an architectural pattern derived from Model-View-Controller (MVC) and originally introduced by Martin Fowler. Since then, Microsoft has introduced the Model View ViewModel (MVVM) pattern as a specialisation of Presentation Model that extends the original concept by adding a Model to make it more relevant to modern application development. I have adopted the MVVM terminology as I believe it better defines the entities involved in the pattern (purely because of its name).

The key features of the pattern are:

  • Model is the object model representation of state (whether as real state or as a data-access layer representation of the state)
  • View is the GUI elements
  • View model contains all GUI state and logic
  • View observes the view model and updates accordingly
  • View is "aware" of the view model
  • View model has no knowledge of the view

The observation required by the view is one of the reasons Flex is particularly suited to this pattern, as its binding functionality is simple and powerful implementation of observation. Additionally, the pattern ties in neatly to the relative merits of Flex's two languages, creating a simple conceptual model for developers:

  • View = MXML = components & layout
  • ViewModel = ActionScript = behaviour & state

The main advantages of Presentation Model (or MVVM) are the clear code separation between GUI layout and behaviour and the testability this inherently introduces (compared to Autonomous View and similar). As well as the advantages this brings in all object-oriented technologies, in Flex this is particularly useful when its Module functionality is leveraged, as it creates clear and easy cut points. The disadvantages of this pattern is that it can be relatively verbose and sometimes requires information to be passed through view model instances in order to appropriately traverse the display tree. However, as I'll show in the worked example below, the Mate framework provides some mechanisms that can drastically reduce the latter problem.

Mate

Mate is an event-driven Flex framework that provides both dependency injection and mechanisms to "glue" your code together, thereby enabling you to produce very loosely coupled code. The worked example below assumes some basic understanding of how Mate works, so if it is unfamiliar I recommend at least running through their Getting Started tutorial. There are also other excellent introductory articles and examples.

Worked Example

Here follows a worked example showing how the Mate framework can be leveraged to not only use the Presentation Model architectural pattern, but also add further elegance by using some of its core features.

The example below applies the Presentation Model (or MVVM) architectural pattern to an application with a global count that can be increased and decreased both directly and by manipulating a dynamic number of counters. Each counter maintains a sub-total that is the sum of its two sub-counters. Additionally, there is a global countdown that on reaching 0 resets all counts at all levels.



Source code

Core code

In order to not get too bogged down in application specifics and better focus on how Presentation Model (or MVVM) and Mate are used, I will focus on the sub-counter block of the user interface and logic. As shown in the class diagram below, the entities involved are: the view, SubCounterView; the view model interface, ISubCounterViewModel; and, an implementation of the view model interface, SubCounterViewModel. The diagram also shows how I have added some unifying interfaces for views and view models and a base class for view models containing some common functionality (more detail on this later when explaining the Mate-related specifics).

The observant among you will have spotted that there are no model classes mentioned. This is because in this particular example the models are the integer counts and as such do not require a custom class type. In more real-world applications it would be normal to have custom model classes that are used by the view models to maintain state.

Presentation Model UMLClass diagram abstractly showing presentation model example

The SubCounterView MXML consists of the viewModel property, a Mate map (more detail on this later when explaining the Mate-related specifics), a Grid to neatly lay out the local and sub-total counts and a couple of buttons to increase and decrease the local count. All state, such as the two count labels, is observed from the view model using binding and all logic/behaviour, such as the count increasing and decreasing, is delegated to the view model:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:map="maps.*"
    implements="views.IView"
>
    <mx:Script>
        <![CDATA[
            import viewModels.ISubCounterViewModel;
            import viewModels.IViewModel;

            [Bindable]
            private var _viewModel:ISubCounterViewModel;
            /**
             * @see IView.viewModel
             */
            public function get viewModel():IViewModel
            {
                return _viewModel;
            }
            /**
             * @private
             */
            public function set viewModel(value:IViewModel):void
            {
                if (!(value is ISubCounterViewModel))
                {
                    throw new Error("ISubCounterViewModel instance expected");
                }
                _viewModel = ISubCounterViewModel(value);
            }
        ]]>
    </mx:Script>

    <map:SubCounterViewMap dispatcher="{this}" />

    <mx:Grid width="100%">
        <mx:GridRow>
            <mx:GridItem>
                <mx:Label text="Subtotal:"/>
            </mx:GridItem>
            <mx:GridItem>
                <mx:Label text="{_viewModel.subTotal}"/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow>
            <mx:GridItem>
                <mx:Label text="Amount:"/>
            </mx:GridItem>
            <mx:GridItem>
                <mx:Label text="{_viewModel.count}"/>
            </mx:GridItem>
        </mx:GridRow>
    </mx:Grid>

    <mx:HBox>
        <mx:Button label="+" click="_viewModel.increaseCount()"/>
        <mx:Button label="-" click="_viewModel.decreaseCount()"/>
    </mx:HBox>

</mx:VBox>

As can be seen in the above code and class diagram, the view is tied to a view model interface rather than concrete class. Although this may seem unnecessarily verbose it brings some powerful benefits. it allows different view models, and therefore different logic and behaviour, to be used with the same view (i.e. inversion of control). This is both a relatively common code requirement and a useful mechanism for activities such as testing. It allows for the potential case of a single view model being shared across multiple views. A final (more arguable) benefit is that it encourages the developer to focus solely on the view rather than be distracted by behavioural details.

The ISubCounterViewModel interface looks like this:

/**
 * Interface for any presentation model that is to be used
 * with the SubCounterView view.
 */
public interface ISubCounterViewModel extends IViewModel
{
	[Bindable("subTotalChanged")]
	/**
	 * The total of all grouped sub-counter's counts.
	 */
	function get subTotal():int;
	/**
	 * @private
	 */
	function set subTotal(value:int):void;

	[Bindable("countChanged")]
	/**
	 * The local count.
	 */
	function get count():int;

	/**
	 * Increases the local count.
	 */
	function increaseCount():void;

	/**
	 * Decreases the local count.
	 */
	function decreaseCount():void;
}

The implementation of the interface, SubCounterViewModel, then looks like this:

/**
 * Default implementation of ISubCounterViewModel.
 */
public class SubCounterViewModel extends ViewModelBase implements ISubCounterViewModel
{
	private static const SUB_TOTAL_CHANGED:String = "subTotalChanged";
	private static const COUNT_CHANGED:String = "countChanged";

	//------------------------------------
	//
	//           Constructor
	//
	//------------------------------------

	/**
	 * Constructor
	 */
	public function SubCounterViewModel(dispatcher:IEventDispatcher)
	{
		super(dispatcher);
	}


	//------------------------------------
	//
	//           Properties
	//
	//------------------------------------

	//----------------------------------
	//  subTotal
	//----------------------------------

	private var _subTotal:int;
	[Bindable("subTotalChanged")]
	/**
	 * @see ISubCounterViewModel.subTotal
	 */
	public function get subTotal():int
	{
		return _subTotal;
	}
	/**
	 * @private
	 */
	public function set subTotal(value:int):void
	{
		if (value != _subTotal)
		{
			_subTotal = value;
			dispatchEvent(new Event(SUB_TOTAL_CHANGED));
		}
	}

	//----------------------------------
	//  count
	//----------------------------------

	private var _count:int;
	[Bindable("countChanged")]
	/**
	 * @see ISubCounterViewModel.count
	 */
	public function get count():int
	{
		return _count;
	}
	/**
	 * @private
	 */
	public function set count(value:int):void
	{
		if (value != _count)
		{
			_count = value;
			dispatchEvent(new Event(COUNT_CHANGED));
		}
	}


	//------------------------------------
	//
	//           Functions
	//
	//------------------------------------

	/**
	 * @see ISubCounterViewModel.increaseCount
	 */
	public function increaseCount():void
	{
		count++;
		subTotal++;

		// inform everyone else that the count has changed
		dispatchEvent(new CountEvent(CountEvent.LOCAL_UPDATE, _subTotal));
	}

	/**
	 * @see ISubCounterViewModel.decreaseCount
	 */
	public function decreaseCount():void
	{
		count--;
		subTotal--;

		// inform everyone else that the count has changed
		dispatchEvent(new CountEvent(CountEvent.LOCAL_UPDATE, _subTotal));
	}
}

The count and subTotal properties are straightforward Bindable properties with custom binding events. The increaseCount and decreaseCount methods are also very simple: they increase or decrease both the local count and the sub-total and then dispatch an event to inform that the sub-total has changed. The assumption here is that its local knowledge of the sub-total is correct and that it will appropriately be informed if that sub-total is changed elsewhere (much as it informs "others" of any change it makes, by dispatching the CountEvent). By making this assumption and using events for functional communication rather than explicit method calls we introduce loose coupling by using the observer pattern. Normally this would require some quite messy code to introduce and handle, but, as I will show, the Mate framework provides mechanisms that elegantly support this approach.

Glueing everything together

At this point we have all the constituent parts of the application, but they are not actually combined together in any functional way. Mate's EventMap and LocalEventMap components are a powerful mechanism whereby this glue code can be introduced with minimal impact on any of the core code and easily swapped in and out. As exemplified in the SubCounterView code, maps are introduced as a simple MXML tag and beyond that are entirely self-contained. They leverage Flex's event mechanism by hooking into an application's display tree, either at the global stage level in the case of EventMap or at the level of a specific DisplayObject in the case of LocalEventMap (see how the dispatcher of the SubCounterViewMap in the SubCounterView above is set to be the view). To achieve better separation of concerns (and to avoid coding using what can quickly become little more than a global variable mechanism) LocalEventMaps are always preferable to EventMaps.

The first part of pulling the parts of the application together is to appropriately combine views with their view model instances. Mate's map components allow us to inject these dependencies Injectors tags. The Injectors component can target any class instance that is added to the display tree below the map's dispatcher or that is created within the scope of the map. With this hook, an ObjectBuilder is used to create the desired view model before being injected into the view using the PropertyInjector. This way we can very specifically target (and easily change) any desired view/view-model combinations. Here is what combining the SubCounterView with its SubCounterViewModel looks like in the SubCounterViewMap:

<mate:Injectors target="{SubCounterView}">
    <mate:ObjectBuilder
        generator="{SubCounterViewModel}"
        constructorArguments="{scope.dispatcher}"
    />
    <mate:PropertyInjector source="{lastReturn}" targetKey="viewModel" />
</mate:Injectors>

The second part of glue code required for the fully functional application is to turn the event communication assumption(s) of the view models (see SubCounterViewModel code and explanation above) into reality. Once again, Mate's map components form the basis, but here it is the EventHandlers tag that provides the desired hook. The EventHandlers component allows a response to any event bubbled past the map's dispatcher (or globally, depending on both the map type and the EventHandlers own dispatcherType). This response can be any sequence of actions, such as invoking a method on a class (MethodInvoker), setting a property (PropertySetter) or even creating another event (EventAnnouncer). An example from the application above is the reset event that is dispatched when the countdown reaches zero and all counters should be reset. For the SubCounterViewModel this is handled by setting the zero value from the reset as its local count value, as shown in the following code snippet from SubCounterViewMap:

<mate:EventHandlers type="{CountEvent.RESET}" dispatcherType="global">
    <mate:PropertySetter
        generator="{SubCounterViewModel}"
        source="event"
        sourceKey="newCount"
        targetKey="count"
    />
</mate:EventHandlers>

As shown in the class diagram and SubCounterViewModel code above (and highlighted by the ObjectBuilder in the view/view-model injection code above), the view model classes all require a reference to an IEventDispatcher instance to act as its event dispatcher despite themselves also being IEventDispatcher implementations. This is the compromise required to use Presentation Model in conjunction with Mate. As previously mentioned, Mate's key components, EventMap and LocalEventMap, hook into the display tree's event hierarchy. Therefore, the view model classes must dispatch their events into the display tree to appropriately tie in with the maps. Hence the IEventDispatcher reference required by all view models. Fortunately this functionality is easily captured by a common base class for all view models in such a way that this becomes all but transparent to specific view model types. See the source code for the worked example for further details.

And there you have it, the Presentation Model (or MVVM) building blocks and how to combine them into an application using the Mate framework in such a way that the various concerns of the code are cleanly separated.

Testing

As mentioned in the background information, testability is one of the strong drivers behind the Presentation Model (or MVVM) architectural pattern. To highlight this, the source code for the example above includes unit tests (using FlexUnit) for, amongst others, the view model classes, thereby showing how the pattern allows testing of view-related logic. By appropriately using interfaces and inversion of control throughout, the testability of the code is further enhanced as this allows us to leverage mock objects in the unit tests. I have used Mock4AS because I happened to have it to hand, but there are several other good mocking libraries for ActionScript, such as asMock and Mockolate, so take a look at the different ones and pick your favourite (I don't have one yet).

Summary

Hopefully this article has highlighted the reason to consider the design of your Flex application in order to improve its maintainability and testability, and has introduced the Presentation Model pattern coupled with the Mate Framework as one of the potential approaches to solving these problems. I have only introduced a small sub-set of Mate's components and their capabilities so would encourage you to delve deeper into the documentation, tutorials and examples to gain a better understanding of the full Mate framework and its subtleties even if you are not interested in the Presentation Model pattern.

You can download the full source code for the Flex example given in this blog post here: MatePresentationModel-src.zip.