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).

12 Responses to Cairngorm Sample – How Business Logic Can Manage Views Part II

  1. My current Flex 1.5 project has a multi document interface, so we need to keep separate models for each view, as you have done. But instead of looking up the models in a single “dictionary”, we kept the models as instance properties of the view’s view helper (I know they’ve gone out of fashion, but we found view helpers to be extremely handy for many reasons) and then passed the view helper as a parameter to our commands – when the commands needed to update the view, they call methods on the view helper to set the model.
    I had considered an approach like yours, but as we already had the models in our view helpers our approach was more straightforward.
    Cheers,
    Robin

  2. AlexU says:

    Hi Robin,
    Thanks for your feedback. It’s always great to hear how others are using Cairngorm and I don’t want to claim, the way I use it above is the only way to do it.
    Using you’re approach, how do you handle it as your View Helpers get more complex? Are you extracting functionality into model / utility objects? Are you unit testing View Helpers?
    Cheers,
    Alex

  3. Tim Hoff says:

    Nice Alex. I’ve been using objects for hash maps in 2.0. I’ll have to look into changing them to Dictionary. Question: Wouldn’t the StockQuote class work as a VO class in the model as well?
    Keep it going man. Delegate, Service, periodic polling for updated stock quotes (or push). Like you need more work right? 🙂
    Thanks,
    Tim Hoff

  4. Thanx Alex !
    Any update for final version of player ?
    Or at least a direct link to source so we can make arrangments ourselves 😉
    Best,
    Jean-Luc

  5. Ok, i’ve managed to find the source.
    Here is the same appli, slightly modified for Cairngorm2 final, and FP9.
    I included CG2 in the source, you can run it as a project on Eclipse.
    http://www.netopole.com/DashboardIteration3ForCG2Final.rar

  6. AlexU says:

    Hi Tim,
    to yours and Russell’s question in the previous post “if the shown StockQuote class would also work as a Value Object (or Data Transfer Object)?”. I’d say yes, definitely. But only as long as your application doesn’t require more functionality in this specific part of your business logic. My intent with this series of articles is to demonstrate how you can manage your views with investing in model objects such as StockQuote. This way functionality can be extracted from view into unit testable model objects. This hopefully becomes clearer in later posts as StockQuote will have methods and more properties assigned to it that i.e. a VO wouldn’t be responsible for. I find often that the Flex client code is very independent of the outside world data model represented i.e. by a VO. Therefore, I often start with creating a model object that might contain a VO and is being bound to views.
    Cheers,
    Alex

  7. AlexU says:

    Hey Jean-Luc,
    thanks for your update of this example. I’ll also plan to release another iteration of the Cairngorm dashboard example soon.
    Best,
    Alex

  8. Tim Hoff says:

    Hi Alex,
    Thank you for taking the time to respond. Your answer makes perfect sense. Scope the functionality of the value object and decide if it merits becoming a class. If so, pull the related function from the viewLocator/viewHelper. That way, it can be reused anywhere. This is similar to the shopping cart in the store example.
    Thank you, very much, for the tips.
    Tim Hoff

  9. sohbet says:

    Ok, i’ve managed to find the source.
    Here is the same appli, slightly modified for Cairngorm2 final, and FP9.
    I included CG2 in the source, you can run it as a project on Eclipse.

  10. Sri says:

    Hi Alex,
    We badly need a test coverage tool for Flexunit and I noticed the article at
    http://weblogs.macromedia.com/swebster/archives/2007/11/unit_testing_an.cfm
    I appreciate if you would please let me know any information regarding it’s availability.
    Thanks so much!

  11. Alex,
    I’ve actually come across the same article. Even if it’s not yet publicly available, I’d be very interested to see some of the approaches you’re taking.

  12. Aryan says:

    How to test ECD chain using Flex unit. In all of the exaples I
    have seen the tests directly invoking the delegate. Instead How can I start from event to have a full flow of ECD (Event-Command-Delegate– result() Command—
    Using Flex Unit It should be able to have this flow. Suite – Test – Event- command – delegate – result() of Test [Not result() of command ]
    Earliest response is really appreciated.