From Cairngorm to PureMVC : a quick comparison

Note : you can download the Flex Builder's Project Archive at the bottom of this page

UPDATE : be sure to read Cliff Hall's comment at the end of this post !

I finally found some time to give a try to Cliff Hall's PureMVC framework. This piece of code seems to gain more and more popularity these days, some even declared it as the best Flex application framework currently available. So I thought it was definitely worth checking.

My goal here was to port an existing Cairngorm application to PureMVC, in order to make some comparisons between the two frameworks. So please don't consider this post as an in-depth coverage of PureMVC, but more like a quick overview from a Flex Cairngorm developer perspective.

The application being ported is not a real world application. It's a test RemoteObject application I usually generate as a test for FCG. Basically, I call a "Library" service which performs CRUD operations to a "book" SQL table. The Flex application uses this service to show a list of books in a Datagrid. The user can select a Book, which is then displayed in a book form. The user can edit the book, create new books, and delete existing books.

Basics

I should probably start by mentionning that PureMVC is not Flex framework. It's an AS3 framework, which is completely independent from the Flex framework and even from the Flash framework. It implements its own Observer mechanism called Notifications. There is no such thing as a PureMVCEvent extending a flash.events.Event. But, conceptually, you can think of it as events.

The Model layer can broadcast notifications. The View layer can both listen to and broadcast notifications. The controller layer can broadcast notifications, and can also listen to it by executing commands.

Framework initialization and Controller

When you create a PureMVC application, you first create a Facade which handles the communication between the Model, View and Controller parts of the app. It's a very lightweight Singleton which mainly registers Notifications with Commands, much like a Cairngorm FrontController associates CairngormEvents with Commands.

package example
{
	import example.control.*;	
	import org.puremvc.interfaces.IFacade;
	import org.puremvc.patterns.facade.Facade;
 
	public class ApplicationFacade extends Facade implements IFacade
	{
 
		public static const APP_INIT:String = "AppInit";	
		public static const CREATE_BOOK:String = "createBook";	
		public static const GET_BOOKS:String = "getBooks";					
		public static const UPDATE_BOOK:String = "updateBook";			
		public static const DELETE_BOOK:String = "deleteBook";	
		public static const BOOK_SELECTED:String = "bookSelected";							
		public static const BOOK_DELETED:String = "bookDeleted";
 
 
		public static function getInstance() : ApplicationFacade {
 
			if ( instance == null ) instance = new ApplicationFacade( );
 
			return instance as ApplicationFacade;
 
		}		
 
		override protected function initializeController():void
		{
			super.initializeController();
 
			registerCommand(APP_INIT, InitAppCommand); 
			registerCommand(CREATE_BOOK, CreateBookCommand);			
			registerCommand(GET_BOOKS, GetBooksCommand);			
			registerCommand(UPDATE_BOOK, UpdateBookCommand);						
			registerCommand(DELETE_BOOK, DeleteBookCommand);
 
		}
 
	}
}

PureMVC Commands are just like Cairngorm Commands. You generally start by registering a "STARTUP" or "INIT" Notification with a "StartUpCommand" which will have to register Proxies and Mediators.

package example.control
{
	import example.ApplicationFacade;
	import example.model.BookProxy;
	import example.view.BookFormMediator;
	import example.view.BooksPanelMediator;
 
	import org.puremvc.interfaces.ICommand;
	import org.puremvc.interfaces.INotification;
	import org.puremvc.patterns.command.SimpleCommand;
 
	public class InitAppCommand extends SimpleCommand implements ICommand
	{
 
		override public function execute(notification:INotification):void
		{
 
			facade.registerProxy( new BookProxy() );
			//...
 
 
			var app:PureMVCExample = notification.getBody() as PureMVCExample;
 
			facade.registerMediator( new BooksPanelMediator( app.booksPanel ) );
			facade.registerMediator( new BookFormMediator( app.bookForm ) );
			//...
 
			sendNotification(ApplicationFacade.GET_BOOKS);
 
		}
 
	}
}

Your application's root tag only has to instanciate the facade. A Cairngorm application's root tag, by comparison, has to instanciate the Model, the FrontController and the ServiceLocator (when needed).

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	creationComplete="start()" xmlns:ns1="example.view.components.*">
 
	<mx:Script>
		<![CDATA[
			import org.puremvc.patterns.observer.Notification;
			import example.ApplicationFacade;
 
			private var facade:ApplicationFacade = ApplicationFacade.getInstance();
 
			private function start():void
			{
				facade.notifyObservers( new Notification( ApplicationFacade.APP_INIT, this ) );
			}
		]]>
	</mx:Script>
	<ns1:BookForm id="bookForm" x="418" y="10">
	</ns1:BookForm>
	<ns1:BooksPanel id="booksPanel" x="10" y="10">
	</ns1:BooksPanel>
 
