Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / Flex Developer Center /

Developing Flex RIAs with Cairngorm microarchitecture - Part 4

by Steven Webster

Steven Webster
  • Adobe Consulting
  • www.richinternetapps.com

by Leon Tanner

Leon Tanner
  • Adobe Consulting

Content

  • Engaging in rich conversations
  • The Service to Worker microarchitecture
  • Service to Worker: mapping user gestures and system occurrences to events
  • Service to Worker: listening for events with the front controller
  • Service to Worker: broadcasting Cairngorm events

Modified

16 June 2008

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
Flex

Requirements

Prerequisite knowledge

Read the previous articles in the series, starting with Developing Flex RIAs with Cairngorm Microarchitecture – Part 1: Introducing Cairngorm before reading Part 4.

User level

Beginning

Required products

  • Flex Builder 3 (Download trial)

Additional Requirements

Cairngorm version 2.2

  • Download Cairngorm version 2.2

Cairngorm Store

Throughout the series you will find references to code taken from an e-commerce application named Cairngorm Store. You may use this sample application to gain a better understanding of Cairngorm, but please consider it only as a guide. Adobe is not responsible for maintaining the Cairngorm Store application.

  • Download Cairngorm Store (J2EE Container version)
  • Download Cairngorm Store (non J2EE Container version)

Part 3 of this series explained how to construct the user interface of an enterprise rich Internet application (RIA) using MXML and ActionScript. Most importantly, it explored an area of application development where Cairngorm imposes itself least—and where it provides the greatest degree of flexibility. After all, it is the user experience that differentiates RIAs from traditional web applications, and any attempt to impose a structure on constructing the view limits the richness of the application.

If an RIA is a conversation between man and machine, then Cairngorm is the interpreter. So far, we have described how Cairngorm enables the machine to talk to the user as it renders the application's state in a dynamic, visual way. Now consider the more important aspect of communication: listening (rather than talking). In this article, we explore the design patterns that Cairngorm uses to listen to the user. we describe how Cairngorm understands user gestures to determine what the user wishes to do in the application. As with Parts 2 and 3, this article will reference samples from the Cairngorm Store application.

In the Cairngorm Store, a user can browse a product catalog, select products, view details on selected products, drag products into a shopping cart, and purchase the products through a checkout process. Cairngorm encourages you to breathe interactivity into rich and immersive experiences, and it enables your application to respond to your users' every gesture, whether through a mouse click or keyboard stroke.

This article explores the most fundamental and innovative patterns of the Cairngorm microarchitecture: the Front Controller, Event Broadcaster, and Command patterns. Understanding how they collaborate will help you to rapidly and consistently develop RIAs with Flex.

  • Part 1: Introducing Cairngorm
  • Part 2: Keeping state on the client
  • Part 3: Architecting the view
  • Part 4: Feature-driven development
  • Part 5: Server-side integration
  • Part 6: Rapid and consistent development with Cairngorm and Flex

Engaging in rich conversations

When we talk about rich Internet applications, we understand the word rich to describe a user experience that is dramatically superior to the experience provided by a traditional web application. In addition there is a richness in the level of interactivity in the application. Interactivity in this case does not mean animation and effects, but rather the extent to which a user can engage with the application as it engages with the user.

Consider a conversation through a translator. As one person speaks, the translator listens and then translates a part of the conversation to a third party. Meanwhile, the speaker waits while the third party responds, also through the translator. This inefficient communication process repeats itself between the two parties.

This is analogous to a web application, which also enables a kind of conversation between man and machine. The web application translates, through the limited medium of a browser, to the user. Then the user clicks a button to communicate back to the web application, also through a browser, continuing a less than optimal process for communication.

A principal benefit of rich Internet applications—beyond the obvious rich user experience—is the rich communication that can occur between man and machine. Freed from the limits of the request/response page-driven browsing metaphor, users can interact at their own pace with the application while the application responds—also at its own pace—to the user. Communication flows better, creating simpler, easier, more effective and, ultimately, more enjoyable user experiences.

