cairngorm guidelines - ufpecin.ufpe.br/~hama/arquivos/cairngorm guidelines.pdf · flex sdk in...

29
Cairngorm Guidelines This page collects together the guidelines and best practices established by Adobe Technical Services, the Cairngorm Committee and our partners. It is presented in three parts: 1. Introduction 2. Architecture Guidelines 3. Best Practices The introduction explains what Cairngorm is and how to begin applying it. The guidelines describe our preferred client-side architecture in detail. The best practices are simple lists of recommendations, ready to be handed to development teams. 1. Introduction Cairngorm covers a lot of ground. These sections introduce the main features and describe how to get started with Cairngorm before diving into the more detailed guidelines and best-practices. Getting Started with Cairngorm Overview of the Cairngorm Architecture A Simple Sample Application Explained A Modular Sample Application Explained 2. Architecture Guidelines Cairngorm recommends an architecture that arranges code into layers with different kinds of responsibilities. The following guidelines explain the architecture in detail: Presentation Layer Application Layer Domain Layer Infrastructure Layer Modular Development Options in Loose Coupling Options to Construct, Retrieve and Isolate Objects 3. Best Practices It's important to make best use of the features provided by the Flex SDK. These best practice documents make that easier: Best Practices for Agile Unit Testing Best Practices for Performance Best Practices for Logging Genuinely Reusable Presentation Components Quality Guidelines Getting Started With Cairngorm 3 The best way to get started with Cairngorm depends on your own background. Are you a new comer, an old timer or a visitor? New Comer - Cairngorm is new to you. How do you even pronounce it? Old Timer - You're already skilled in Cairngorm 1 & 2, so what's new? Visitor - You prefer to use another framework, but maybe Cairngorm 3 has something to offer?

Upload: others

Post on 26-Jun-2020

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Cairngorm Guidelines

This page collects together the guidelines and best practices established by Adobe Technical Services, the Cairngorm

Committee and our partners. It is presented in three parts:

1. Introduction

2. Architecture Guidelines

3. Best Practices

The introduction explains what Cairngorm is and how to begin applying it. The guidelines describe our preferred

client-side architecture in detail. The best practices are simple lists of recommendations, ready to be handed to

development teams.

1. Introduction

Cairngorm covers a lot of ground. These sections introduce the main features and describe how to get started with

Cairngorm before diving into the more detailed guidelines and best-practices.

Getting Started with Cairngorm

Overview of the Cairngorm Architecture

A Simple Sample Application Explained

A Modular Sample Application Explained

2. Architecture Guidelines

Cairngorm recommends an architecture that arranges code into layers with different kinds of responsibilities. The

following guidelines explain the architecture in detail:

Presentation Layer

Application Layer

Domain Layer

Infrastructure Layer

Modular Development

Options in Loose Coupling

Options to Construct, Retrieve and Isolate Objects

3. Best Practices

It's important to make best use of the features provided by the Flex SDK. These best practice documents make that

easier:

Best Practices for Agile Unit Testing

Best Practices for Performance

Best Practices for Logging

Genuinely Reusable Presentation Components

Quality Guidelines

Getting Started With Cairngorm 3

The best way to get started with Cairngorm depends on your own background. Are you a new comer, an old timer or a

visitor?

New Comer - Cairngorm is new to you. How do you even pronounce it?

Old Timer - You're already skilled in Cairngorm 1 & 2, so what's new?

Visitor - You prefer to use another framework, but maybe Cairngorm 3 has something to offer?

Page 2: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

New Comer

Named after a mountain range in the eastern Highlands of Scotland, Cairngorm is a resource provided by the Adobe

Technical Services Organisation to developers and technical architects to deliver successful Flex projects in the

enterprise. Cairngorm is pronounced kern-ˌgȯrm and you can hear it said with an American accent here or in more

authentic Scots here.

Cairngorm began life in 2002 as an ActionScript library for building applications that apply a form of the Model-

View-Controller (MVC) pattern. This continued for some time, with the project becoming open-source in 2004. In the

meantime, many third-party frameworks were also developed and released, and a trend emerged towards Inversion-of-

Control (IoC) frameworks. In 2009, the scope of Cairngorm was increased, transforming the MVC framework into a

broader foundation of guidelines, tools and libraries that apply across frameworks.

To get started with Cairngorm, begin on the Cairngorm Guidelines page, where you will find some introductory

material and a sample application. These explain the Cairngorm Architecture, which is our preferred way to structure

Flex applications. Below the introductory material can be found more detailed information about the Cairngorm

Architecture, and a collection of Best Practices documents covering many areas of Flex development. These can be

used to help keep development teams on the right track.

In addition to the guidelines, Cairngorm provides tools and libraries. The tools help to ensure efficient and high-

quality delivery of Flex applications, while the libraries solve recurring problems in Rich Internet Applications.

Old Timer

If you're already skilled in Cairngorm 1 & 2, you'll be interested to hear how the focus of Cairngorm 3 has changed.

Instead of centering around a specific implementation of the Model-View-Controller pattern, Cairngorm 3 consists of

a broader set of guidelines, tools and libraries that apply across frameworks. Cairngorm aims to help developers apply

Flex and third-party frameworks effectively.

The Cairngorm Guidelines describe our preferred client-side architecture, which is known as the Cairngorm

Architecture. It consists of various patterns, some of which will be instantly familiar to experienced Cairngorm users.

For example, the Command pattern is still recommended for encapsulating the operations that an application performs.

Similarly, models and services are declared externally to the view, as with the Service Locator and Model Locator.

However, Cairngorm 3 acknowledges new approaches to accessing models and services, such as using an Inversion-

of-Control (IoC) container to automatically inject them where required.

The original Cairngorm library remains a part of Cairngorm 3, but has not been updated for this release. It can be

found on the Cairngorm Libraries page, alongside a collection of newer libraries. In addition, the Cairngorm Tools

page describes various tools that we have found useful for ensuring quality in the applications we deliver. These range

from commerical products for automation testing, to simple Ant scripts for generating test suites and cleaning up

MXML files.

To migrate from Cairngorm 2 to 3, you should first read the Cairngorm Guidelines to understand how your existing

client-side architecture might be improved. This could involve introducing an inversion-of-control container or simply

refining the way you use the original Cairngorm library. For example, it may be beneficial to decentralize, splitting an

application into distinct functional areas, with their own models and services. After reading the guidelines, you may

evaluate the Cairngorm Tools to decide whether there are parts of your development process that could be

strengthened. Finally, you might review the Cairngorm Libraries to see whether they provide solutions to any of the

problems you are facing in your application code.

Visitor

Thanks for dropping by. If you're already happy delivering Flex projects with one of the newer third-party

frameworks, you might be interested to hear that Cairngorm has changed. Instead of centering around a specific

implementation of the Model-View-Controller (MVC) pattern, Cairngorm has transformed into a foundation of

guidelines, tools and libraries, many of which apply across frameworks.

The Cairngorm Guidelines describe a client-side architecture that can be implemented using any modern inversion-of-

control framework, such as Parsley, Swiz or Spring ActionScript, while the accompanying Best Practices apply to the

Page 3: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Flex SDK in general, regardless of your application framework of choice. These guidelines and best practices can help

teams to delivery high-quality applications.

The Cairngorm Tools page describes a number of tools that we have found valuable during development of large

enterprise projects. These range from commercial products for automation testing, to simple Ant tasks for generating

unit test suites and cleaning up MXML files. The emphasis of the tools is on achieving quality and agility during

delivery.

The Cairngorm Libraries may or may not be of interest. They address common problems that we have encountered

repeatedly in our engagements, such as complex validation requirements, processing tasks sequentially and in parallel,

and managing pop-ups. Some third-party libraries provide their own solutions to these problems. Please evaluate these

libraries and apply them if they solve problems relevant to your project needs.

Overview of the Cairngorm Architecture

Cairngorm provides a reference architecture for Flex applications that is designed to keep things simple and testable. It

describes a way of structuring large applications out of smaller units.

A Cairngorm application consists of:

Architectural Layers that separate classes with different kinds of responsibility

Functional Areas that group classes related to the same area of functionality

Design Patterns that coordinate objects in consistent ways

Architectural Layers

Classes with different kinds of responsibility belong to different layers than can be changed separately from one

another. So if the visual design needs to be tweaked, most changes are confined to the classes in a presentation layer,

and not other parts of the code.

Cairngorm recommends the layers described by Eric Evans in Domain-Driven Design:

Presentation - presents data to the user and gathers input. i.e. the fancy UI

Application - performs the operations of the application. i.e. saving a form

Domain - models the business concerns of the application. i.e. the form data

Infrastructure - coordinates objects and integrates with other systems. i.e. talking to the server

The figure below shows that code in a higher layer makes direct use of code below it. In the other direction,