</mx:Application>

Anyway, for me, this is kind of a non-issue since I think decoupling between the root application tag and any framework should be achieved by creating a main View (a Canvas, most of the time), which is the one initializing the framework and which then be added as a child to the application root tag (whether it is mx:Application, mx:WindowedApplication, or mx:Module).

Model

There is no single-class Model such as a Cairngorm ModelLocator. Here, you create Proxies which store the state of the application, but also some logic related to accessing this data. PureMVC Proxies tend to have more responsabilites than a Cairngorm ModelLocator, as they are meant to deal with remote services, for instance.

Of course, calling the actual remote services will happen through Delegates. But still, I felt quite uncomfortable with the idea of calling my business delegate from the Model layer. So I decided to call it from a command, just like I would in a Cairngorm application. While this seems to be "not very PureMVC", Cliff Hall happened to mention that it could also be done that way. And, indeed, I worked fine.

package example.model
{
	import example.model.vo.Book;
 
	import mx.collections.ArrayCollection;
 
	import org.puremvc.interfaces.IProxy;
	import org.puremvc.patterns.proxy.Proxy;
 
	public class BookProxy extends Proxy implements IProxy
	{
 
		public static const NAME:String = "BookProxy";
 
		public function BookProxy(proxyName:String=null, data:Object=null)
		{
			super(NAME, new ArrayCollection() );
		}
 
		public function get books():ArrayCollection
		{
			return data as ArrayCollection ;
		}
 
 
		public function replaceBook(pNewBook:Book):void
		{
			var id:int = pNewBook.idbook;
 
			for ( var i:int =0 ; i < books.length ; i++)
			{
				var book:Book = books.getItemAt(i) as Book;
				if( book.idbook == id){
					books.setItemAt(pNewBook, i);
					return ;
				}
			}
		}
 
		public function removeBook(pBook:Book):void
		{
			var i:int = books.getItemIndex(pBook);
			if(i!=-1) books.removeItemAt(i);
			else trace("Book not found")
 
		}
 
	}
}

package example.control
{
	import example.business.LibraryDelegate;
	import example.model.BookProxy;
 
	import mx.controls.Alert;
	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
 
	import org.puremvc.interfaces.ICommand;
	import org.puremvc.interfaces.INotification;
	import org.puremvc.patterns.command.SimpleCommand;
 
	public class GetBooksCommand extends SimpleCommand implements ICommand, IResponder
	{
 
		override public function execute(notification:INotification):void
		{
 
			var delegate:LibraryDelegate = new LibraryDelegate(this);
			delegate.getBooks();
 
		}
 
		public function result(data:Object):void
		{
			var bookList:Array = data.result as Array;
			var bookProxy:BookProxy = facade.retrieveProxy(BookProxy.NAME) as BookProxy;
			bookProxy.books.source = bookList;
		}
 
		public function fault(info:Object):void
		{
			Alert.show((info as FaultEvent).toString());
		}
 
	}
}

View

One thing I think PureMVC is very could at is adressing a very well known Cairngorm issue : letting view components be notified of events happening in our application while maintaining these views completely decoupled from the rest of the application.

PureMVC does it with the use of Mediators. Mediators are classes that have knowledge of both the view it deals with and the facade (ie the rest of the application). It declares its interests towards some of the Notifications of the application. It then handles these notifications in a dedicated method.

The way it does it, though, is not as elegant as you could imagine : only one "handleNotifications" method handles all notifications in a big - and quite ugly - switch statement. Anyway, it works just fine, and it's still, as far as I'm concerned, a big improvement over Cairngorm ViewHelpers I had used so far.