One of the key innovations in Cairngorm leverages a collaboration of design patterns that the J2EE community used to centralize and manage the man/machine communication in the traditional web application world and brings it up to date for the more seamless, flowing conversations that rich Internet applications enable. These design patterns work so well together at solving the higher level problem of achieving rapport between user and application that they are considered their own microarchitecture: the Service to Worker microarchitecture.

The Service to Worker microarchitecture

This article describes the three most important patterns in a Cairngorm application, namely the Front Controller, Event Broadcaster, and Command patterns. These patterns make up the Service to Worker microarchitecture.

Rich Internet applications have grown in complexity due to the number of features required in the application. Consider the following features of the Cairngorm Store:

  • Get products from database
  • Sort products by name or price
  • Filter products according to price slider
  • Add product to shopping cart
  • Remove product from shopping cart
  • Check out an order

In this case, there are six features, requirements, use cases, or stories, depending on the methodology you follow (and terminology you use) for capturing business requirements. For a real-world rich Internet application, the total number of features can easily increase from fewer than 10 to 50 or more. In fact, the Adobe Consulting team has consulted on and developed applications that have hundreds or thousands of these features, delivered by teams of 20 to 30 developers.

As the feature list grows, so too does the scale and complexity of the rich Internet application. Cairngorm shows its true value when applied to these large-scale applications, because it helps teams simplify the inherent complexity of enterprise RIA development.

Service to Worker: mapping user gestures and system occurrences to events

The user accesses the vast majority of features through user gestures. Gestures are made through the mouse or keyboard, for example, by clicking a button, double-clicking an entry in a data grid, dragging and dropping an object, or submitting a form.

Consider the Cairngorm Store features listed previously. All of them except the requirement to get products from the database occur in response to a user gesture. Here are some examples of user gestures:

  • User sorts products by clicking a button
  • User drags a slider component to filter products
  • User adds products to the shopping cart by clicking an Add to Cart button or by dragging and dropping an image into the shopping cart

More often than not, you map the execution of a feature to a user gesture.

If you come from a web application development world, you will likely see each feature as requiring an HTTP request to the server, with the server doing some of the work on your behalf and returning data to the client, most often with a browser refresh.

With an RIA, a feature is not always instigated by a user gesture; for example, the fetching of the products from the database is done immediately when the application starts. Rather than resulting from a user gesture, getting the products from the database is an event that happens within the system. Other such system-level events might include a feature that executes periodically, such as fetching e-mail messages every 30 seconds.

The single biggest innovation in Cairngorm is that the Cairngorm framework treats user gestures and system-level events the same way by mapping them to a Cairngorm event. Breaking free of the request/response paradigm of HTML-based technologies, user requests are no longer synonymous with HTTP requests. Instead, user requests are internal events that components can broadcast when they recognize a user gesture or system event.

Service to Worker: listening for events with the front controller

In the Cairngorm Store, the features mentioned previously map to events such as getProducts, addProductToShoppingCart, deleteProductFromShoppingCart, filterProducts, sortProducts, and so forth. Whenever a user gesture indicates the desire to execute a feature, Cairngorm requires that you broadcast an appropriate event.

In response to each event, we have mentioned executing a feature. A common design pattern from the original Gang of Four pattern catalog is an excellent choice in such a scenario. In the Front Controller pattern, you implement features as classes, called commands. Each and every command exposes a single entry point, a method called execute(), which allows a third party to invoke the command without understanding what the command accomplishes. Often these commands are called worker classes because they are responsible for actually carrying out the work behind the application.

Understanding Command classes

Within Cairngorm, adding a new feature to the application typically involves adding a new Command class that can accomplish the work associated with the feature.

Consider one of the key items from the Cairngorm Store feature list—the ability to add products to a shopping cart. To implement this feature, you can create a new Command class called AddProductToShoppingCartCommand. The code for this class is as follows:

package com.adobe.cairngorm.samples.store.command { import com.adobe.cairngorm.commands.ICommand; import com.adobe.cairngorm.control.CairngormEvent; import com.adobe.cairngorm.samples.store.model.ShopModelLocator; import com.adobe.cairngorm.samples.store.event.UpdateShoppingCartEvent; public class AddProductToShoppingCartCommand implements ICommand { public function execute( event : CairngormEvent ): void { var shoppingEvent : UpdateShoppingCartEvent = UpdateShoppingCartEvent( event ); ShopModelLocator.getInstance().shoppingCart.addElement( shoppingEvent.product, shoppingEvent.quantity); } } }

