Cairngorm Sample – How Business Logic Can Manage Views

There are many ways to update your views when your server- or client side business logic changes. The strategy you find me usually recommending is the ModelLocator strategy, which leverages the Flex binding feature. See Steven Webster’s article for more information.
Basically, your views bind to properties that are retrieved from your ModelLocator. These properties can be changed from your Commands, other business logic or other views and once changed; all listening views are updated seamlessly.
Since many of the Cairngorm examples out there are meant to be easy to understand, they often just show these properties exposed as single properties on the ModelLocator. Once your RIA grows in size, this indeed can be quite limiting. I’ll showcase another Cairngorm sample application that focuses on how you can improve your architecture with investing in slightly more advanced business logic. The sample application is a stock market dashboard that allows users to retrieve price quotes on a company stock. From time to time I’ll add features to this application, to showcase some possible ways to architect a Cairngorm application. This version of it will use Cairngorm for Flex 2 Beta 3.


Iteration 1 – The simplest thing that could possibly work
But let’s first look at the very simplest possible solution with still leveraging MVC principles advocated by Cairngorm. The stock market dashboard in its first iteration just contains a single pod with minimal UI controls. See example.
With pressing the “Get Quote” button the user dispatches a Cairngorm event that is handled by a Command instance to request a new quote.
StockMarketPod.mxml dispatches the Cairngorm event on behalf of a Button’s click event:

<mx:Button label="Get Quote" click="getQuoteForSymbol();"/>

And the corresponding mx:Script tag:

import org.nevis.cairngorm.samples.dashboard.events.GetStockQuoteEvent;
private function getQuoteForSymbol() : void
{
var event : GetStockQuoteEvent = new GetStockQuoteEvent( symbolTextInput.text );
dispatchEvent( event );
}

GetStockQuoteCommand handles the Cairngorm event and asks a business delegate class (StockMarketDelegate) for a quote.

public function execute( event : CairngormEvent ) : void
{
var symbol : String = GetStockQuoteEvent( event ).symbol;
var delegate : StockMarketDelegate = new StockMarketDelegate( this );
delegate.getQuoteForSymbol( symbol );
}

In a real world scenario this would be most likely a server side call, but for the sake of simplicity in this demo, I’ve commented the code that would be needed to call a remote service in the business delegate.
StockMarketDelegate.as just calls back to the Command immediately. Notice that if the user enters a symbol “fail”, the Command’s onFault handler will be invoked.

public function StockMarketDelegate( responder : Responder )
{
//disabled for demo
//this.service = ServiceLocator.getInstance().getService( "stockMarketDelegate" );
this.responder = responder;
}
public function getQuoteForSymbol( symbol : String ) : void
{
//disabled for demo
//var call : AsyncToken = service.getQuoteForSymbol( symbol );
//call.resultHandler = responder.onResult;
//call.faultHandler = responder.onFault;
if( symbol == "fail" )
{
responder.onFault();
}
else
{
responder.onResult();
}
}

Our StockMarketPod view just needs two information from the application’s business logic.

  • The answer to the quote (the stock price)
  • An error message, that communicates to the user if a request fails.

Because this is so simple, I have just created two properties directly on the ModelLocator.

public var lastStockQuote : Number;
public var stockQuoteError : String;

GetStockQuoteCommand can then just change these values in the onResult or onFault handler. For this demo purpose, it performs some dummy calculations to simulate the remote service result.

public function onResult( event : ResultEvent = null ) : void
{
//simulate a result from service
var stockQuote : Number = Math.random() * 50 + 5;
model.lastStockQuote = stockQuote;
model.stockQuoteError = "";
}
public function onFault( event : FaultEvent = null ) : void
{
model.lastStockQuote = NaN;
model.stockQuoteError = "An error occured.";
}

The StockMarketPod view can bind to those properties and further format the result based on its presentation needs:

<mx:FormItem label="Symbol">
<mx:Label text="{ formatQuote( model.lastStockQuote ) }"/>
</mx:FormItem>
<mx:FormItem>
<mx:Label text="{ model.stockQuoteError }"/>
</mx:FormItem>

And the formatting logic of a mx:Script block.

private function formatQuote( quote : Number ) : String
{
return ( isNaN( quote ) ) ? "" : String( quote );
}

Note that this type of code could be refactored and extracted out from the view into a unit-testable utility class.
Iteration 2 – Creating business logic that fit your needs
Now, the above example is the simplest thing that could possibly work. As your RIA grows in size this can be limiting. You may find that your ModelLocator instance is “overcrowded” with properties that you might even forget what they are needed for. You might also run into naming conflicts with other properties designed for different use cases.
A common refactoring is to create business objects that encapsulate the properties of your use case. These business objects can represent the information that your views need in a business context. You can design them at a granularity that fits your use case best. For example in a larger RIA you might want to think about a number of classes to represent your use case. Your views may then bind to these business objects or to properties of it. Your views might bind to other application objects that translate your business objects to a form that’s more useful to your views.
This way, your ModelLocator might better communicate its intent as it’s much easier for developers to grasp what business logic your application contains.
In our stock market dashboard, we could e.g. encapsulate the lastStockQuote and stockQuoteError properties in a simple business object:

package org.nevis.cairngorm.samples.dashboard.model
{
public class StockQuote
{
[Bindable]
public var lastStockQuote : Number;
[Bindable]
public var stockQuoteError : String;
}
}

Note, that in a true business object you would probably not even have a property that directly holds a String with an error message. Instead, the business object would just hold an error type and other application objects would translate that error type to e.g. a String representation, which could then be used by views.
Our ModelLocator would just define a single property representing a stock market pod view in a business context. E.g. with

public var stockQuote : StockQuote = new StockQuote ();

And our view would change to this:

<mx:FormItem label="Symbol">
<mx:Label text="{ formatQuote( model.stockQuote.lastStockQuote ) }"/>
</mx:FormItem>
<mx:FormItem>
<mx:Label text="{ model.stockQuote.stockQuoteError }"/>
</mx:FormItem>

You might want to check out the complete source (right click on the sample app and choose “View Source” or download it as a ZIP file).

9 Responses to Cairngorm Sample – How Business Logic Can Manage Views

  1. Tim Hoff says:

    Alex, this is a great example to build upon. Some of us, that only have a few months of Cairngorm experience, are interested in examples that exhibit some of the advanced business logic that you mention. From my perspective, there are a few fuzzy areas that may qualify for best practices clarification through example:
    1. Should a ViewHelper, ViewLocator, Utility Class, or Script Block (inside of the view), be used for view-related code that doesn’t merit the creation of a command? Currently, there are differing opinions concerning this topic. This may simply be a practice that relies on developer discretion. However, it would useful to establish some reasonable guidelines for making this type of decision?
    2. As an application scales up, what are some practical techniques currently used, to break-up related code into more manageable classes? Ideally, I would prefer maintaining only one ModelLocator and FrontController. However, this practice can easily lead to monolithic singleton classes. It seems that it would be useful to enhance FlexBuilder’s capabilities in this area. Perhaps, in a future release of Flex/Cairngorm, the IDE could have a built-in repository component, that would provide an organized interface for adding, changing, deleting and grouping related lines of code in the Cairngorm singletons. A similar interface would also be very useful for CSS. Maybe just allowing user-defined collapsible regions of code would suffice.
    3. If needed, what would be the recommended practice for allowing a command to directly communicate back to the view that dispatched the associated event? I know that the need for this type of communication doesn’t occur frequently. In almost every case, the command would communicate indirectly with the view, by updating the bindable properties of the ModelLocator. However, as we’ve just witnessed, it does happen. Maybe it would be useful to see an example of the ChangeWatcher utility in action.
    Please accept my apology for asking these verbose questions here. My only excuse is the understandable shortage of Cairngorm best practices and available training materials, that relate to these types of issues. Thank you very much for this example and for your regular contributions to the Flex community.
    Best Wishes,
    Tim Hoff

  2. Dominick says:

    I can understand that you placed the [Bindable] tag on each property for the sake of example. However, was there a specific reason that it wasn’t placed above the class?
    // All public properties are bindable
    [Bindable]
    public class StockQuote {
    btw, Great article!

  3. AlexU says:

    Hi Tim,
    Thanks for your great feedback. I’ll keep that in mind for future blog entries. Shortly to your first question, though: I’d always try to extract as much as possible view code into a unit testable, reusable utility class. This has a way higher priority than any other of your mentioned alternatives. However, if that’s not feasible in a particular case, I find there’s often not too much difference in either of the alternatives since they would all be still live in a view context. Often, the MXML Script tag does just fine for this type of code. But I also find there are slight differences and I’ll plan to write more about this and around your other topics later on. Also, keep eyes on Paul’s blog regarding binding. 😉

  4. AlexU says:

    Hi Dominick,
    Thanks for your feedback as well. In that particular case you’re right that the Bindable metadata could have well be placed on a class level. Just keep in mind that adding Bindable metadata does have slight effects on performance, since it’s generating event dispatching code behind the scences. But then, I think in most use cases you’d probably never notice a difference.
    However, I think for maximum performance but also to better express the intend of your code you should only add class level Bindable metadata if you’re using most or every property for bindings as a ModelLocator would. A business object however, would often not have all or most properties needed to be bindalbe.

  5. Hi Alex
    Thanks for a great article. In your follow article Tim Hoff made a comment that got me thinking about what you wrote hear: “Wouldn’t the StockQuote class work as a VO class in the model as well?”
    I realised that I (in my inexperience) had been using my VO’s as business object by creating Bindable variables in the VO’s. Having one object (combined business object & VO) seem to work great for me, but I have a sneeking suspision I’m missing a point somewhere. Can you tell me if this combined approch is cosher?
    Thanks in advance 🙂

  6. AlexU says:

    Hi Russell,
    thanks for your feedback. To your question please check my reply to Tim’s original question on blog entry forward.
    Best,
    Alex

  7. Thanks for taking the time to respond to my question Alex, much appreciated 🙂

  8. bala says:

    how to write business logic in java class