package example.view
{
	import example.ApplicationFacade;
	import example.model.BookProxy;
	import example.model.vo.Book;
	import example.view.components.BookForm;
 
	import flash.events.Event;
 
	import org.puremvc.interfaces.IMediator;
	import org.puremvc.interfaces.INotification;
	import org.puremvc.patterns.mediator.Mediator;
 
	public class BookFormMediator extends Mediator implements IMediator
	{
 
		private var bookProxy:BookProxy;
		public static const NAME:String = 'BookFormMediator';
 
 
		public function BookFormMediator(viewComponent:Object=null)
		{
			super(viewComponent);
			bookForm.addEventListener( BookForm.EVENT_UPDATE, onUpdate );
		}
 
 
		public function get bookForm():BookForm{
			return viewComponent as BookForm;
		}		
 
		private function onUpdate(pEvt:Event):void
		{
			sendNotification(ApplicationFacade.UPDATE_BOOK, bookForm.editedBook);
		}
 
 
		override public function listNotificationInterests():Array
		{
			return [
			ApplicationFacade.BOOK_SELECTED,
			ApplicationFacade.BOOK_DELETED,			
			 ];
		}
 
		override public function handleNotification(notification:INotification):void
		{
			switch ( notification.getName() )
			{
 
				case ApplicationFacade.BOOK_SELECTED:
					var book:Book = notification.getBody() as Book;
					bookForm.currentBook = book ;
					break;
 
				case ApplicationFacade.BOOK_DELETED:
					bookForm.currentBook = null ;
					break;
			}			
		}
 
	}
}

The real problem though, is the idea of having to create one Mediator per view. That could mean a lot of files in a real world application. Perhaps it is possible to factorize them in some way, but I'm not sure. To be sure, this process might be eased by the use of generators.

(early) Conclusion

I must say I've encountered more difficulties than I expected. Some aspects, such as the relationship between the views and Mediators, are more subtle than they seem at first. While it definetly has similarties with cairngorm, the differences are still very important.

  • The fact that you cannot use data binding between the model and the views is pretty annoying since - as I understood it - you have manually update the data to keep them in sync.
  • There is no ServiceLocator-like tool, so I had to hardcode RemoteObjects by AS in my Delegate.
  • The one Mediator per view paradigm could end up being really annoying too.
  • Just like CairngormEvent's, Notification's data property is not strongly typed. So I guess we'll have to subclass and cast a lot...

All in all, I found PureMVC very impressive. If I manage to overcome the few issues that still bother me, It might even be a good alternative over Cairngorm for my next applications.

Finally, as you probably guessed, you can expect to see PureMVC generation in future releases of FCG.

You can download the Flex archive here :

AttachmentSize
PureMVCTestApp.png29.65 KB
PureMVCExample.zip21.53 KB

Anonymous on December 27th 2007

David, Thanks for posting this demo and your impressions and conclusions about PureMVC. Cairngorm aficionados will appreciate this work. I'd like to address the points in your conclusion in the best way possible. * The fact that you cannot use data binding between the model and the views is pretty annoying since - as I understood it - you have manually update the data to keep them in sync. PureMVC cannot rely upon data binding innately, since it is available only in Flex and AIR. (Remember the framework is currently being ported to 5 other programming languages...). And for moving data between the Model and View, it doesn't make sense in PureMVC because the Model is not just a class with a bunch of static variables, which you can bind to directly in an expression. Part of the philosophy of PureMVC is to make the View Components portable with no knowledge of PureMVC. You can take a View Component (MXML or AS class) from any PureMVC demo and move it to Cairngorm without removing any imports or expressions that tie it to the PureMVC framework. So data binding is typically restricted to moving information around inside the black box of a View Component. However, you don't have to do a lot of work to get updates from the Model to the view. For instance if a Proxy has as its Data Object an ArrayCollection, you can expose that as a public variable, use a Mediator to set a reference to it as the dataProvider of a View Component, and Flex will cause any View Components with a reference to the ArrayCollection to be updated when items are added removed or changed inside it. The same is true of XMLListCollections. * There is no ServiceLocator-like tool, so I had to hardcode RemoteObjects by AS in my Delegate. You easily can create a 'ServiceLocator' as a separate Proxy. Create a 'ServiceProxy', whose Data Object is an AS class (call it 'ServiceLocator') that has public properties that are your initialized services. When you would in Cairngorm get your ServiceLocator, simply retrieve the ServiceProxy and reference the service you need in your caller (Delegate or Proxy). You can even add handy methods to the ServiceProxy like getLoginService() that return the appropriate service. * The one Mediator per view paradigm could end up being really annoying too. There is no constraint on the Mediator / View Component relationship. For instance the top-level Application generally has an ApplicationMediator associated with it. This class is the only place that has a reference to the Application instance. However the ApplicationMediator often interacts with several children of the Application itself, by simply referencing their id properties. It is up to you as a developer to determine the granularity of the View Component that is handled a given Mediator. If a Mediator becomes to bloated dealing with all the children of its View Component simply create a Mediator for the more complicated child. For instance, if the child components of the Application instance are complicated enough to justify their own Mediators, then the ApplicationMediator typically creates and registers the appropriate Mediator for each of those children. * Just like CairngormEvent's, Notification's data property is not strongly typed. So I guess we'll have to subclass and cast a lot... The data property is in fact strictly typed - to type Object. Granted that is the root of all objects, but it is typed. Inside an application there are any number of things you may want to pass as the body of a Notification, so there is no choice but to subclass and cast if you need a specific body type. At least you can pass an object, unlike the Flex Event class, which requires a subclass to pass *anything*. Cheers, -=Cliff>