As you can see, it is not complicated. First of all, notice that the application-specific Command class implements the Cairngorm Command interface. If you were to view the source code for Cairngorm, you would see that this interface simply enforces that the command must have a method called execute() that acts as its entry point. This allows Cairngorm to execute each and every command without caring about what the command actually does.

If you look at the implementation of the execute() method, you can see how the event that caused Cairngorm to execute the command contains a ProductVO value object (shoppingEvent.product) and a quantity (shoppingEvent.quantity) in the data payload of the CairngormEvent class. The CairngormEvent class is also a Cairngorm class that gives each Cairngorm event a type (such as the addProduct type) and some associated data (such as the product and the product quantity to add to the shopping cart).

Because the shopping cart is a client-side state, it resides on the ShopModelLocator class. Therefore, the command has only to carry out the work that the user requests and add it to the appropriate amount for the product in the shopping cart, using the methods on the ShoppingCart class.

That's it! There's minimal work required to create a simple feature of the Command class. The command queries the event, extracting any data associated with the event so that it can perform its task. If executing the command affects the state of the application—such as requiring a new product to appear in the Shopping Cart view, for instance—the application accomplishes the task by updating the client-side state through ShopModelLocator. If you bind views correctly to ShopModelLocator, the user interface will reflect these state changes accordingly.

Creating helper classes in Cairngorm

There is a very important design point to emphasize here. In the preceding example, all complex business logic associated with what a shopping cart can and cannot do is encapsulated in a class called the ShoppingCart class. If a user adds a product to the cart, for instance, the ShoppingCart class adds it to the cart if it is not already in the cart. If the product already exists in the cart, the class increases the product's quantity.

Cairngorm does not relieve developers of the responsibility of creating their own business objects and classes on the client. Indeed, such classes make the application specific to its business domain.

Developers should always seek to extract these classes from the Cairngorm architecture. Pulling business logic out of commands and encapsulating into classes is a classic implementation of Extract class refactoring. The beauty of this technique is that you can perform extensive unit tests on these classes, document their APIs, and make them available for reuse to other application developers without any dependency on the surrounding Cairngorm application.

Managing commands with a controller

In the real world, having a large collection of workers with no one in charge is a recipe for chaos. Delegating responsibility to someone to take charge and control the workers from the front ensures that workers do their duty when they're required to do so.

In Cairngorm, the Front Controller pattern performs a similar function. Borrowing a design pattern from the J2EE community (but implementing it to respond to events on the client rather than HTTP requests on the server) Cairngorm introduces the Front Controller pattern as a single point of entry for all Cairngorm events.

Let's take a look at the Front Controller pattern from Cairngorm Store as an example: package com.adobe.cairngorm.samples.store.control { import com.adobe.cairngorm.control.FrontController; import com.adobe.cairngorm.samples.store.command.* import com.adobe.cairngorm.samples.store.event.UpdateShoppingCartEvent; import com.adobe.cairngorm.samples.store.event.FilterProductsEvent; import com.adobe.cairngorm.samples.store.event.GetProductsEvent;; import com.adobe.cairngorm.samples.store.event.SortProductsEvent; import com.adobe.cairngorm.samples.store.event.ValidateOrderEvent; import com.adobe.cairngorm.samples.store.event.ValidateCreditCardEvent; import com.adobe.cairngorm.samples.store.event.PurchaseCompleteEvent; public class ShopController extends FrontController { public function ShopController() { initialiseCommands(); } public function initialiseCommands() : void { addCommand( GetProductsEvent.EVENT_GET_PRODUCTS, GetProductsCommand ); addCommand( UpdateShoppingCartEvent.EVENT_ADD_PRODUCT_TO_SHOPPING_CART, AddProductToShoppingCartCommand ); addCommand( UpdateShoppingCartEvent.EVENT_DELETE_PRODUCT_FROM_SHOPPING_CART, DeleteProductFromShoppingCartCommand ); addCommand( FilterProductsEvent.EVENT_FILTER_PRODUCTS, FilterProductsCommand ); addCommand( SortProductsEvent.EVENT_SORT_PRODUCTS, SortProductsCommand ); addCommand( ValidateOrderEvent.EVENT_VALIDATE_ORDER, ValidateOrderCommand ); addCommand( ValidateCreditCardEvent.EVENT_VALIDATE_CREDIT_CARD, ValidateCreditCardCommand ); addCommand( PurchaseCompleteEvent.EVENT_COMPLETE_PURCHASE, CompletePurchaseCommand ); } } }