interactions happen indirectly through event listeners, callback functions and other means.

Continue reading to learn about the relationship between architectural layers, functional areas and design patterns.

Later on, you can refer to the detailed guidelines for each architectural layer.

Page 4: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Functional Areas

Applications are usually composed from distinct functional areas, like a publication editor and a news list. The code

for a functional area should be grouped together and separated from other functional areas, so it can be developed,

tested and profiled independently.

The code for a functional area is contained beneath a single source package. Although multiple functional areas may

be contained within a single Flash Builder project, it is common to extract them into modules or sub-applications,

stored is separate application projects. Shared infrastructure code, such as reusable components and utilities, is best

extracted into library projects.

Functional areas usually contain their own presentation and application layers. They sometimes contain their own

domain models, but other times share some domain knowledge with other functional areas. In that case, the shared

domain models are normally extracted into library projects that functional areas can depend upon.

Functional areas interact with one another through thin interfaces. These typically consist of events and ActionScript

interfaces. Shared infrastructure, such as a messaging framework or service registry is often used to facilitate

interactions.

Functional areas also reuse components, styles and skins for a consistent look and feel and to prevent code

duplication. These are a kind of infrastructure, like the components in the Flex SDK, that should be designed without

knowledge of the particular business domain of an application.

Design Patterns

Design patterns are repeatable solutions to commonly occurring problems in software design. Cairngorm catalogues a

number of design patterns that are effective in Flex. They can be applied within functional areas and across layers to

bring consistency to a large code base. For example, the Command pattern is advocated for encapsulating operations

and the Presentation Model is advocated for removing state and logic from MXML view components.

Conclusion

The Cairngorm architecture recommends composing larger applications from smaller parts, known as functional areas.

It recommends separating code into architectural layers, based on the kind of responsibilities that are carried out. And

it recommends applying consistent design patterns to solve recurring problems and advocates a number of these that

have proven useful in Flex applications.

An analogy for the Cairngorm architecture might be a number of businesses doing work together. Each business

premises is a functional area. The building's exterior and decoration is its presentation layer. Inside there are people

carrying out tasks and they are the application layer. The business that is accomplished – the products created and

Page 5: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

sold – is the domain layer, and the building materials, telephones and coffee machines that facilitate the whole

enterprise are the infrastructure layer.

To find out more about the Cairngorm architecture, you can refer to the documentation on the Cairngorm Guidlines

page and the sample applications.

A Simple Sample Application Explained

Introduction

This article takes a whistle-stop tour of a simple Cairngorm application to demonstrate the architecture in action.

The example shown was part of the presentation "Using Flex Frameworks to build Data Driven Applications" at the

MAX 2009 conference. The client side source of the example is now maintained and extended as part of Cairngorm.

For more information about this presentation view Christophe Coenraets blog.

Browse the client side source code here

Server-side component

o Download

o Setup instructions

In order to run the sample application a server side component is required. This is not required to

understand this tutorial.

This example uses the Parsley Application framework, however this tutorial doesn't go into the Parsley feature set and

instead follows on guidelines that Cairngorm specifies on top of that. Therefore, the conceptual practices used here are

applicable to a variety of application frameworks available for the Flash platform today.

The Package Structure

Packages are an important way to organize code. A consistent package structure makes it easier to navigate the code

base, and easier to understand the dependencies between different parts. The InsyncBasic application only shows how

Cairngorm separates code by architectural layer. Behind the insync package, you'll find the following packages,

describing architectural layers:

presentation (concerns visual appearance and presentation behaviour)

application (concerns coordination of presentation and infrastructure components)

domain (soley focused on the problem domain)

infrastructure (concerns i.e. remote services or UI frameworks)

These packages showcase our recommendation for a layered architecture.

Let's dive into these architectural layers starting with the presentation layer.

Look, No Script-block Functions

To begin with, open up the main application file, InsyncBasic.mxml, and navigate down through the view hierarchy.

The first thing you'll notice is an absence of Script-block functions in the MXML view components. This keeps the

views focussed on sizing and layout concerns, making them easier to read and change as the visual design evolves.

Here's the Toolbar component:

<mx:Script>

<![CDATA[

[Inject]

[Bindable]

public var model:ToolbarPM;

]]>

</mx:Script>

<mx:Button

styleName="contactsAddButton" toolTip="Add Contact"

click="model.addContact()"/>

<mx:Spacer width="100%"/>

Page 6: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

<mx:Label text="Search:"/>

<mx:TextInput id="searchBox"

change="model.search(searchBox.text)"/>

Notice the Inject tag. This is part of the Parsley Application framework to inject the ToolbarPM into the Toolbar

view.

Enter the Presentation Model

The variables and functions that might have been in the Script-block are instead extracted into presentation models.

Each significant MXML view component has its own presentation model (PM). The PM is focussed on the state and

behaviour required to present data to the user and process user input. It knows nothing about any other presentation

model and it doesn't have knowledge of the application around it.

Here's the ToolbarPM, which trims white-spaces from user input before other client side components receive a search

event.

public class ToolbarPM extends EventDispatcher

{

[MessageDispatcher]

public var dispatcher:Function;

public function addContact():void

{

dispatcher(ContactEvent.newAddContactEvent());

}

public function search(keywords:String):void

{

if (keywords == null)

return;

keywords=StringUtil.trim(keywords);

dispatcher(new SearchEvent(keywords));

}

}

A PM should not reference a view component directly; instead the view observes the PM. The wiring between a view

component and its corresponding PM takes place through binding expressions and in-line event handlers. Here is the

ContactsList view component:

<mx:Script>

<![CDATA[

import insync.domain.Contact;

[Inject]

[Bindable]

public var model:ContactsListPM;

]]>

</mx:Script>

<mx:DataGrid id="list"

width="100%" height="100%"

dataProvider="{ model.contacts.items }" doubleClickEnabled="true"

itemDoubleClick="model.editContact(Contact(list.selectedItem))">

<mx:columns>

<mx:DataGridColumn dataField="firstName"

headerText="First Name"/>

<mx:DataGridColumn dataField="lastName"

headerText="Last Name"/>

<mx:DataGridColumn dataField="phone"

headerText="Phone"/>

</mx:columns>

</mx:DataGrid>

With this approach there is little room for logical errors in the view, and the real logic instead exists in the PM, where

it can easily be unit tested. Unit testing view components directly is more difficult due to the asynchronous component

life-cycle and less dependencies with other components such as other view controls. A PM also simplifies a

component with driving out the used API of a view component from the larger UIComponent API available in

MXML.

Page 7: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Coordinating the Components

The different components of an application need to be coordinated to provide useful features to the user. When

something is typed into the search box, the results need to appear in the contacts list below. This behaviour is

separated from presentation concerns, so the layout can be changed independently.

In the InsyncBasic application, the search operation is invoked by dispatching a SearchEvent from the ToolbarPM:

public function search(keywords:String):void

{

if (keywords == null) return;

keywords = StringUtil.trim(keywords);

if (keywords.length > 0)

{

dispatcher(new SearchEvent(keywords));

}

}

Encapsulate Operations in Commands

The SearchEvent is handled by the application layer, a Command invokes the RPC operation with invoking a remote

service and adding the successful result into a domain object of the domain layer.

public class SearchContactsCommand

{

[Inject]

public var contacts:Contacts;

[Inject]

public var cache:IDataCache;

[Inject]

public var service:RemoteObject;

public function execute(event:SearchEvent):AsyncToken

{

return service.getContactsByName(event.keywords) as AsyncToken;

}

public function result(items:IList):void

{

contacts.addContacts(cache.synchronize(items));

}

}

The Command follows a convention from Parsley's DynamicCommand feature. The request of the SearchEvent is

handled by the method named "execute", while the result is handled by the method named "result". If this example

would need to handle a error response additionally, then another method could have been specified named "error".

This convention is enfored by declaring a DynamicCommand inside a Parsley context.

<DynamicCommand type="{ SearchContactsCommand }"/>

Also note the injected IDataCache utility, which is part of the Cairngorm Integration library. For more information

about IDataCache, click here.

Sequencing Operations

The Insync application refreshes the search view with the latest data once a new contact has been saved. This is easy

to do using the data synchronization feature of LiveCycle Data Services, however using only RPC operations as the

Insync application, the client side needs to manually invoke a search operation after the save operation has returned

successfully. This simple sequencing can be achieved with a dedicated object in the application layer, the

RefreshSearchAfterSaveController.

public class RefreshSearchAfterSaveController

{

[MessageDispatcher]

public var dispatcher:Function;

private var lastSearch:String="";

[MessageHandler(selector="search")]

public function onSearch(event:SearchEvent):void

Page 8: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

{

lastSearch=event.keywords;

}

[CommandResult(selector="save")]

public function onSaveComplete():void

{

dispatcher(new SearchEvent(lastSearch));

}

}

