Check out our Angular Book Series.

Learning Cairngorm ( Part 5)

Note: The alias link to this post is now working. I had to refresh the blog cache for the date to update. Not sure how this will affect aggregators

In the fifth part of this series on Cairngorm, I'm going to talk about how Cairngorm handles remote data calls. This, once again, is following along with the fifth part of Steven Webster's Adobe Center Articles.

A Recap

In the last article I wrote in this series I spoke about how Cairngorm handles user gestures. A user gesture is anything that the user will do to interact with your application. This is the general procedure:

  • Dispatch a custom event (AKA User Gesture).
  • The Event goes to a controller and figures out what command to execute.
  • Execute the Command
  • Command updates Model data, which updates the user views

We are missing is getting live data, probably from a database, using some intermediary software like ColdFusion (my choice) or PHP. (Or Java or Ruby, or .NET, or whatever).

3 Types of Services, one Location

There are three ways to get data from within Flex. You can use an HTTPService, a WebService, or a RemoteObject. Based on their names, it is probably pretty obvious what each tag does (Right?). My preferred method is RemoteObject because it ties into ColdFusion so nicely, but I've also done some work with HTTPService for integrating with remote systems beyond my control. Cairngorm uses a ServiceLocator to help you find services. This is, conceptually, not much different than a Model Locator. It is a singleton class that allows you to store your services in a central location, and access them anywhere from the application. This would be a sample ServiceLocator:


<cairngorm:ServiceLocator
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:cairngorm="http://www.adobe.com/2006/cairngorm">


<mx:HTTPService id="sampleHHTTPService" url="http://mysite.com/myservice" method="POST" resultFormat="xml" contentType="application/xml" />

<mx:RemoteObject id="sampleRemoteObject" destination="ColdFusion" source="com.dotcomit.service" />

<mx:WebService id="sampleWebService" wsdl=" http://mysite.com/myservice.wsdl" />

</cairngorm:ServiceLocator>

I have three (fake) services, one of each type. If you've used any of these tags before, you'll notice that I left out the 'result' and 'fault' events. Result and Fault methods are known as responder methods. They get called when the remote service sends data back to Flex. Cairngorm puts the responder methods in the Command, and calls the Service using a Delegate.

Delegates

A Delegate is a class that does nothing but call the service. Here is a simple example:


package com.tempApp.fooDelegate{

public class MyCustomDelegate{
private var responder : IResponder;
private var service : Object;

public function MyCustomDelegate ( responder : IResponder )
{        
this.service = ServiceLocator.getInstance().getRemoteObject( " sampleRemoteObject " );
this.responder = responder;
}
}

public function getFoo ([foo arguments]):void{
var call : Object = service.getFoo([foo arguments]);
call.addResponder(responder);
}
}

Cairngorm keeps the delegates and the services file in a business folder. I guess this ties into "Business Logic". Really this is just a layer to reach the backend business logic, though. I don't do a lot of work in the delegates. They just make calls. To me it makes sense to do data parsing in the responder, and as such in the command. ( Although this approach has been contested ). We can modify our command, Part 4, to handle make the delegate call.


package com.tempApp.command{

import com.adobe.cairngorm.control.CairngormEvent;
import com.adobe.cairngorm.commands.ICommand;
import com.tempApp.event. MyCustomEvent
import com.tempApp.business. MyCustomDelegate
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;

public class MyCustomCommand implements ICommand
, IResponder{

public function execute(event:CairngormEvent):void{
var e : MyCustomEvent = event as MyCustomEvent;
var delegate : MyCustomDelegate = new MyCustomDelegate ( this );
delegate.getFoo([instance variables from event]);
}

public function result( event : ResultEvent) : void
{
[do stuff with results]
}

public function fault( event : FaultEvent ) : void
{
[Do error processing]
}

}
}

The first change is that the code implements an IResponder interface in addition to the ICommand. The second change is that the execute method actually creates an instance of the delegate (passing in the instance of this object), and calls a method on the delegate. This modified class also includes a result event and a fault event. These are the responder methods called by the service when results are returned. Most likely the result event will be performing some actions on the ModelLocator, which will change some other aspect of the app via data binding.

A recap of Today

So, our updated process is this:

  • Dispatch a custom event (AKA User Gesture).
  • The Event goes to a controller and figures out what command to execute.
  • Execute the Command
  • Command creates instance of Delegate.
  • Delegate gets Service Object from Service Locator
  • Delegate calls the Service Object Method
  • Remote Service does stuff and sends data back to the respond methods in the Command
  • Command updates Model data, which updates the user views

A few final thoughts

I've spoken to some people who claim that Cairngorm must use a one-to-one ratio with Events, Commands, and Delegates. However, I do not implement code in such a manner. It seems to add an extra layer of complexity. I often have one delegate per service, and that delegate is used by many commands. Since the delegate does not do anything but call the remote service, it's a really small file. I can easily deal with multiple remote method calls in the same file.

It's a bit late, so if this is weird, it's because I'm proof-reading while tired.

On Friday (Tomorrow when you read this), I plan to start releasing the collection of articles from my CF101 column over at sys-con. They should be easy lunch time reading.

Related Blog Entries

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Oliver Merk's Gravatar Dude, its *Steven* Webster...;)
# Posted By Oliver Merk | 11/2/07 3:04 PM
Jeffry Houser's Gravatar Thanks, fixed; That must be a cause of proof reading while tired. ;)
# Posted By Jeffry Houser | 11/2/07 4:35 PM
Steven Webster's Gravatar Jeffry; no need to apologise to Oliver, he can't spell my old company name......ask him :)

On delegates; to me there should be a one:many relationship between commands and delegates, ie many commands should invoke the services of one delegate. One delegate per event is just an extra layer of hierarchy you don't need, to be delegates group related service calls together, and or more often than not facades to equivalent server-side facades. So think about "CustomerDelegate", "AuthenticationDelegate", "ShoppingCartDelegate", etc.
# Posted By Steven Webster | 11/6/07 11:02 AM
Jeffry Houser's Gravatar Steven,

No argument from me. It makes a lot of sense. And that is the way I work. Thanks for reading.
# Posted By Jeffry Houser | 11/6/07 11:13 AM
Oliver Merk's Gravatar @Steven: That's cold ;)
Good thoughts on delegate organization. I'll add it to the upcoming Cairngorm courseware (which now spells iteration::two correctly).
Cheers,
Oliver
# Posted By Oliver Merk | 11/6/07 12:18 PM
Sudha's Gravatar Thanks a lot Jefry
I had read about cairngorm, but this article made the concepts so clear
It's really a great one
# Posted By Sudha | 11/22/07 7:39 AM
Jeffry Houser's Gravatar Thanks for reading; I'm glad it helped!
# Posted By Jeffry Houser | 11/22/07 9:38 PM
Alen's Gravatar I have a small issue with this:
this.service = ServiceLocator.getInstance().getRemoteObject( " sampleRemoteObject " );

Wouldn't it be better like this:
this.service = ServiceLocator.getInstance().getRemoteObject( ServiceLocator.SAMPLE_REMOTE_OBJECT );

Otherwise, thanks Jeff. Really good addition to 6part article.
# Posted By Alen | 4/24/08 12:03 AM
Alen's Gravatar Sorry, that should be:

Services.SAMPLE_REMOTE_OBJECT
# Posted By Alen | 4/24/08 12:42 AM
Jeffry Houser's Gravatar Alan,

Interesting. How would you change the ServiceLocator to support this?
# Posted By Jeffry Houser | 4/24/08 9:57 AM
All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.