david_deraedt on December 28th 2007

Hi Cliff Thank you very much for these explanations. About databinding, I understand that as long as you don't have to rewrite the object representing the model's data, there is no problem involved since the object just dispatches "change" events and any view representing that data will be updated accordingly. For example, if I call my "getBooks()" method of my Service, my "bookCollection" has to be updated, and I can do that by just rewriting its source property, and everything goes just fine. But for objects that have to be re-written (say, a currentBook object that can be updated when calling some service methods), I've had to update the view's property representing that data "by hand". That's not a huge issue, since it's just one small line of code, but it's still a difference with the Flex+Cairngorm paradigm I wanted to point out. About the ServiceLocator-like Proxy, you're completely right. I'll try that. About the one-to-one relationship between the view and the Mediator, that's probably because I'm still new to this way of working. In a real world application, chances are that I would have tried to create a common mediator for several views. I'll definitely update my impressions once I'm more familiar with this framework.

Anonymous on January 10th 2008

Hi Guys, I'm agree with you on a lot of point I've also make my own Framework comparison (additioned with LowRA), but for now is in french : http://www.kilooctet.net/index.php/2007/12/14/69-comparatif-de-framework... I've alos make my Mediaro class to correct what I don't like in PureMVC (such as have the getMediatorName() method rewrite every time and the switch statement in handleNotification) : http://www.kilooctet.net/index.php/2007/12/11/68-puremvc-le-komediator-u... Both traductions are on the way... Flapflap from KiloOctet

Anonymous on August 01st 2008

i think it's not really true that you can't use data binding if you use the mxml views as black box you can reuse them for a lot of occasion, although, the mediator is used to communicate to the view in a specific case. For that reason, you can instantiate a protected instance of the proxy in the mediator class and then use BindingUtils.bindSetter I don't know if it is elegant but it's very powerfull suppose the case that your view is created after the DATA_READY_NOTIFICATION is dispatched... in that case i think the only way to get data is binding, otherwise your view can't receive the notification Matteo Lanzi

Anonymous (not verified) on January 07th 2009

I realised that one would have to set up data binding in the mediator's constructor between the model objects in the proxy and the visual components in the view component in order to use Flex's dynamic binding facility (one of its best features i.m.o.), but it still doesn't feel quite right. How would you bind an evaluation expression with BindingUtils? E.g. the following doesn't work: BindingUtils.bindProperty(viewComponent.someLabel, "text", proxy, "Welcome, Mr someName"); // someName is just a String here The following works: <... text="Welcome, Mr {ModelLocator.getInstance().someName}"> I got around this by adding the "Welcome, Mr " String to the someName variable every time I set it. Not a train smash, but not as nice. Also, it suffers from textual representations of fields which are not compiler checked. Someone suggested using a VO inside the view component to bind to (but they omit explaining how the properties of this VO would bind to the proxy model object). I'd really like to be able to apply my dynamic binding directly to my Model objects without having to use BindingUtils.

david_deraedt on January 08th 2009

To answer your first question, you may need to bind to a setter using BindingUtils.bindSetter() public static function bindSetter(setter:Function, host:Object, chain:Object, commitOnly:Boolean = false):ChangeWatcher Now, you're totally right about the lack of compile time checking due to the textual representation of the properties. About having a reference to a VO in the view, that may be an idea (as long as you realize that you then create some coupling between your view and domain model). DataBinding without BindingUtils is possible if you use MXML (of course), but you can also implement your own observer logic (eg Observer class from Adobe Consulting). I'm also struggling to find a elegant solution here. You may want to read my series about the Presentation Model pattern.

Anonymous (not verified) on January 08th 2009

