explorers' club

explorations in dev, science, sci-fi, games, and other fun stuff!

Solution Found: Cairngorm + Maté + PureMVC

10 Comments

Notes

  • the code samples and sample application utilized Flex 4 SDK
  • I am using a patched version of Mate to accommodate the Flex 4 SDK
  • I will post further updates to this post so stay tuned
  • the sample application is UGLY, so don’t expect anything great here folks.   Just enter some text in both text fields and hit save.  Tada.  It gets sent to two hidden labels.
  • you can download the sample application here – link

Goals

As of late I have been quite obsessive about finding a solution in which an application’s MVC constituents could be highly decoupled.  Some of the requirements in this solution are:

  • view classes have no knowledge of UX logic (no dispatching of CairngormEvents)
  • view classes have no knowledge of Model objects nor {binding} to objects outside their local members
  • view classes are ignorant of any mediators wrapping them
  • command classes are ignorant of any mediators dispatching any UX logic events
  • command classes have no knowledge of Model objects outside of data payloads riding in on execution parameters
  • avoiding Singletons where possible

Don’t you just love lists?  I do 🙂  This at first sound very restrictive especially to those of us who are long-time Cairngorm users.  It is quite easy to {bind} a view to a model class and be done with it.  This is great in mocking up prototypes quickly but is a shortcut that leads to plenty of headaches later on.  It’s quite easy to perform UX logic inside a Command class and then directly wire into a ModelLocator.  Again, this proves to be a pain if you are looking to do some sort of unit testing or want to decouple from your Model classes.

So let’s look at a few good things that each of these frameworks provide such that we can pick out the best and discard the rest.

Cairngorm

  • Command pattern – being able to abstract out UX logic into commands is just great.  It adheres to the MVC paradigm so long as we are able to decouple the constituents.

PureMVC

  • Mediator pattern – this allows us to accomplish points 1 & 2 above in the requirements.  It does mean that mediators are obviously knowledgeable about their views but its a one-way coupling issue.

Maté

  • Dependency Injection – the ultimate solution in decoupling all MVC actors.  Maté’s unique binding solution for DI is great in that we needn’t have to implement anything aside from the Injector mechanisms.  This affords us the ability to also have multiple DI maps for various scenarios, whether that be UI testing, unit testing or whatever.
  • EventMaps – In a general sense the EventMap is a great way to delegate the task of doing all the Controller logic that normally we have to do ourselves in Cairngorm or PureMVC.

So there you have it: An outline of how I plan to use these 3 frameworks to build a highly decoupled app in which all our MVC actors are basically plug n’ play classes.  Cool sounding eh?  Ok, now that that is out of the way I need to explain a few specifics with respect to the Command and Mediator pattern and how I use them.

Command classes

Using an out of the box Cairngorm ICommand is a bit restrictive in this solution so we are going to borrow the interface and change it up a bit.  Rather than this:

execute (event:CairngormEvent):void

we need something a bit more loose like so:

execute (params:Object):void

In this, we are simply accommodating some of the restrictive mechanisms found in how Maté handles the event system.  You could wire it in many ways but for the most direct route let’s stick with this.  The other thing we need to do is have our Command classes extend event dispatcher so that we can signal back to Maté that either the response logic or fault logic has commenced.  So here is a sample of how the IResponder portion works in these commands:

result (data:Object):void
{
     //assuming your response from some service,
     //say an XML response that needs massaging to an AS3 Value Object
     var vo:SomeValueObject = data;

     var resultEvt:DynamicEvent = new DynamicEvent("result");
     resultEvt.data = vo;
     dispatchEvent(resultEvt);
}

fault (info:Object):void
 {
    var faultEvt:DynamicEvent = new DynamicEvent("fault");
    faultEvt.data = vo;
    dispatchEvent(faultEvt);
 }

So this should be roughly inline with what most Cairngorm users do on a regular basis.

Mediator classes

It’s not like PureMVC invented the mediator pattern, but it certainly brought the valued pattern to light to many Flex developers.  There are no particular implementations from PureMVC or anywhere else, this is just my spin on it:

Views should implement a simple marker interface called IMediatorClient.  That’s it.  If you are using Flex 4 SDK, then remember currently code hinting will not work when typing “interface” in your MXML tag.  It will compile just fine.  At least it did for me.

Mediators happen to implement the following interface:

public interface IMediator
{
   function get target ():IMediatorClient;
   function registerTarget (target:IMediatorClient):void;
}

Now we need keep in mind a few Maté specific things here.  Because Maté happens to leverage the display list and some of the inner workings of the Flex system manager we have three choice in how Maté gets wind of our events.

  1. dispatch bubbling events from our IMediatorClient (because all views will be IEventDispatchers by their very nature)
  2. wire in a specific Dispatcher from Maté (this means bringing in Maté specific code into our pristine code. no….)
  3. find some other solution that probably entails a central event dispatcher that then wires into an EventMap

Obviously solution #1 is the most unobtrusive and easiest for OCD folks (like me) to deal with.  Let’s stick with that for now.   Besides, a bubbles event is not such a bad thing is it?  Just make sure that you are using some unique event types otherwise you might trigger unexpected handlers elsewhere in your application.

So let’s see how we wire these puppies up into Maté.

Maté wirings

First up is the what I am calling a Mediator Map.  Simply put it is a way to wrap your IMediatorClient views into IMediators without specifically having the wiring code in anything but Maté.  Note that I am doing this in Flex 4 SDK and thus requires the use of the declarations tag.

<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap
xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo" xmlns:mate="http://mate.asfusion.com/">

<fx:Script>
<![CDATA[

import app.view.*;
import app.view.mediators.*;

]]>
</fx:Script>
<fx:Declarations>
    <mate:Injectors target="{SampleIMediatorClient}" debug="true">
       <mate:MethodInvoker generator="{SampleIMediator}" method="registerTarget"
          arguments {event.injectorTarget}"/>
     </mate:Injectors>
</fx:Declarations>
</mate:EventMap>

So this MediatorMap would be placed on your Application level class.  Simple enough right?  The cool thing is that this autowires a view to a mediator when the view gets attached to the display list.  And because Maté is in charge of caching the instances it creates, you will soon see that the mediator can be referenced in the DI map below.

Dependency Injection map

So this next map is where we are basically injecting model classes into mediators which in turn affect changes on the views through whatever logic you so desire.  I like using the IDataRenderer interface alot as it is easy to remember.  Check it out:

<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap
 xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" xmlns:mate="http://mate.asfusion.com/">

 <fx:Script>
 <![CDATA[

 import app.model.*;    
 import app.view.*;
 import app.view.mediators.*;

 ]]>
 </fx:Script>

 <fx:Declarations>

 <mate:Injectors target="{CurrentUserViewMediator}">
 <mate:PropertyInjector targetKey="data" source="{UsersManager}" sourceKey="currentUser"/>
 </mate:Injectors>

 </fx:Declarations>

</mate:EventMap>

So all this does is say, when we make a change to the currentUser on the UsersManager class, we want to let the CurrentUserViewMediator know about the change.  Maté handles the binding mechanisms internally.  Lastly I need to show you how the command classes are wired into the EventMap.

EventHandlerMap & Commands

I had written an earlier post on this but I will post it here too.  So this is kinda the tricky part about Cairngorm and Mate playing well together.  Remember we had to make our Commands dispatch events?  Well here is why.  The AsyncCommandInvoker class in Mate expects to hook into events of specific types.  Since our commands implement IResponder, why not keep it simple and just have it expect the same from the event types.

<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap
 xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" xmlns:mate="http://mate.asfusion.com/">

 <fx:Script>
 <![CDATA[

 import app.controller.commands.*;
 import app.model.*;
 import app.view.*;
 import app.view.mediators.*;

 import com.adobe.cairngorm.control.*;

 import mx.events.*;

 ]]>
 </fx:Script>

 <fx:Declarations>

 <mate:EventHandlers type="saveUser">
    <mate:AsyncCommandInvoker generator="{SaveUserCommand}" successType="result">
      <mate:successHandlers>
        <mate:PropertySetter generator="{UsersManager}" targetKey="currentUser"
           source="{scope.currentEvent.data}"/>
      </mate:successHandlers>
    </mate:AsyncCommandInvoker>
 </mate:EventHandlers>

 </fx:Declarations>

</mate:EventMap>

Notice the successType in the AsyncCommandInvoker.  This is that event we dispatched from the result method on the command.  Similarly there is a faultType which we would dispatch “fault” there.  So one question you might have is, “where is the saveUser event type getting dispatched?”.  So in this example, I had my mediator wrapping the EditUserView tie into a save button click event.  Then it dispatched an event from the view that bubbled up with the type saveUser.

Conclusion

Well there you have it, a quick mash up of how to combine the best elements from the various MVC frameworks along with some cool IoC/DI wiring from Maté.  Is it perfect?  Far from it.  It’s funny how these things come in spurts.  I hadn’t made any progress for a long time, reading up on things like Parsley, Swiz, Spring Actionscript and RobotLegs only to find that I was dissatisfied.  To tell the truth I didn’t even give these other framework a chance.  I was lazy and didn’t feel like learning something new when I knew that I could do it pretty much with Mate, Cairngorm and PureMVC.

I am psyched to try this in a bigger application at some point.  I do have a few reservations regarding Mate, specifically with respect to cached instances and all the behind-the-scenes {binding} that takes place.  How does this affect performance and memory?  The other big issues is that debugging this is a pain.  I haven’t figured out all the intricacies of Mate and if a problem occurs outside of your main actors, debugging gets difficult.  Lastly, the idea of Singletons have been removed from your MVC actors but isn’t this then just passed on to Mate in some sense?  Well not to fret but just some things to think about.

Let me know your thoughts and ideas.

Advertisements

10 thoughts on “Solution Found: Cairngorm + Maté + PureMVC

  1. Hi. Interesting idea. I am using Cairngorm as well and, like you, have found the need to extend the command with Event Dispatcher. The problem I am having with that is that due to the component heirarchy, my UI components are never notified of the dispatched event. I do have clunky workarounds for this, but was looking for a more elegant solution (without pulling in something new like Mate).

    • @Adam Preston

      Thanks for the comment. I too was on a similar search to find a better solution than what Cairngorm offered. At first I like the idea of doing something on my own but my experience with Mate from my last project was pretty good that I figured, why reinvent the wheel. It is a pretty neat and powerful framework. I understand the apprehension of pulling in another library but any professional project is bound to have numerous libraries. The really nice thing about this solution I found is that if it doesn’t work for you, most of your MVC actors are already decoupled from Mate. So refactoring would be minimal.

  2. If you take a look at Actionscript Swing, they provide a DI solution for PureMVC (and Cairngorm for that matter). That might let you achieve what you are after by just using 2 frameworks (PureMVC + parts of Swing) rather than 3 🙂

    • It should be noted that the only framework being used is Mate. I am only borrowing two design patterns popularized in AS3 by Cairngorm and PureMVC. So in reality, you need only to download Mate.

      I have read some on Actionscript Swing but I haven’t really found that the DI is implemented in a similar fashion as Mate in which it pushes through changes. Do you not still have to provide the pushing mechanism to get the data into your apps with ASSwing?

  3. Spring ActionScript lets you externalize your objects + dependencies to an XML file so that when an object is needed, the object graph is constructed and injected where you need it.

    The XML file looks like:

    Of course you can change the XML file, and thus the structure of your application very easily on the fly… do other frameworks provide this too then? (only have Maté experience)

    Thx for the interesting article!

    Greets,

    Jochen

  4. Couldn’t post the XML, though you can see what I mean over here: http://www.herrodius.com/blog/158

    Greets

  5. Thanks, useful material. Has added your blog in bookmarks.

  6. Pingback: Switch from PureMvc to Mate for Flex development (ToDoList 2010) | Rive.be | Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s