For more evolved requirements on sequencing that are often useful for i.e. application start-ups or sequencing of

domain behaviour, check out the Task library.

Keep Your Objects Small

Note the small objects of the previous samples. The RefreshSearchAfterSaveController from above or the

Contacts domain object below only really have one responsiblity. All their methods reference all instance properties

and therefore achieve a high functional cohesion. This keeps your code simple, focused and more resilient to change.

Check out the Single Responsibility Principle for more information.

[Event(name="itemsChange", type="flash.events.Event")]

public class Contacts extends EventDispatcher

{

public static const ITEMS_CHANGE:String = "itemsChange";

private var _items:IList = new ArrayCollection();

[Bindable("itemsChange")]

public function get items():IList

{

return _items;

}

public function addContacts(items:IList):void

{

_items = items;

dispatchEvent(new Event(ITEMS_CHANGE));

}

public function addContact(contact:Contact):int

{

var index:int = -1;

if (items.getItemIndex(contact) == -1)

{

items.addItem(contact);

index = items.length - 1;

}

return index;

}

public function addItemAt(contact:Contact, index:int):void

{

items.addItemAt(contact, index);

}

public function removeContact(contact:Contact):int

{

var index:int = items.getItemIndex(contact);

if (index != -1)

{

items.removeItemAt(index);

}

return index;

}

public function removeContactAt(index:int):Contact

{

return Contact(items.removeItemAt(index));

}

}

This concludes the basic tour through InsyncBasic. InsyncBasic only shows one functional area, dealing with contacts.

The InsyncModularExtended sample application and tutorial focuses on how Cairngorm recommends to deal with

additional functional areas such as messaging and expenses and is therefore a logical next step to learn more about

Cairngorm.

A Modular Sample Application Explained

Page 9: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Introduction

This article takes a whistle-stop tour of a modular Cairngorm application to demonstrate the architecture in action.

Please take the tutorial for a simple Cairngorm application before reading this.

The example shown was part of the presentation "Using Flex Frameworks to build Data Driven Applications" at the

MAX 2009 conference. It is now maintained and extended as part of Cairngorm. For more information about this

presentation view Christophe Coenraets blog.

Browse the client side source code here

o InsyncModularExtendedShell

o InsyncModularExtendedAPI

o InsyncModularExtendedContacts

o InsyncModularExtendedMessaging

o InsyncModularExtendedExpenses

Serverside component

o Download

o Setup instructions

In order to run the sample application a server side component is required. This is not required to

understand this tutorial.

This example uses the Parsley Application framework, however this tutorial doesn't go into the Parsley feature set and

instead follows on guidelines that Cairngorm specifies on top of that. Therefore, the conceptual practices used here are

applicable to a variety of application frameworks available for the Flash platform today.

Project and Package Structure

The InsyncBasic application only showed one functional area, dealing with contacts. The InsyncModularExtended

sample application focuses on how Cairngorm recommends to deal with additional functional areas such as messaging

and expenses and is therefore a logical next step to learn more about Cairngorm. This modular requirement reflects in

how projects and packages are organized.

While Cairngorm recommends that code be organized by architectural layer (presentation, application, domain,

infrastructure) within a functional area, outside a functional area, code is organized more functionally.

InsyncModularExtended is broken into separate projects, where each project represents a separate functional area,

and compiles to its own Flex module.

An additional shell project InsyncModularExtendedShell allows to start the application with loading functional areas

into a generalized UI shell application that provides global navigation between functional areas.

An API project InsyncModularExtendedAPI is used to share commonly used objects between functional areas. Care

needs to be taken that as little behaviour as possible is shared to minimize dependencies and be more resilient to

change.

The projects reflect the grouping by functional areas via its package structure. All objects within the functional area

contacts belong to a contacts package, and architectural layers are placed as packages beneath these functional

packages.

For more information on functional areas see Creating Functional Areas within Modular Development.

Shell - Module Loading and Navigation

The shell application defines the generalized UI shell and navigation between functional areas. Because functional

areas are contained within Flex modules, the shell application loads and unloads these modules leveraging

Cairngorm's module library. Concrete module SWF files are references in a context file InsyncContext as shown in

this excerpt:

<!-- Infrastructure -->

<module:ParsleyModuleDescriptor objectId="contacts"

url="../../insync-modularExtended-contacts/bin-debug/ContactsModule.swf"

Page 10: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

domain="{ ClassInfo.currentDomain }" />

and loaded with a generalized view component InsyncViewLoader of ContentViewStack. Note, that the loading

component only needs the IModuleManager interface of the Flex SDK to be injected.

<mx:ViewStack xmlns:fx="http://ns.adobe.com/mxml/2009"

xmlns:mx="library://ns.adobe.com/flex/mx"

xmlns:core="insync.core.*"

xmlns:spicefactory="http://www.spicefactory.org/parsley">

<fx:Metadata>

[Waypoint]

</fx:Metadata>

<fx:Script>

<![CDATA[

import insync.application.ContentDestination;

import com.adobe.cairngorm.module.IModuleManager;

[Bindable]

[Inject(id="contacts")]

public var contacts:IModuleManager;

[Bindable]

[Inject(id="messageCompose")]

public var messageCompose:IModuleManager;

[Bindable]

[Inject(id="expensesModule")]

public var expensesModule:IModuleManager;

]]>

</fx:Script>

<fx:Declarations>

<spicefactory:Configure/>

</fx:Declarations>

<s:NavigatorContent width="100%"

height="100%"

automationName="{ ContentDestination.CONTACTS }">

<core:InsyncViewLoader moduleId="{ ContentDestination.CONTACTS }"

width="100%"

height="100%"

moduleManager="{ contacts }"/>

</s:NavigatorContent>

<s:NavigatorContent width="100%"

height="100%"

automationName="{ ContentDestination.MESSAGE_COMPOSE }">

<core:InsyncViewLoader moduleId="{ ContentDestination.MESSAGE_COMPOSE }"

width="100%"

height="100%"

moduleManager="{ messageCompose }"/>

</s:NavigatorContent>

<s:NavigatorContent width="100%"

height="100%"

automationName="{ ContentDestination.EXPENSES }">

<core:InsyncViewLoader moduleId="{ ContentDestination.EXPENSES }"

width="100%"

height="100%"

moduleManager="{ expensesModule }"/>

</s:NavigatorContent>

</mx:ViewStack>

The view loader component InsyncViewLoader allows to specify what happens while the module is loading or if an

error occurs during the loading process. InsyncViewLoader is part of the InsyncModularExtendedAPI project because

its behaviour relates not only to the shell application but also to other functional areas that load further modules, and

must provide consistency in doing so.

Isolated Development and Testing of Functional Areas

When applications are organized in functional areas teams can develop functional areas in isolation of the shell

application, which can reduce development times. The ContactsModuleRig of the InsyncModularExtendedContacts

project simulates the shell application in the most simplest form in order to concentrate only on the functional area

contacts. This not only reduces compilation and manual testing time but can also increase the number of developers

who can productively work on the overall project once a project architecture allows isolated development and

manages dependencies between the isolated functional areas.

Page 11: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Agile Testing

All InsyncModuleExtended projects also keep their own unit test suites local to the functoinal areas they belong to.

This keeps the unit test suites fast to execute, an important best practise of agile unit testing.

The test-suite of the InsyncModularExtendedContacts project uses FlexUnit 4. Browse through the

ContactsTestRunner to understand how and where it's used. Notice that unit tests do not test MXMLs or other view

components. Cairngorm recommends to extract behaviour of difficult to unit test view components into dedicated

objects of the Presentation Model and Domain Model.

Also notice that unit tests test objects in isolation to ensure i.e. granular defect localization and fast test suites. If

objects contain dependencies, test doubles are injected and controlled with the mock framework ASMock as shown in

i.e. SaveContactCommandTest. The Insync test suites focus on the behaviour of objects instead of the wiring. The

ContactsTest class i.e. tests the bounds checking of the Contact domain.

Configuration and wiring are not part of unit tests and can more effectively be covered via functional testing. One area

the Insync samples currently don't show is a functional test suite. Functional test suites are highly important and a vital

part of any enterprise Flex application.

Domain Validation

The InsyncModularExtended projects make use of the Cairngorm Validation library in order to group validators and

extract them from particular view components into the client side domain. For more information about domain

validation and other features of the Validation library, click here.

Error Handling

An intelligent error handling strategy provides an opportunity for RIAs to shine with i.e. graceful fall-back

mechanisms. The Insync samples showcase a generic structure of how errors of remote service calls can be handled.

The Cairngorm Integration library offers a Parsley tag to define a global error handler for all RemoteObjects in any

module. The InsyncModularExtendedShell project's AlertHandler class defines this handler that currently only logs