Thanks for your response and suggestions. I've enjoyed adapting my Cairngorm-only app to use MVC (and I retain the Cairngorm service locator and responder stuff). It makes a lot of sense and the event infrastructure is great. That said, the view to model binding is the biggest problem I have. What if I want to bind two difference view components to the same UI (not data) model object? e.g. If I check a box in a dialog, then my main app panel must change a label. I can't host the UI model object in one of the view components because the other won't be able to access it. I can host it in a proxy, but then both mediators for the dialog and main panel need to bind their view components to it (which is ok). It gets ugly when I want to actually declare that e.g. a list's enabled property is bound to the UI model object in a boolean expression: I current achieve this by having my mediator bind the UI model object to a special variable (duplicate essentialy) starting with an underscore: var _lastFourSelected:Boolean = false; Then I can bind my List to this normally. I'd imagine I'd have the same problem with model data objects. It doesn't feel right to put UI model objects in the Proxy, but then where? The BindingUtils functions really suck that they require Strings ;-P Lastly, I don't believe that PureMVC's use of "Model", "View" and "Controller" are that accurate - a command is not the application's controller, a Mediator is not a view and a Proxy is not a model (entirely)!

david_deraedt on January 09th 2009

I should probably remind that I'm really no PureMVC expert, so take this with caution. Also, I'm not sure what you mean exactly by UI model object, but I'm guessing what you refer to is a presentation state + logic holder. Why not create and share an ad hoc presentation object to store the presentation state, and share it between your mediators ? Your facade can instantiate this object and inject it in your mediators. Wouldn't that work for you ? That's how I work with presentation models for, say, navigation logic, asset management, i18n, and all sorts of presentation logic and states that should be shared across the presentation layer.

Cialis (not verified) on September 22nd 2009

I have been pondering how to reconcile a Flex dataProvider with a PureMVC proxy object. I have done almost exactly what you described. I was sure that I was doing something "bad" :). Thanks, Derek Basch

Gerrar Tadalafil (not verified) on March 17th 2009

Hi Cliff, Could you tell more about the component relationship & child components relationship. I don't understant it as well as you do. Thanks! ---------- Cheers, Gerrar Tadalafil Tadalafil gerrar.tadalafil@hotmail.com

Anonymous (not verified) on November 14th 2010

Got it, I'll send an email to you now, I myself have only learned about this recently so I hope that it wouldn't be too late. calories burned

Anonymous on December 31st 2007

Hi All, Just regarding command-to-view notfications. In our project we have extended the Cairgorm framework so that an event can contain success and error function/clusure that will be invoked by the command. Cheers, Bartek

Anonymous on March 07th 2008

However, you don't have to do a lot of work to get updates from the Model to the view. For instance if a Proxy has as its Data Object an ArrayCollection, you can expose that as a public variable, use a Mediator to set a reference to it as the dataProvider of a View Component, and Flex will cause any View Components with a reference to the ArrayCollection to be updated when items are added removed or changed inside it. The same is true of XMLListCollections. I have been pondering how to reconcile a Flex dataProvider with a PureMVC proxy object. I have done almost exactly what you described. I was sure that I was doing something "bad" :). Thanks, Derek Basch

Anonymous on April 21st 2008

Just to point out your statement: >>The way it does it, though, is not as elegant as you could imagine : only one "handleNotifications" method handles all notifications in a big - and quite ugly - switch statement. Anyway, it works just fine, and it's still, as far as I'm concerned, a big improvement over Cairngorm ViewHelpers I had used so far. This is the "official" position of pureMVC: >>There has been much debate on the usage of â??switch/caseâ?? as many developers consider it to limiting since all cases execute within the same scoped method. However, the single Notification method and â??switch/caseâ?? style was specifically chosen to limit what is done inside the Mediator, and remains the recommended construct. I also was a bit surprised...but it seems that everything in pureMVC is well thought! Good for us! /Roberto Cosenza

Anonymous on May 12th 2008

We over here at 9mmedia have been doing some of the same research on PureMVC vs Cairngorm and decided a few months back to go with PureMVC also. We've got a handful of posts up on our blog (http://9mmedia.com/blog/?cat=9) with similar info, and some tutorials on how to get started and create custom components and things like that.

Romain (not verified) on October 29th 2008

Hello, In a project I'm actually running with Cairngorm, I cannot get the data to be correctly binded in the view using the [Bindable] meta-tag. The only option I have is to use the model directly in the view and this is really not what I want. Thus, Im looking at PureMVC and making my first steps with it. Thanks for the comments you make, they are very appreciable and informative.

serg (not verified) on March 25th 2011

Thanks, David, that was just what I was looking for!