First, notice the code follows the best-practice approach of naming all events as constants. A common development error is to set up the controller to listen for an event, for example addProductToShoppingCart, but then, when broadcasting the event, mistakenly refer to it by another name, for example, addProduct. Consequently, you think you have broadcast a valid event and caused a Command class to execute; instead, FrontController refuses to invoke any commands and does not recognize the event name. These errors manifest at runtime only, as you are broadcasting events. In contrast, when you use static constants the compiler catches any mistyped events noisily at compile time, rather than failing silently at runtime. To catch such errors early, it is a best practice to use a static constant within each CairngormEvent class to identify the event within the FrontController. The addCommand statements in the code above are examples of this practice.

Next, notice how the constructor calls the initialiseCommands() method in the controller, ensuring that when the application does create the controller, it creates it with the full knowledge of which commands it can delegate work to, depending on which events it hears being broadcast.

Consider the example of adding products to the shopping cart. When the application broadcasts the UpdateShoppingCartEvent .EVENT_ADD_PRODUCT_TO_SHOPPING_CART command, the following entry in the controller ensures that the execute() method on the AddProductToShoppingCartCommand is invoked:

addCommand( UpdateShoppingCartEvent .EVENT_ADD_PRODUCT_TO_SHOPPING_CART, AddProductToShoppingCartCommand );

By extending the base FrontController class in the Cairngorm framework, you can use the addCommand() method of the FrontController class to register events with corresponding Command classes. The underlying Cairngorm architecture does the rest of the work. It simply broadcasts the appropriate event from anywhere in your application and Cairngorm ensures that the relevant command is invoked.

Also, notice how the Command classes are married to the events in the Front Controller via the addCommand method. There is no need to manually instantiate the Command class here, these "stateless commands", have the subtle benefit of ensuring a new instance of the Command is instantiated on the event call.

You create your controller in the main entry point for MXML, use the following code:

<control:ShopController id="controller" />

This is all you need to do to ensure that your application has a Front Controller pattern that responds to all the events and invokes all the commands that you registered with the addCommand() method.

Service to Worker: broadcasting Cairngorm events

The final step involves broadcasting the events to which the controller responds.

Cairngorm events can be distinguished from events raised by the underlying Flex framework, by extending the CairngormEvent class. When registering an event in the Front Controller, it is expected that only Cairngorm events are registered.

Earlier versions of Cairngorm featured a singleton EventBroadcaster class (CairngormEventDispatcher) that collaborated with the Front Controller class. In the current version, this mechanism has been replaced with self-dispatching events. Each Cairngorm Event class has a dispatch() method allowing a new instance of an event to be created and dispatched via the EventBroadcaster. The following code is an example of dispatching a Cairngorm Event:

new GetProductsEvent().dispatch();

Consider how a user might perform a user gesture to add products to a shopping cart. The user could click the Add to Cart button on a product details screen or drag a product image to the cart. The application needs to translate each of these user gestures into a Cairngorm Event encapsulating the event information. So on the product details view component, a drag and drop action calls a helper method used to broadcast the event:

public function doDragDrop( event : DragEvent ) : void { var item : Object = event.dragSource.dataForFormat( "item" ); ... addProductToShoppingCart( ProductVO( item ) ); ... } public function addProductToShoppingCart( product : ProductVO ) : void { var quantity:Number = 1; new UpdateShoppingCartEvent( product, quantity ).dispatch(); } ... <mx:DataGrid id="dataGridComp" dataProvider="{ shoppingCart.elements }" width="100%" height="100%" dragEnter="doDragEnter( event )" dragExit="doDragExit( event )" dragDrop="doDragDrop( event )" dragOver="doDragOver( event )"> ... </mx:DataGrid>