the error using Flex SDK logging:

[GlobalRemoteObjectFaultHandler]

public function logError(event:FaultEvent):void

{

LOG.error("A server error occurred. event={0}", event.toString());

}

Additionally to this global error handling on remote service calls, other objects might have the need to perform custom

behaviour on remote service errors. The Command tag of the Cairngorm Integration library offers a CommandFault

tag to handle a fault in any architectual layers.

For example in the InsyncModularExtendedContacts project needed to display an Alert box once a save operation

failed and additionally display a failure message to a particular view component. The SaveContactCommand declares a

CommandFault directly in the Command object.

public function execute(event:ContactEvent):AsyncToken

{

return service.save(event.contact) as AsyncToken;

}

public function result(savedContact:Contact, event:ContactEvent):void

{

cache.updateItem(event.contact, savedContact);

}

public function error():void

{

dispatcher(new AlertEvent("Unable to save the contact."));

}

Its implementation (error method) just dispatches an event, which is handled in the AlertHandler object that

dispatches a global Alert view control.

Page 12: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

[MessageHandler]

public function showAlert(event:AlertEvent):void

{

var text:String = event.text;

if (text == "" || text == null)

{

text = "An error occurred.";

}

Alert.show(text);

}

This keeps global Alert controls centrally defined and more resilient to changes of i.e. User Experience teams that

decide to change the error handling of global Alert controls. It also improves the testability of Commands.

The display of the failure message of a particular view component is separated from the Command and defined only in

the view component that needs to know about it, the ContactFormPM

[CommandError(selector="save")]

public function onSaveFault(event:ContactEvent):void

{

status="Unable to save contact.";

}

The concludes the whistle-stop tour of a modular Cairngorm application. Feel free to browse more into the examples

and provide feedback here. We indent to continuously increase the feature set of these examples in order to show more

realistic examples of enterprise Flex applications using Cairngorm.

Cairngorm Architecture: Presentation Layer

Cairngorm recommends a separation between presentation objects

Presentation Graphic Layer Responsible for visual appearance, layout and effects. Objects often extend flash.display.DisplayObject (i.e.

mx.core.UIComponent) and are often represented in MXML.

Presentation Behavior Layer Responsible for presentation behavior such as communicaton with and between UI controls, formatting, input

validation (validation can also be a Domain Layer responsiblity).

Cairngorm recommends the Presentation Model (PM) as a presentation pattern of the Presentation Behavior Layer,

which encapsulates behaviour and state of the Presentation Graphic Layer while observing it using Flex's data binding.

PMs can ease unit testing of presentation behaviour and clarify the presentation component API of interest.

Applying the Presentation Model: Componentized vs. Hierarchical

Applying the Presentation Model: Coordination with Presenters

Cairngorm Architecture: Application Layer

Coordinates components of other layers and directs to Domain Objects.

Controllers

Cairngorm Architecture: Domain Layer

An abstraction of a real-world problem domain and is there to serve the UI with a representation of the real world.

Responsilbities

Validation

Computations around the problem domain

Filtering (additional filtered collections enrich the Domain Model)

Sorting (can also be Presentation Layer responsibility)

Page 13: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Formatting (can also be a Presentation Layer responsibility)

Cairngorm Architecture: Infrastructure Layer

Support of other layers by providing i.e.

UI framework (Flex framework)

Communication with external environments (See Cairngorm Integration Library)

Change Tracking

Original Value Tracking

Lazy Loading

Caching

Commands

Task framework (See Performing Tasks in Sequence and Parallel)

Modular Development

Modularity

Creating Functional Areas

Using the Cairngorm Module Library

Modular applications and Developing and loading sub-applications of Chaptor 3 Application Architecture

within the Flex 4 Developer Guide

How to Unload Modules Effectively

What is Modularity?

Modularity is something more general than the Flex Module and ModuleLoader components. These are a form of

modularity, but the principle is broader: modularity is about separating applications into smaller units that can be

developed and deployed independently. In the context of Flex, these units might be modules, sub-applications or any

other kind of encapsulated content. A modular application usually has a structure:

In the figure above, the application consists of a thin shell that loads three modules: Dashboard, Contacts and

Messages. These modules represent different functional areas of the application. Their implementation detail is

independent of one another. The application shell is responsible for loading and laying out the modules, and providing

a means of communication between them. This might be a global data model, a registry of interfaces, or in the case of

the illustration above, some kind of message bus.

What are the Benefits of Modularization?

Modularization can bring benefits for end-users of an application and also for the teams that develop and deliver them;

Here are some of the benefits:

Page 14: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

A module can be developed, tested and profiled in relative isolation.

Build times are shortened, since changes to one module don't require other modules or the shell application to

be recompiled.

Modules can be loaded on demand, so the initial download for an application is smaller.

If a user never uses the features of one module, that module need not be loaded.

Individual modules can be deployed into production, instead of redeploying an entire application.

Different modules can be loaded for different users based on their entitlements.

A module is easier to understand and maintain than a monolithic application, since it is more functionally

cohesion.

The interactions between modules can be confined to a thin API, reducing regression as an application grows.

The benefits in terms of architecture and development efficiency are arguably the most important. Small groups of

developers can work on individual modules. The contracts of communication between modules can be agreed, so the

implementation is free to change and improve without regression.

Cairngorm 3 and Modularization

One of the key messages of the Cairngorm 3 reference architecture is to separate applications into distinct functional

areas that can be developed independently; in other words a modular architecture, where each functional area is a

module. The Cairngorm guidelines recommend that communication between functional areas takes place through a

thin API that might consist of interfaces, events and data transfer objects. This approach minimizes dependencies

between the distinct functional areas of an application. It promotes what Robert Martin terms "good dependencies" in

the direction of stability. More details can be found in the Creating Functional Areas guideline.

Framework Provisions for Modularization

Many Flex frameworks provide features that support modularity. Parsley, Spring ActionScript, Swiz, Potomac,

PureMVC all offer support in varying degrees. The Cairngorm Module Library builds on top of Parsley's support for

modularity.

The Enterprise Solution: Adobe LiveCycle Mosaic ES2

LiveCycle Mosaic ES2 is far more than a modularity framework, but modularity is an important part of its design. It's

a client-and-server-side technology for building applications by composition, combining different pieces into

personalized views that are focussed on the activities that different users perform. On the server-side, reusable

application assets can be stored and shared, while LiveCycle DataServices is available for integrating with different

data sources. On the client-side, applications are assembled from different tiles, that can be developed from scratch or

adapted from existing Flex and HTML applications. A customizable shell application loads and lays out the tiles, and

the framework provides a publish-subscribe messaging API for communication between them. For more details about

the Adobe enterprise solution to modularity, refer to the LiveCycle Mosaic ES2 product page.

Conclusion

As we attempt to build bigger and better applications in Flex and AIR, the topic of modularity becomes more

important to ensure efficient development, scalability and maintainability. It is vital to be able to separate portions of a

large application so they can be developed, tested and deployed independently. The Flex SDK provides some simple

means of modularization with Modules and Sub-Applications, and some frameworks build on top of these to provide

more features. Furthermore, Adobe has now released an enterprise solution in LiveCycle Mosaic ES2 that provides the

infrastructure for rapidly developing and deploying RIAs to the browser and desktop in a modular way.

Options in Loose Coupling

We loosely couple architectural layers because we assume each architectural layer is likely to change for different

reasons. Loose coupling of each architectural layer can improve the resilience to change of each layer. It can also

reduce dependencies of shared objects (or API projects, see Creating Functional Areas) as less dependent objects

exist. However, loose coupling comes at a price. The looser an object is coupled to another object, the higher the cost

of readability. Sometimes, objects are not likely to change and very loose coupling only introduces overhead.

Page 15: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

There are options in loose coupling that developers can consider depending on the likelihood of change.

The following explains options in coupling on one example relationship between an object of the presentation layer

(ContactListPM) and a domain layer (Contacts). The ContactListPM references an items property of type

mx.collections.IList on the Contacts domain.

Concrete References to Domain

A concrete reference of one object to another represents no coupling. It is the easiest option to read at the cost of a

lower resilience to change and a higher number of dependent objects when shared.

Furthermore, unit tests cannot substitute the concrete reference with a test double in order to focus the purpose of the

test to the object under test, simulate and test interactions and test difficult-to-test behavior in case the concrete object

is difficult to test. See the Agile Testing paper for more information.

Domain Interface

The domain could implement an interface of its API. Preferable this interface will abstract the domain from the

presentation with i.e. only describing the interaction with the PM. Domain interfaces allow a unit test to substitute the

domain in case that is preferable.

Abstracted Interface

A domain interface could be further abstracted to not describe that the PM interacts with a domain. For example a

ContactListPM might need to read an IList collection of a domain object. Instead of creating a domain interface,

create an abstracted interface that only describes the property of an IList i.e.

public interface IItems

{

function items():IList;

}

Metadata Injection

A metadata tag can be created that injects the items property of the Contacts domain into the PM.

The Cairngorm Observer library offers one example build upon Parsley

[Wire(id="contacts",property="items")]

The "contacts" value is how the Contacts domain is declared in the context.

Presenter

Presenters are part of the presentation layer and take a higher level role than PMs. They can coordinate PMs but also

loosen the coupling of a PM to a domain. A ContactListPresenter could be created that subscribes to a domain event

whenever its items collection is changed. On a change, the presenter would directly update the PM as it contains a

reference. The coupling is not loosened overall, but if the PM is regarded to be reused in a different context, this form

of coupling could be a consideration.

Messaging

A messaging mechanism allows two objects to communicate with each other by only knowing about the shared

messaging mechanism. Various architectural Flex frameworks support a messaging mechanism with different

requirements on each participant to know about the messaging mechanism. Cairngorm 2 requires both participants to

know about a concrete framework component, while Swiz can loosen the framework coupling of the message handler

part with metadata while comletley removing it on the message dispatching part. Parsley messaging either only

requires both participants to know about metadata or specify the coupling externally via a context.

Messaging can achieve a low coupling between objects, often with introducing further dependencies on frameworks,

or the introduction of event objects. Furthermore, the scope of messaging is often global, which can lead to further

complications where and in what order messages are handled.

Page 16: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Options in Loose Coupling

We loosely couple architectural layers because we assume each architectural layer is likely to change for different

reasons. Loose coupling of each architectural layer can improve the resilience to change of each layer. It can also

reduce dependencies of shared objects (or API projects, see Creating Functional Areas) as less dependent objects

exist. However, loose coupling comes at a price. The looser an object is coupled to another object, the higher the cost

of readability. Sometimes, objects are not likely to change and very loose coupling only introduces overhead.

There are options in loose coupling that developers can consider depending on the likelihood of change.

The following explains options in coupling on one example relationship between an object of the presentation layer

(ContactListPM) and a domain layer (Contacts). The ContactListPM references an items property of type

mx.collections.IList on the Contacts domain.

Concrete References to Domain

A concrete reference of one object to another represents no coupling. It is the easiest option to read at the cost of a

lower resilience to change and a higher number of dependent objects when shared.

Furthermore, unit tests cannot substitute the concrete reference with a test double in order to focus the purpose of the

test to the object under test, simulate and test interactions and test difficult-to-test behavior in case the concrete object

is difficult to test. See the Agile Testing paper for more information.

Domain Interface

The domain could implement an interface of its API. Preferable this interface will abstract the domain from the

presentation with i.e. only describing the interaction with the PM. Domain interfaces allow a unit test to substitute the

domain in case that is preferable.

Abstracted Interface

A domain interface could be further abstracted to not describe that the PM interacts with a domain. For example a

ContactListPM might need to read an IList collection of a domain object. Instead of creating a domain interface,

create an abstracted interface that only describes the property of an IList i.e.

public interface IItems

{

function items():IList;

}

Metadata Injection

A metadata tag can be created that injects the items property of the Contacts domain into the PM.

The Cairngorm Observer library offers one example build upon Parsley

[Wire(id="contacts",property="items")]

The "contacts" value is how the Contacts domain is declared in the context.

Presenter

Presenters are part of the presentation layer and take a higher level role than PMs. They can coordinate PMs but also

loosen the coupling of a PM to a domain. A ContactListPresenter could be created that subscribes to a domain event

whenever its items collection is changed. On a change, the presenter would directly update the PM as it contains a

reference. The coupling is not loosened overall, but if the PM is regarded to be reused in a different context, this form

of coupling could be a consideration.

Messaging

A messaging mechanism allows two objects to communicate with each other by only knowing about the shared

messaging mechanism. Various architectural Flex frameworks support a messaging mechanism with different

requirements on each participant to know about the messaging mechanism. Cairngorm 2 requires both participants to

Page 17: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

know about a concrete framework component, while Swiz can loosen the framework coupling of the message handler

part with metadata while comletley removing it on the message dispatching part. Parsley messaging either only

requires both participants to know about metadata or specify the coupling externally via a context.

Messaging can achieve a low coupling between objects, often with introducing further dependencies on frameworks,

or the introduction of event objects. Furthermore, the scope of messaging is often global, which can lead to further

complications where and in what order messages are handled.

Agile Unit Testing

This paper explains one of the prime goals of Cairngorm; which is to allow developers to follow agile testing of Flex

RIAs. Agile testing combines engineering with testing activities into one activity and can be expressed by following

Test Driven Development where a developer would write tests first. In particular, this paper focuses on unit testing

where units represent objects or single architectural layers in contrast to functional testing (customer tests) where a

unit represents the complete application (many architectural layers). Agile functional testing is also vitally important

to Flex project but the focus of a separate paper.

Why to Isolate - Focus of Unit Tests

Most objects of a system depend on other objects (depended-on objects). Objects contain other objects and delegate

work to them. When unit testing one object the decision is up to the developer to touch the depended-on object with

the same unit test or substitute the depended-on object with an alternative object (test double).

The motivations to substitute (isolate) depended-on objects with test double are manyfold and have to be contrasted

with the motivations against it.

Motivations for isolation

Motivations for isolation (concrete depended-on objects are substituted with test doubles) are:

Simulate hard to test behaviour If the object under test contains behaviour that is hard to simulate in a unit testing environment (i.e. specific

error conditions, or asynchronous events), a substituted test double could simulate it and could allow more test

scenarios and faster test runs.

Granular defect localization If an implementation changes and a test flags up an unexpected result, it is easy to localize the defect if the

object under test is isolated from it's environment.

No triggering of unwanted behaviour Without any isolation, the behaviour of the object under test might trigger behaviour on the depended-on

object that cause other unexpected effects in unit tests. With substituting the depended-on object, the

behaviour the object under test triggers is controlled.

Less setup code to satisfy dependencies of depended-on objects Without any isolation, depended-on objects might require further dependencies in order to execute without

errors. These dependencies would need to be satisfied by the unit test, increasing the knowledge of the unit

test unnecessarily. Would the object under test use test doubles instead of concrete depended-on objects, the

test doubles can be designed not to require further dependencies, reducing setup code and making the test

easier to read and maintain.

Higher coverage through testing interactions In order to test how an object under test interacts with depended-on objects, the unit test needs access to the

depended-on objects. Test doubles can be designed to offer flexibility on how unit tests can observe the

interaction with their object under test (mocks). Depended-on objects are often not designed for it, and

developers are in danger of sacrificing encapsulation of a concrete implementation of a depended-on object.

Code coverage numbers are more significant When concrete depended-on objects are substituted, they don't get coverage through unit tests that focus on

other objects. Code coverage tools can easier identify the need for additional tests of the concrete depended-on

objects when no code coverage on concrete depended-on objects has been registered via other unit tests.

Page 18: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Motivations against isolation

When concrete depended-on objects are executed by the object under test, then isolation is not performed and the

motivations against isolation can be:

Over-specified unit tests hinder refactorings If all interactions with depended-on objects are tested, refactorings on objects under test that change any of the

interactions also need to change the unit tests and this increases the overall effort of refactorings. To counter

this danger, unit tests should only focus to test one piece of behaviour and not repeat tests of other tests and

not test interactions that are not significant.

More difficult to write and read unit tests If the object under test has to create test doubles for all it's depended-on objects, it increases the size of the

unit test and makes it more difficult to read. However, automatic test double creation libraries such as

ASMock ease the time it takes to write test doubles.

Higher code coverage with fewer tests While objects under test might not be able to and want to test all interactions with depended-on objects, they

automatically cover some behaviour of depended-on objects and can achieve a higher code coverage with

fewer tests. However, the higher code coverage through unit tests that triggered code of depended-on objects

might lead to a false sense of security as the focus of the unit test was not on the depended-on object but on

the object under test.

Danger of substituting behaviour to test When isolating too much developers might accidently substitute objects they actually would want to test and

the risk of integration increases. Watch code coverage results to ensure behaviour continuous to be tested.

The more depended-on objects the unit test touches, the more it steers testing efforts away from unit testing towards

functional testing. At some point, different tooling than unit testing frameworks, additional qualified personnel such as

Quality Engineers and separate test runners are more appropriate for complete functional and black box testing (See

Agile Functional Testing).

When to Isolate - Isolation by Architectural Layer

As shown above, motivations to isolate objects exists as motivations against it exists. When should a developer decide

to isolate an object and when not?

As a general rule of thumb, this paper recommends isolating at each client side architectural layer. Presentation layer,

domain layer, application layer and integration layer. Often developers can isolate objects beyond that and prevent the

dangers of over-specified tests that hinder refactorings with focusing each test on a new, small piece of behaviour that

is not repeatedly covered by another test. Also, interactions do not always need to be tested to the full API including

parameters. A test might gain enough realism by just observing one method call that identifies an interaction with a

depended-on object instead of observing all method calls of similar type including full parameter sets. Mock

frameworks in Flex today (i.e. ASMock and mock-as3) allow this flexibility to prevent having to specify every detail.

How to Isolate - Substitution Patterns and Test Doubles

Isolation can be achieved with substituting the depended-on object with an object that is controlled by the unit test

suite; a test double. The test double takes the same interface as the production object but does have a different

implementation, usually much simplified and targeted for a particular test case or suite.

Test doubles can come in many flavours. The most important test doubles are stubs and mocks. A stub directs inputs

into the object under test. A mock object observes outputs of the object under test.

Substituting - Dependency Injection and Dependency Lookup

Two of the most popular approaches to get substituted behaviour into objects under test is dependency injection and

dependency lookup. Dependency injection injects the test double via the API of the object under test. Dependency

lookup retrieves the test double via another object. The other "locator" object should then provide some mechanism to

configure it during unit tests with a test double.

Page 19: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Substituting - Overriding and Overwriting

Substitution by overriding can be achieved with subclassing the depended-on object with an object only used in tests

that provide an alternative implementation for the behaviour not of interest in unit tests of the object under test.

Substitution by overwriting simply sets (overwrites) a depended-on object with a test double. However, this is only

feasible if the depended-on object is exposed via an API of the object under test and typed as an interface or common

base class.

Substituting - Test Hocks

A Test Hock is a control point (i.e. if-statement) within the object under test that decides to behave differently based

on if it's being exercised during unit tests or during production. This can require a known object with global state to

tell objects if they are in a unit tests or not. However, instead of hardcoding a conditional statement in production

code, this could also be done using Flex's conditional compilation. In any case, this approach adds risk, size and

complexity to production code that would not need to be there following the approaches above and should be used

with caution.

How to Test - Applying Testing Knowledge

More information

Unit Test Organization

While there are many solutions to how to organize unit tests, Cairngorm attempts to provide a convention to improve

consistency between projects.

Same Project

Prefer to keep test code in the same project as source code, separated by source paths.

Same Package

Application code and test code are separated within the same project but use the same package. This is possible by

putting them into different source paths. The application code remains in Flash Builder's standard "src" source path,

while the test code is moved to the additional source path "test". This can also make searches for objects easier.

Test Case Naming

Prefer "Test" suffix instead of "Test" prefix. A suffix can ease type-ahead searches via Flash Builder as the test is

immediately alongside the object under test. If a test class becomes too large, analyse if the design of the object under

test can be improved. If not, multiple test classes can be created with the naming "Type_Feature_Test".

Test Method Naming

Self-documenting tests i.e. should or Given/When/Then naming.

http://dannorth.net/introducing-bdd

Summary - Test Infected Teams

Test-infected developers never write their tests days after their code. Test-infected developers want to write tests,

because that's the way they think about software development. They don't want to think otherwise. Test-infected

developers never have excuses not to test. They are never too busy to test, their environments never take up too much

time to create test data, and their customer never complain that testing is too expensive because it takes too much time.

If it is difficult to create an environment of test infected developers,

Page 20: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

analyze the design of the application for testable code, prevent repetitive and over-specified tests, keep tests

small, easy and close to the way objects are used in production.

don't attempt to test every single line of code in your Flex application. Realize that functional tests have a role.

Focus your unit tests to test behaviour and not structure and wiring.

ensure libraries, frameworks, API projects, any other code that is shared by multiple developers achieve the

highest coverage and quality of tests.

keep quality of test code as high as application code. Maintain tests with refactorings but realize that it's

sometimes easier to throw away and rewrite tests as it's sometimes easier to throw away and rewrite code.

Best Practices for Performance

Performance issues fall into three main categories:

1. Perceived Performance

2. CPU Utilization

3. Memory Footprint

These guidelines address all three and aim to help developers produce Flex and AIR applications that feel responsive

and make efficient use of system resources. Performance tuning is all about making trade-offs, so these are not black-

and-white instructions, but rather explanations to be considered when optimizing your own applications.

Note that "micro-level" optimizations, such as using bitwise operators to accelerate algorithms are not covered here.

Also, profiling tools are not explained in detail, but it is assumed that readers are familiar with the Flex Builder

Profiler and standard operating system tools, like Activity Monitor and Task Manager. For additional information

about optimization on the Flash Platform, please read http://www.adobe.com/go/optimize

Contents

Contents

1. Perceived Performance

Defer Creation Policies

Create Lightweight Item Renderers

Make Effects Snappy

Parallelize Service Requests

Load Data on Demand

Be Careful with StyleManager

2. CPU Utilization

Minimize Redraw Regions

Minimize Background Processing

Patch UIMovieClip for Stateful Skins

Don't Play Effects Excessively

3. Memory Consumption

Unload Modules Effectively

Remove Singleton Event Listeners

Consider Reusing Popups

Find Memory Leaks by Comparing Instance Counts

Profile The Export Build with Release Player

Conclusion

1. Perceived Performance

Perceived performance is about the way an application feels to users. If the application start-up seems to take a long

time, or performing an action is sluggish, the perceived performance is poor. If the app feels fast and responsive,

perceived performance is good. Sometimes the perceived performance can be improved easily, for example by

changing the duration of an effect, and other times larger changes are required, like deferring the loading of data.

Page 21: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Defer Creation Policies

Defer the creation of view components that are not immediately visible. Creating and rendering large view

components can be expensive, harming perceived performance. Instead, create views as they are needed, taking

advantage of the default creation policies of containers such as the ViewStack and Accordion.

Note that it is occasionally justified to create hidden views up-front, using an =all= creation policy, but this is the

exception to the rule. If a view that is initially hidden participates in a transition, then the perceived performance of the

transition may be improved by creating the view up-front.

Create Lightweight Item Renderers

List-based components like DataGrid, Tree and List create many item renderers up front and redraw them whenever

changes occur in the data provider. For this reason, it is important that item renderers are lightweight in terms of

creation time and redrawing.

For purely textual item renderers, use or extend the default item renderers for the list-based component. i.e.

DataGridItemRenderer, AdvancedDataGridItemRenderer, etc. For more complex item renderers, containing multiple

text fields, graphics and other assets, prefer to extend UIComponent in ActionScript. Make use of lightweight

children, such as TextField and Sprite, instead of Label and Image. Avoid using Containers for item renderers unless

the simplicity outweighs the loss of performance.

Make Effects Snappy

Effects that play too slowly make an application feel slow, particularly when they occur regularly. Fine tune the effect

play speen to make sure the app feels snappy.

Parallelize Service Requests

Applications often perform many different service requests, particularly during start-up, when the user profile and

initial data sets are usually loaded. These service requests are often independent of one another, so can be performed

concurrently. If there is no need to wait for the result of request A before starting request B, then don't wait and start

request B immediately. The ParallelTask feature of the Cairngorm Task library may help you to achieve this.

Load Data on Demand

Load data on demand when it is needed by the user, instead of upfront. For example, load only the summary

information for a collection of data items, then load the detailed data for individual items as the user navigates into

them. For large collections, use paging to fetch only enough data to render the currently visible rows in a list-based

component. Adobe products such as LiveCycle Data Services provide paging and lazy-loading features.

Be Careful with StyleManager

Operations involving the style manager can be expensive, sometimes causing applications to become unresponsive

while style updates are processed. In particular, using the setStyleDeclaration or loadStyleDeclarations

methods with the update parameter set to true will cause an immediate update of all styles in the whole application,

which can be time consuming.

The best practice is the set the update parameter to false whenever you can. For example, if you are loading a mode

and corresponding compiled stylesheet SWF at runtime, don't add the module to the display list until after the style

loading is complete. Then there will be no need to refresh the styles and the update parameter can be set to false.

2. CPU Utilization

A well-implemented Flex application should have low and stable CPU when the application is idle, with short peaks

in CPU during periods of high activity. For example, when viewing a static publication the CPU should remain low

and stable, but when navigating to a new area of the application for the first time, the CPU should be expected to spike

Page 22: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

while the new views are created and rendered. The following are guidelines for achieving the expected CPU profile

for a large Flex application.

Minimize Redraw Regions

Use the "Show Redraw Regions" feature of the Flash Debug Player to see how much redrawing is occurring. Optimize

your application so that only the parts of the screen that you need to change are redrawn. Take particular care with:

Animated SFWs - if an animation has been added to the display list, it will continue to cause redrawing, even

when it is not visible. To avoid this, remove animations from the display list when they are not needed.

List-based components - By default the List, DataGrid, AdvancedDataGrid and Tree will be redrawn entirely

whenever a property of an item in a data provider changes. This can be avoided by following the instructions

in this blog post: Optimizing the DataGrid for Frequent Updates

Minimize Background Processing

The amount of ActionScript processing that happens during each frame affects CPU. Most logic in a Flex application

should be short-lived and happen in response to a user gesture. However, sometimes regular processing is performed

in the background, perhaps in response to frame events or timers. Take care to minimize background processing, since

it can accumulate and push up the CPU.

Patch UIMovieClip for Stateful Skins

A good practice for skinning components is to produce stateful vector skins in Flash and export them with the Flex

Component Kit. All skins produced in this way use UIMovieClip as their base class, as described in the Design Spec.

However, pre-Spark versions of UIMovieClip perform sizing calculations on every frame which causes an

accumulation of CPU. For Flex 3.x projects, this can be avoided by extending UIMovieClip and removing the event

handler, as described by Guillaume Malartre. A consequence of his approach is that some skinned components may

need to be explicitly sized.

Don't Play Effects Excessively

Playing effects consumes CPU because of the processing and redrawing that takes place during the effect. In

applications, it is best to use effects occasionally and not frequently or continuously. For example, using effects to

animate prices in a price grid that changes every second is not advisable.

3. Memory Consumption

When a Flex application is running, Flash Player grabs the memory it needs from the operating system. It uses

memory for storing the class definitions and object instances for your application and also for rendering to screen. A

well-implemented Flex application will maintain an acceptable memory footprint by managing its class definitions

and object instances, so garbage collection can take place effectively. The steps below can help to achieve this.

Unload Modules Effectively

For large, modular applications, the lowest memory footprint can be achieved by unloading modules when they are no

longer needed. However, there are some known issues that can prevent modules from unloading effectively. Conduct

memory profiling and follow the advice from the following blog posts to ensure that modules are unloading

effectively:

How to Unload Modules Effectively

Garbage Collection & Memory

Using the Flex Profiler

More On Finding Memory Leaks

What We Know About Unloading Modules

Note that in some cases, the better approach is the load modules once and then leave them in memory. The trade off is

a higher memory footprint but more responsive navigation between modules. Loading once and leaving in memory

also prevents memory leaks to do with loading duplicate modules into memory.

Page 23: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Remove Singleton Event Listeners

A common cause of memory leaks is to attach event listeners to singletons that reference short-lived objects. For

example:

MySingleton.instance.addEventListener("something", somethingHandler);

The code above creates a reference from the singleton to the object with the somethingHandler function. That

prevents the object from being garbage collected until the singleton itself becomes eligible. But since the singleton

instance is stored in a static class variable, it won't be garbage collected until the class definition is garbage collected.

And that won't happen until the application domain containing the class definition is garbage collected! So, use weak

event listeners when listening to singletons or else explicitly remove event listeners afterwards.

Consider Reusing Popups

The Flex PopupManager class is usually used for opening and closing popup windows. It is common to create a new

instance of a popup each time it is opened. This can cause a memory leak when the popup is later closed. Unless all

references to the popup and its children have been removed, the popup will not be eligible for garbage collection. The

next time the popup is shown, a new instance may be created and again never garbage collected.

Avoid popup-related memory leaks by either:

1. Conducting memory profiling to ensure that the popup instances and all their associated objects are effectively

garbage collected.

2. Reuse a single instance of the popup for showing the popup multiple times.

There is a trade-off here, since option 1, when implemented correctly should produce an application with the lowest

memory footprint. However, option 2 will prevent memory leaks and result in faster perceived performance when the

popup is re-opened. The Cairngorm Popup library includes declarative tags for managing popup opening and closing

that support reuse.

Find Memory Leaks by Comparing Instance Counts

The Loitering Objects view provided by Flash Builder Debugger can sometimes contain false positives, because an

application may legitimately have different instances, but the same instance-count, between two memory snapshots. In

these situations, a better methodology for finding memory leaks is to compare the instance counts between two

snapshots. Alex Harui describes this process on his blog post: More On Finding Memory Leaks

Profile The Export Build with Release Player

Beware that the Flash Debug Player consumes more memory when running an application than the release Flash

Player, due to the instrumentation carried out for debugging and profiling. For this reasons, you should export a

release build and profile your application in the release Flash Player using system tools such as Task Manager,

Activity Monitor, Process Explorer, etc. These tools will give an accurate representation of the memory footprint

experienced by users. Revert back to the Flash Builder Profiler to identify memory leaks and performance bottlenecks.

Conclusion

For performance tuning, there are some best-practices that should just be followed as a matter of course, like using

deferred creation policies and developing with a modular architecture. There are others that involve additional effort

and are not always justified, like replacing easy-to-read with cryptic bitwise operations.

When a performance optimization is likely to complicate the code, it's worth considering whether there might be a

more elegant solution. For example, one might be tempted to introduce a pseudo-threading library to simulate

multiple-threads when translating large quantities of data, but the better solution is not to serve the client with a large

quantity of data in the first place. Instead, paging and lazy-loading can be used to serve smaller quantities of data on

demand.

Page 24: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Best Practices for Logging

Introduction

The Flex Logging Framework is easy to learn and flexible to use. It supports many different scenarios, from helping

developers to debug their code, to sending the details of production application errors over the wire to a remote server

for monitoring. To learn the basics of the Logging Framework refer to the latest Flex Developer Guide and the Flex

Logging Framework chapter of Professional Flex 3. This document describes some best practices for applying the

Logging Framework on enterprise projects.

The APIs provided by the Logging Framework are simple, but they need to be used properly to get the best out of

them. If care is not taken, the benefits that logging can provide for debugging and monitoring an application in

production can be lost. Performance problems can even be created. This article provides a set of best practices to keep

the developers in your team on the right track.

Best Practices

The following best practices are covered:

Get Loggers by Class

Declare Loggers as Static Constants

Format Log Statements Consistently

Parameterize Log Statements with Tokens

Use Log Levels to Indicate Severity

Use Log Filters for Focus

Include Categories to Show Class Names

Use Guard Conditions Appropriately

Configure Logging at Runtime

Get Loggers By Class

Use a simple utility method to retrieve the logger for a particular class, instead of passing in the qualified class name

as a string.

Good:

private static const LOG:ILogger = LogUtil.getLogger(MyClass);

Bad:

private static const LOG:ILogger = Log.getLogger("my.package.MyClass");

With the utility method approach, the class name can be refactored without needing to edit the string. Here is an

implementation for the LogUtil.getLogger() method:

public static function getLogger(c:Class):ILogger

{

var className:String =

getQualifiedClassName(c).replace("::", ".")

return Log.getLogger(className);

}

If performance profiling shows this method call to be a performance bottleneck, you may decide to revert to passing in

the class name manually, since this will run a little faster. However, in most cases the convenience and refactor-ability

of the above approach is the best practice.

Declare Loggers as Static Constants

In most cases, log statements apply to a particular class, so a logger should be declared as a static constant and not an

instance variable.

Page 25: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Good:

private static const LOG:ILogger = LogUtil.getLogger(MyClass);

Bad:

private var log:ILogger = LogUtil.getLogger(MyClass);

Format Log Statements Consistently

Log statements should be formatted consistently and not haphazardly. This ensures that logging code looks

professional and improves readability of log files (for humans or machines).

Good:

LOG.error(

"Something bad has happened: event={0}, message={1}",

event.type,

message);

Bad:

LOG.error("----------- SOMETHING BAD HAS HAPPENED! -----------------");

Parameterize Log Statements

Parameterize log statements using the rest parameter and tokens, instead of appending strings manually. This produces

cleaner code and prevents the composite string from being assembled in the event that no log target is registered.

Good:

LOG.debug(

"Something interesting is happening: event={0}, message={1}",

event.type,

message);

Bad:

LOG.debug(

"Something interesting is happening: event=" +

event.type +

", message=" +

message);

Use Log Levels to Indicate Severity

Use the debug/info/warn/error log levels to indicate the severity of the message, instead of emphasizing important

messages with special characters.

Good:

LOG.error("The service has failed and no data is available.");

Bad:

LOG.debug("!!! ERROR !!! The service has failed !!! ERROR !!!");

Use Log Filters for Focus

Set the log filters on your logging targets in order to focus on the logs produced by a certain part of the system. Do not

instead try to make certain log statements stand-out with special characters. Remember to keep the format of log

messages consistent.

Good:

target.filters = [ "my.important.package.MyClass" ];

Page 26: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

target.level = LogEventLevel.INFO;

...

LOG.info("My important message");

Bad:

LOG.debug("----------- My really important message! -----------");

LOG.debug("<<<<<<< another super important log! >>>>>>>>");

LOG.debug("************* CAN YOU SEE ME????? ***************");

The trouble with the "special character" approach is that what is important for one developer one day is different to

what is important for another developer on another day. If every developer uses their own notation for making their

logs stand-out, the resulting log file becomes harder to read than when no emphasis has been placed in the text. Log

levels and filters provide a more controllable and consistent mechanism for the same purpose.

Include Categories to Show Class Names

If you want to see the name of the class issuing a log statement, include categories for your log targets. Do not instead

hard-code the name of the class into log statements.

Good:

target.includeCategory = true;

...

LOG.debug("Executing command");

Bad:

LOG.debug("<<< Executing SynchronizationCommand >>>");

The bad practice above is not refactor-safe. If the class is renamed, the message becomes confusing and if categories

are included in the output, part of the message becomes redundant.

Use Guard Conditions Appropriately

Use guard conditions to prevent unnecessary processing where a log statement is expensive or nested within a loop or

iteration. However, do not use guard conditions around every single log statement since this clutters up code.

Decide whether or not the log statement may be expensive or use the profiler to verify this.

Good:

if (Log.isDebug())

{

LOG.debug("Result received: {0}", ObjectUtil.toString(model));

}

for (var i:int = 0; i<10000; i++)

{

if (Log.isDebug())

{

LOG.debug("Blah blah blah: i={0}", i);

}

}

Bad:

LOG.debug("Result received: {0}", ObjectUtil.toString(model));

for (var i:int = 0; i<10000; i++)

{

LOG.debug("Blah blah blah: i={0}", i);

...

}

In the bad practice above, the model will be converted into a string even if there is no registered target for the debug-

level. Similarly, 10,000 log statements will be issues inside the loop regardless of whether or not there is a target

registered.

Page 27: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Configure Logging at Runtime

Configure logging at runtime is a best practice, since it allows developers to change their log filters without rebuilding

and also allows logging to be reconfigured in production without redeploying. Different log settings can even be

applied to different users.

The process for configuring logging at runtime is beyond the scope of this article, however, the Parsley 2 Application

Framework provides a feature for doing precisely that by loading an external XML file that specifies the targets, levels

and filters.

There are also examples available showing how to do the same thing using Spring ActionScript.

Genuinely Reusable Presentation Components

On larger projects and within enterprises, there's often a case for extracting a set of reusable presentation components

into a Flex library project. In theory, the same components can be reused across modules and sub-applications of

multiple Flex or AIR clients, bringing greater consistency and more rapid development. However, in practice there are

some common mistakes that limit the reusability of components. This post explains what makes a component

genuinely reusable and highlights some techniques from the Flex SDK that can be applied to your own components to

make them more reusable.

What Makes a Component Genuinely Reusable?

There are different levels of reusability, but a fully reusable component should be able to render any kind of data. It

should be equally comfortable with an array of basic, dynamic Objects or a collection of concrete Kangaroos. The

Flex DataGrid has this quality:

<mx:DataGrid dataProvider="{ kangaroos }">

<mx:columns>

<mx:DataGridColumn headerText="Name" dataField="name"/>

<mx:DataGridColumn headerText="Weight" labelFunction="calculateWeight"/>

</mx:columns>

</mx:DataGrid>

Notice how the dataField and labelFunction properties tell the component how to get its data from the Kanagoo

objects without imposing any dependency. These are two of the mechanisms available to make a component genuinely

reusable. Even if the developer has no control over the Kangaroo class itself, perhaps it's part of a 3rd-party library,

they can still easily render these objects within a DataGrid.

The Data Interface Anti-Pattern

One common mistake is to insist that the data being rendered by a component implements a specific interface. For

example, consider a DistributionBar component that renders a simple graph, like that shown in Figure 1.

The distribution bar shows a number of regions with different sizes, each containing a label. It's tempting to configure

this using an array of IRegion objects:

public interface IRegion

{

function get label() : String;

function get size() : int;

}

The distribution bar can then extract the size and label information for each region through this interface. The rationale

for this is that the interface decouples the component from the concrete objects it renders. Anything can be rendered so

long as it implements IRegion, but that so long is in fact the design flaw. By imposing the use of the IRegion interface

the reusability is limited. The interface needs to be added to existing model classes before they can be rendered in a

distribution bar, and worse, if the models were produced by another library or separate team, it might not be an option

to change them, so they'd need to be wrapped. For these reasons, the component is not genuinely reusable.

Page 28: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

Reusable Components of the Flex SDK

The Flex SDK contains many reusable components and this is achieved by applying a few standard approaches:

1. Data Fields

2. Data Functions

3. Data Descriptors

4. Factory Objects

These approaches are now described and the same techniques can be applied to your own components to make them

reusable.

Data Fields

A data field is a String property that specifies the name of another property. For example, the labelField property of

the ComboBox or the dataField and dataTipField properties of the DataGridColumn:

<mx:ComboBox dataProvider="{ items }" labelField="name"/>

The component implementation uses the data field to read the data values from the items it renders. For example:

for each (var item:Object in dataProvider)

{

var value:Object = item[dataField];

// do something with the value

}

This is a simple approach but it offers great flexibility. The component can render any readable property of any class

of object.

Data Functions

A data function is a property of type Function that is used to specify a reference to another function. For example, the

labelFunction property of the ComboBox or the dataFunction property of the DataGridColumn.

<mx:DataGridColumn headerText="weight" dataFunction="calculateWeight"/>

The component then invokes the data function, typically passing through an item of data as a parameter. For example:

for each (var item:Object in dataProvider)

{

var value:Object = dataFunction(item);

// do something with the value

}

This approach is similar to using a data field, but offers even more flexibility, since the function can perform

calculations or formatting before returning a value to the component for rendering.

Data Descriptors

A data descriptor is an interface through which a component can analyze the items of data it renders. The developer

can then pass their own implementation of the interface to the component in order to configure it. An example can be

seen in the Tree component of the Flex SDK:

<mx:Tree dataProvider="{ items }">

<mx:dataDescriptor><my:MyDataDescriptor/></mx:dataDescriptor>

</mx:Tree>

The tree can then discover characteristics of the data by querying its data descriptor interface. For example:

for each (var item:Object in dataProvider)

{

var isBranch:Boolean = dataDescriptor.isBranch(item, dataProvider);

// do something with the outcome

}

Page 29: Cairngorm Guidelines - UFPEcin.ufpe.br/~hama/Arquivos/Cairngorm Guidelines.pdf · Flex SDK in general, regardless of your application framework of choice. These guidelines and best

This approach is very powerful, but is only necessary for complex components such as the Tree. The effort of using

the component is more than a simple List or ComboBox, but the component is still completely decoupled from the

data it renders. If a developer needs to render a new class of object in a tree, they typically write a new implementation

of the ITreeDataDescriptor interface.

Factory Objects

A factory object, in terms of component developent, is a property of type IFactory that is used to instantiate children at

runtime. For example, the itemRenderer property of the List and DataGrid or the dropdownFactory property of the

ComboBox.

<mx:List dataProvider="{ items }" itemRenderer="my.package.MyItemRenderer"/>

The component uses the standard IFactory interface of the Flex SDK to create new objects at runtime:

var itemRenderer:Object = itemRenderer.newInstance();

Then it passes data items into the new object through the IDataRenderer interface:

if (itemRenderer is IDataRenderer)

{

IDataRenderer(itemRenderer).data = item;

}

This approach gives great control over the visual appearance of parts of the component. By providing custom item

renderers, completely different results can be achieved. The logic for processing the item of data can be as simple or

complex as needed and it can be encapsulated inside the item renderer class. Having said that, components that use

factories should define sensible default values, so the component is easy to use in simple cases without setting special

factories. This is the case for all the ListBase components, such as DataGrid, which uses the general purpose

DataGridItemRenderer by default.

It's worth noting here that the Flex compiler has a special relationship with properties of type IFactory. When it

notices such a property being set in MXML, it will automatically generate code to convert class names and in-line

components into instances of ClassFactory. This makes the components easier to use, so developers don't usually need

to instantiate class factories manually, but instead just specify a class name or declare an in-line component.

Performance Considerations

Since fully reusable components tend to use mechanisms such as dynamic property lookups and function references,

there are some trade-offs to consider. These techniques are slower than accessing properties of strongly-typed objects

and they're not resiliant to compile-time type checking. However, the advantages of flexibility and reduced

dependencies can outweigh these drawbacks for larger projects and enterprises.

Conclusion

When fully reusable components are needed, it's good to remember the simple rule: a reusable component should be

able to render any kind of data. This is best achieved by following the conventions laid out in the Flex SDK, such as

data fields, data functions, data descriptors and object factories. It's important to resist the urge to introduce new

interfaces that impose unnecessary obligations on users of your component, because to do so limits its reusability.

Quality Guidelines

Prefer small classes with high functional cohesion (every method refers to all properties).

TODO: add a lot more content.