Adobe Flex development is different from traditional Web development in a number of ways. These differences, when properly understood and leveraged, can make a much richer user experience, which in turn can increase the usability of a Web site as well as increase traffic and conversion rates. Flex also provides a large library of components and technologies that make Web development easier—powerful tools that far surpass traditional Web 2.0 Asynchronous JavaScript and XML (Ajax) applications. In addition, Adobe’s recent release of Adobe Flash Builder 4 beta (formerly Adobe Flex Builder) provides many new and improved tools for developing large-scale Flex applications. Flash Builder 4 focuses on improving developer productivity and the workflow between designer and developer.
One of the keys to Flex development is using modules and Runtime Shared Libraries (RSLs), which provide benefits ranging from parallel development of different parts of the system to more efficient memory management on the client. Another key is leveraging the Spring BlazeDS Integration, which greatly simplifies server-side development as well as integration with a variety of technologies in the back end, such as messaging with the Java Messaging Service (JMS) and Spring Security.
Here are the keys to Flex development:
- Leverage Flex’s differences. Understand why Flex development is different from typical Web development, and leverage those differences to the benefit of your application.
- Flex modules. Understand Flex modules as well as how to modularize Flex development and deployment. Use Flex modules to decouple the different layers of your project.
- Know anti-patterns and patterns. Know proper application anti-patterns up front so that you can implement good design following proper architecture patterns.
- Simplify development with the Spring BlazeDS Integration Project. Simplify Java server integration with the Spring BlazeDS Integration Project.
- Design effectively. Work closely with your design team to fully leverage the strengths of Flex and Adobe Flash.
- Testing. Plan up front for testing.
What Makes Web Development with Flex Different?
Unlike traditional Web applications, Flex applications are developed much more like traditional desktop applications. For example, the entire Flex application is downloaded to the client—potentially a sizeable download, depending on how the application is structured. There are, however, a number of techniques that can help optimize this process, such as using component libraries and breaking the project into modules.
This methodology differs from traditional Hypertext Markup Language (HTML) applications—even those developed with Ajax. An HTML Web site is most often divided into different pages, making download sizes fairly small. And even when using Ajax, download sizes are still relatively small, because most Ajax libraries are themselves relatively small. This low profile makes the overall size of the individual pages small compared to downloading an entire application.
Another difference between Flex and traditional Web application development is that state is primarily maintained on the client. This goes beyond adding Ajax controls to a Web application, which commonly only maintains a small amount of the client state in the Web browser. Now, the entire state of the application is maintained in local variables.
Yet another difference that can be a challenge if you are new to Flex is that it uses a highly event-driven style of programming, primarily because Adobe Flash Player is single threaded, so any long-running calls need to register a callback function. Being single threaded is advantageous for graphical user interface (GUI) development, because it keeps the GUI responsive by not allowing for deadlocks or a thread stealing all the CPU cycles. It also makes development much simpler, because you don’t have to deal with multi-threaded programming. (Remember the nightmare of dealing with threads in Swing?)
The primary mechanism for dealing with events in Flex is function pointers, or closures that are passed as parameters to a method. Such pointers can be a user registering for an event or a notification when an asynchronous event finishes. (Function pointers can be challenging for Java programmers, because the core Java language does not yet support closures.)
Flex Modules Are Critical
Flex provides a number of options for dividing an application into modules, including creating libraries and sub-modules of the main application. By setting up the project in a hierarchal fashion, development and compilation can happen on the individual branches.
The primary benefit of modularizing a Flex project is that doing so allows you to optimize the download and startup time on the client. A small part of the application can be downloaded to the client, with the rest of the application loaded as needed in the background. Also, modules can be loaded and unloaded to minimize the amount of memory used on the client.
The other primary benefit of modularizing a Flex project that is it allows easier modularization of the development process. Allowing developers to work on different modules and libraries can prevent deadlocks in the development team.
One challenge of building large Flex applications, however, is that the compile process is much slower than what many developers are used to with Java. Recompiling a large Flex application can take several minutes. To optimize this process, you can build separate libraries and only compile the parts of the application you are currently working on.
One method of doing so is by using libraries in the form of SWC files. SWC is the primary archive format used by Flex and is similar to Java Archive (JAR) files in Java. You can use an SWC archive for a number of purposes:
- Component libraries. A component library is an SWC file that contains classes and other assets that the Flex application uses.
- Themes. Themes define the look and feel of an application and consist of the assets for the theme (such as image files and fonts) along with the Cascading Style Sheet (CSS) that defines how they are applied to the application.
- Resource bundles. These are sets of localized property files and Adobe ActionScript classes.
There are two ways of including SWCs in your project: You can either statically link to them, in which case they are compiled into your project, or they can be used as an RSL. The advantage of using RSLs is that they are separately downloaded and cached on the client. Also, if the client has multiple modules, they can all share the same RSL.
You can also modularize the main application. One method for doing so is by using Flex modules; another is by using sub-applications. The advantage of sub-applications is that they can be developed and tested independent of the primary application.
Note that when compiling modules and libraries, the Flex compiler does not walk the tree in a hierarchal fashion as in Java. Instead, it only compiles based on what links into the main application. This behavior can make developing modules and libraries tricky, because you may not be aware that a file is not being linked in. When developing libraries, it is necessary to define which files are to be included.
Modules have the advantage of being relatively straightforward to work with. Essentially, you pass to the Flex module manager the URL of the module you want to load:
protected function getModuleFromServer(url:String, onSuccess:Function) :void { var module:IModuleInfo = ModuleManager.getModule(url); module.addEventListener(ModuleEvent.READY, loadModuleSuccessHandler); module.load(); } public function loadModuleSuccessHandler(moduleEvent:ModuleEvent) :void { var module:IModuleInfo = moduleEvent.module; .... handle initializing the module }
The interesting thing with load modules like this is that you could specify alternative URLs for loading the module—for example, in scenarios such as failover, where the client has lost the connection to the primary server. In such a case, the client could still load the module from an alternative URL to maintain critical business functions.
Making the Application Easy to Develop and Test
By enabling a proper view stack, you can easily reach any spot in the application with a single URL—a feature that has advantages beyond mere usability. It also makes it easier to work with the application. A common problem is when you need to change part of the application that requires navigating through several screens and forms. This process can be time-consuming if you have to navigate manually for every change. Instead, using a fixed URL to access different parts of the application can greatly ease development pain and speed up the process. This same pattern also makes the application easier to test, because it makes individual parts of the application reachable and testable by URL.
With Flex, there are two ways to build view stacks into an application. One is using states, and the other is using a view stack component (ViewStack.mxml). View stacks are simply a stack of display objects: They can be a canvas wrapping a simple display form or a complex component. The nice thing with view stacks is that you can easily manipulate them from code by setting the current child. For example myViewStack.selectedChild=accountInfo would set the view stack to show the accountInfo view. This functionality allows you to easily manipulate the view stack for testing and development purposes.
Client Architectures: Anti-patterns
In software programming, a common paradigm is defining anti-patterns and patterns of software development. Patterns are repeatable solutions to common problems; anti-patterns are some of the most common mistakes programmers new to a language make. By defining the patterns and anti-patterns for Flex, I show some of the most common mistakes novice Flex programmers make, then show some of the best practices that have been developed for Flex programming. These practices help a project grow to large scale by helping you avoid common pitfalls.
The first common anti-pattern is placing all the MXML views in the root package. What is not obvious is that you can place MXML files in packages and reference them by their name space—possible because the actual package name is not specifically named in the file. What happens under the covers is that the MXML file is put in a package based on the directory structure it is in. So, leaving the MXML files in the root package is equivalent to not putting them in any package, which can create a mess with larger projects.
As an example, the Flex Store Sample puts not only the main application file in the root package but also the home, products, and support (HomeView.mxml, ProductsView.mxml, SupportView..mxml) all in the root package. This is similar to putting all of your view packages in the root directory in a Java package.
A common problem developers face when creating their first application is how to wire an application model with the view. A common anti-pattern is to pass the model between classes or require that it be set before calling a view. An example of this anti-pattern—again from the Flex store—is in the main application the catalog passes into the ProductsView:
<ProductsView id="pView" label="Products" catalog="{catalog}"
This code works well for small samples, but as the application becomes larger, it creates problems, such as tight coupling between the layers, and makes it difficult to develop and test. Another problem occurs is that when the same data needs to be displayed in different parts of the application, the data has to be passed separately to each component. At first, this might not seem like a problem, but imagine trying to mock out each instance where the data is passed and used.
Client Architectures: Recommended Patterns
Many architectures and frameworks have been developed for Flex over the years. One of the most promising uses a light dependency injection framework like Swiz to separate the project structure. With Swiz, you define application beans, similar to Spring, that are injected into the different part of the application. A typical application structure would then consist of a central class that defines how the services are wired together—for example, a ClientBeans.mxml class that declares the common model and controllers for the application:
<model:StoreModel id=”storeModel” />
View classes then are wired to controllers:
[Autowire(bean="storeModel")] [Bindable] public var storeModel: StoreModel;
You could then do the same thing for controllers and other common components. There are a number of advantages to laying out the project structure in this fashion. One is that it allows for mock services to be easily plugged into the controller, which facilitates both development and testing. Another advantage is that it eliminates coupling between the different layers of the client architecture.
Having a well-designed main view state helps to achieve the previously mentioned goals of usability and testability. The idea with the view stack is to make any view in the application reachable by a single URL. In addition, the parameters for the view stack need to be passed in via a common controller, which allows you to use a common URL with the desired view and necessary parameters throughout the application.
A Flex Architecture Example: Re-factoring the View Store
As an example of the architecture, let’s walk through the first steps of refactoring the Flex Store sample found at http://www.adobe.com/devnet/flex/samples/flex_store/ into a proper architecture.
First, move the main source code into subfolders. In this example, I moved all the assets, productsView, and samples directories into flexstore/src/flex as the main source code directory. I also moved all the MXML and CSS files into this directory.
Next, move the three main view classes into a view directory. Under the src/flex directory, create a com/anvilflex/view directory. Within this directory, place the HomeView.mxml, ProductsView.mxml, and SupportView.mxml files. To do so, you must make the following changes to flexstore.mxml:
- Add
xmlns:view="com.anvilflex.view.*"
to the<mx:Application>
tag. - Change the views to use the
<view>
tag (so<HomeView>
would become<view:HomeView ... />
).
Move the Product.as
class into a separate folder called domain - com/anvilflex/view. To do so, you must fix the import
statements of the Product
class. The easiest way to fix these in Flash Builder is to delete the existing import
statements, then delete the last letter of the Product
word. Then, press Ctrl-Space to use auto-completion, which also adds the correct import for the Product
class.
Finally, introduce a ProductControl
to manage transition between the product pages and checkout.
Simplifying Java Development with Spring BlazeDS Integration
The Spring BlazeDS Integration project greatly simplifies the development of the Java server code to interact with the client. With the latest release of this project, one team member can implement the core configuration files, such as web.xml, and the Flex services configuration files. Then, team members simply need to understand how to add the proper annotations on their class to expose them as services to BlazeDS. As a simple example, you can expose a Product Service
class as a remote service:
@Service("productService") @RemotingDestination(channels={"my-amf"}) public class ProductDAO { @Autowired public ProductDAO() { //... initialize the ProductDao class } @RemotingInclude public Product findProductName(int id) { Product product = database.findProduct(id); return product.name; }
In this example, I declared the ProductDAO class as a remote service that can be called using productService. The individual methods are exposed as remote methods using @RemotingInclude. Underneath the covers, these methods are exposed to BlazeDS as remote services that Flex can call into via remote objects.
Working with a Graphical Design Team
A common problem with new Flex projects is ensuring that the graphical design team understands what is possible in Flex. Flex opens a lot of possibilities for interesting designs: The graphical design team needs to understand what is possible and how Flex represents a fundamental design shift.
Often, the challenge is that the graphical design team creates a mock-up using a tool like Adobe Photoshop. Although the design will look great, it is often a long way from a working application. The design will have to be cut up and the color and fonts matched in the CSS of the application. Also, depending on the complexity of the design, custom Flex components will need to be created. These components might be things as simple as buttons with custom skins or as complex as components that define a new set of functionality. The challenge with this process is that it can be extremely time-consuming going between design and implementation, figuring out what is possible, and implementing changes from design. Each iteration can become more complicated.
Fortunately, Adobe has introduced a new tool to help with this process called Flash Catalyst. It allows for artwork from a tool such as Photoshop to be easily turned into a usable prototype. It can also be used to create a prototype GUI that can eventually be leveraged into the final application. By using this tool, designers can much more quickly iterate their designs, which in turn allows the organization to more effectively tweak the design to meet its requirements.
Unit and Functional Testing
With Flex development, there are two primary ways of testing an application. The first is functional testing either using funit
or fluint
. This method allows for testing of the services and controllers. The second method is functional testing, which can test the actual interaction with the GUI. Functional testing can either use a commercial tool like Mercury or an open source tool like FlexMonkey.
They key to either type of testing is creating the code in loosely coupled layers that can be independently tested. For example the services can be easily mocked up by changing the default wiring to use mock services.
Conclusion
Flex with Spring BlazeDS Integration provides an excellent set of technologies for large-scale enterprise applications. The challenge for developers lies in designing the architecture so that it is modular, easy to test, and scalable. And by scalability, I mean not only the software but also the development process. Addressing the challenges up front can help make a project successful and deliver a higher return on investment for the organization.