This is an example of another best practice Adobe Consulting recommends: Provide simple methods that describe the broadcasting of an event in business terms. With those few lines of code, you can now entrust Cairngorm to ensure that AddProductToShoppingCartCommand is invoked, doing all the work necessary to update the client-side model through ShopModelLocator, which will also result in the ShoppingCart view (which binds to the ShoppingCart model in ShopModelLocator) updating to show the new state of the shopping cart.

Putting it all together, you can see how the Service to Worker microarchitecture (including the Front Controller, Event Broadcaster, and Command patterns) collaborates with the Model Locator and Value Object patterns to handle an entire conversation between the user and the rich Internet application.

Where to go from here

Through Parts 2, 3, and 4 of this series you've seen much of the Cairngorm framework. Regardless of how you choose to build rich and immersive user interfaces using MXML and ActionScript, Cairngorm plays an important role in listening to the user's interaction with these experiences, translating these interactions into an understanding of what the user expects, and executing work on the user's behalf.

Cairngorm helps to achieve this goal with a clean collaboration of design patterns: Front Controller, Event Broadcaster, and Command. These patterns form a tight microarchitecture called Service to Worker.

Every time you add a feature to the application, do so by creating a specific Command pattern, such as AddProductToShoppingCartCommand. This command performs the work associated with the user gesture or system event that caused the command to execute. For Cairngorm to execute this command, you use the addCommand() method in FrontController to register each Command pattern with an appropriate event name. Furthermore, it pays to follow a best practice of identifying your events as static constants so that you can benefit from compile-time checking of your event broadcasts, rather than trying to debug silent runtime failures when you mistype event names.

We've shown how Cairngorm provides a base class for your Front Controller patterns and an interface for your Command patterns. If you extend and implement these classes, you can broadcast self-dispatching Cairngorm Events from anywhere in your application to translate user gestures or system-level events into events that cause Cairngorm to pass control to the appropriate Command pattern.

This initial foray into application development may cause you to pause and reflect on the flow of control between different parts of the application architecture. As you become comfortable with this flow, however, you and your team will approach the addition of each and every feature to a Cairngorm application in the same way. In short, you will add an event to the controller, register it with a Command class, and implement the Command class to do all the work. With this approach you can drive the development of your application with feature after feature, in a consistent, predictable manner that scales well. With Cairngorm, your business logic is no longer scattered across an ever-growing ecosystem of MXML components. Gone, too, are the monolithic classes that state "if the application just did this, then do that; but if it did this, then do the next thing..."

The scalability of design, maintainability of the code base, and the simple, consistent approach to delivering new features into the application highlight the major benefits of using the Cairngorm microarchitecture.

For the rich part of rich Internet application, we've shown how to implement the view and how to keep state on the view. For the application part of rich Internet application, we've shown how to implement features with the addition of business logic encapsulated in commands.

In Part 5, we'll discuss the Internet in rich Internet application. You'll learn how to complement the richness of the user experience with the ability to reach out and invoke business logic that exists on a server—whether it's your own application server or third-party services exposed on third-party servers. With this final piece of the puzzle, you'll have everything you need to know to design, architect, and deliver your own highly complex enterprise rich Internet applications.

Developing Flex RIAs with Cairngorm Microarchitecture – Part 5: Server-Side Integration

Tutorials & Samples

Tutorials

  • Flex mobile performance checklist
  • Flex and Maven with Flexmojos – Part 3: Journeyman
  • Migrating Flex 3 applications to Flex 4.5 – Part 4

Samples

  • Twitter Trends
  • Flex 4.5 reference applications
  • Mobile Trader Flex app on Android Market

Flex User Forum

More
07/25/2011 Flash Player Debug Issues - Safari 5.1 & Chrome 13
04/22/2012 Loader png - wrong color values in BitmapData
04/22/2012 HTTPService and crossdomain.xml doesn't work as expected
04/23/2012 Memory related crashes in Flex application

Flex Cookbook

More
04/06/2012 How to detect screen resize with a SkinnableComponent
02/29/2012 Embed Stage3D content inside Flex application components
02/15/2012 Custom WorkFlow Component
02/09/2012 Using Camera with a MediaContainer instead of VideoDisplay

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement