Cairngorm Sample – How Business Logic Can Manage Views Part II

In the last post I’ve introduced you to the stock market dashboard sample application and how you can create business logic to manage your view. In this post and example, I’ll apply another use case to the sample application. With adding multiple pods to the stock market dashboard, the user can request price quotes independently. Each pod has its own stock market data. I’ll cover how your business logic can be adjusted to manage each view. I’ll also touch on how stateless Commands can help making this application easier.


Iteration 3 – Creating business logic that manages multiple views
Often, I find use cases where many views of a same type share common functionality. Each view can be represented by a model object. The view can request the appropriate model object from a manager object at start up. For example the StockMarketDashboard.mxml would look like this with additional pods:

<view:StockMarketPod
quoteId="quote1"
title="Stockmarket first pod"/>
<view:StockMarketPod
quoteId="quote2"
title="Stockmarket second pod"/>
<view:StockMarketPod
quoteId="quote3"
title="Stockmarket third pod"/>
<view:StockMarketPod
quoteId="quote4"
title="Stockmarket fourth pod"/>

Notice that the view needs to send some form of unique identifier to the manager. We add this unique identifier to our StockQuote class.

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

The manager object StockQuoteManager, is declared at initialization on the ModelLocator instance. But how does this manager object look like?
Usually, I implement it with a hash map that is keyed by the view’s unique identifier. In the constructor we can initialize the hash map. In Flex 1.x we would have used a generic Object to create a hash map. In Flex 2, I like to use flash.utils.Dictionary, which more clearly shows the indent of the code.

public function StockQuoteManager()
{
stockQuotes = new Dictionary();
}

The getStockQuote method either creates or returns a specific StockQuote object based on the view’s unique identifier quoteId.

public function getStockQuote( quoteId : String ) : StockQuote
{
var key : String = quoteId;
if( stockQuotes[ key ] == null )
{
var stockQuote : StockQuote = new StockQuote();
stockQuotes[ key ] = stockQuote;
}
return StockQuote( stockQuotes[ key ] );
}

The StockMarketPod.mxml almost looks identical to the old version of iteration 2, which refactored ModelLocator properties to the StockQuote model object. We just need to send the unique identifier to the Command, since the Command itself needs to retrieve a StockQuote object from the manager object. So the GetStockQuoteEvent class gets another quoteId property.
In GetStockQuoteCommand we need to store the unique identifier as an instance property since the command needs to retrieve and update the correct StockQuote object in an onResult or onFault handler.

private var model : ModelLocator = ModelLocator.getInstance();
private var quoteId : String;
public function execute( event : CairngormEvent ) : void
{
var stockQuoteEvent : GetStockQuoteEvent = GetStockQuoteEvent( event );
var symbol : String = stockQuoteEvent.symbol;
quoteId = stockQuoteEvent.quoteId;
var delegate : StockMarketDelegate = new StockMarketDelegate( this );
delegate.getQuoteForSymbol( symbol );
}
public function onResult( event : ResultEvent = null ) : void
{
//simulate a result from service
var lastStockQuote : Number = Math.random() * 50 + 5;
var stockQuote : StockQuote = model.stockQuoteManager.getStockQuote( quoteId );
stockQuote.lastStockQuote = lastStockQuote;
stockQuote.stockQuoteError = "";
}
public function onFault( event : FaultEvent = null ) : void
{
var stockQuote : StockQuote = model.stockQuoteManager.getStockQuote( quoteId );
stockQuote.lastStockQuote = NaN;
stockQuote.stockQuoteError = "An error occured.";
}

Also, notice that in the example above we make use of stateless commands. Since the introduction of stateless commands in Cairngorm, new command instances are being created each time a Cairngorm event is dispatched (see Alistair McLeod’s blog entry). You can now securely save instance properties in your commands without fearing that the instance property will be modified by another Cairngorm event. Would your command not be stateless, our quoteId could be overwritten by a quoteId from another stock pod. When the command’s onResult is triggered by the incoming response from the first remote service request, the response would map to a wrong StockQuote object. But thankfully, we don’t need to think about that anymore. ;)
Spinning it a wee bit further.
Notice, that your manager object could contain other functionality that applies to all pods. Think about a domain model that suits your use case best.
Furthermore, in a slightly more elaborate use case it might be significant that the onResult handler in GetStockQuoteCommand requests the currently up-to-date StockQuote object from the manager object as it does in the example shown. While the command is waiting for a response for a request to a remote service, the state of the RIA could change. When the response comes in, the manager object might want to return a different StockQuote object or not a StockQuote object at all.
For example, the end user might be able to close certain stock quote pods. In this case, its representing StockQuote model object may also have been deleted. On subsequent requests to the StockQuote object, the manager object might want to throw an exception. This exception might be handled differently by the command.
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).