In Flex, it's easy to make applications go. You've probably seen a number of examples. Go into Flex Builder, throw down some components, add some ActionScript functions to handle events, maybe a WebService or two, and bingo... you've got something that sort of works. The results are often amazing relative to the small amount of effort.
Is it all a little too easy to be true? That first proof-of-concept prototype comes together mighty fast in Flex, but what happens when you're building a production project that's ten times as ambitious? What happens when the boss finally looks at the prototype and asks for a bunch of changes after it's partly built? What happens when usability testing suddenly discovers a need to revamp the UI? What happens when your web services change from REST to SOAP? Can you write a robust working client before a server side even exists?
In short: Does the dream of easy Flex development crack up and fall apart when assaulted by real-world complexity, change, and schedule pressure? Well, it needn't—if you pay attention to the right details. A modest up-front investment in structuring your application thoughtfully will pay off later, big time, in the face of these challenges.
In this article, I'm going to share a high-level architecture for building real-world Flex applications, with the help of a complete working example application called ReviewTube. I've been building complex applications for almost three decades, which allows me a modest claim: I have made lots of mistakes. (Or, in President Reagan's classic formulation, "mistakes were made.") Somewhat less modestly, I claim to have learned from these mistakes over the years, and a lot of what I've learned can be applied to development with Flex. The architecture I describe below embodies many of these learned lessons, and the example application illustrates them concretely with working, documented code. I'll also throw in some useful Flex tips along the way.
See and experience the application.
To complete this tutorial you will need to install the following software and files:
Architecture is about looking at a system from a point of view that makes it appear simple, sometimes artificially so. An architectural viewpoint has to be simple to understand, to describe, to compare with other viewpoints, and, ultimately, to implement. The viewpoint might seek a broad approach to a big, sweeping problem. Then again, it might be a thematic idea that can generate a family of similar solutions to a crowd of similar little problems—that is, a design pattern. Either way, architecture provides a way of looking at the "conceptual essence" of something without getting bogged down in every detail.
Adopting such a viewpoint inevitably limits one's implementation choices. That can be good. If all options are open, ten similar problems in your project might get solved using ten different techniques. That can require up to ten times more work than solving them all with a single technique, outweighing the usually meager benefits of handcrafting each solution. It can take up to ten times as much effort for a new developer to understand how the code works. And when it comes time to change the approach(es), it might take up to ten times as much effort to make that change (since you'd have ten different starting points). So, limiting variation saves time, makes things much easier to understand, and lets you discover and factor out common code much more easily.
When we simplify enough, and in the right way, we wind up with a viewpoint that contains a small number of concepts to think about, each of which has a clear set of responsibilities, relationships, and interactions with the others. It points the way forward on multiple fronts. A viewpoint like that is usually a sign of a successful architecture.
In this article, I present an architecture with concrete, identifiable benefits and costs, relative to a specific example. This article does not demonstrate a "right" architecture for Flex. Don't make me laugh (or cry)! There is no architecture that is general enough to be called "right."
My view of architectures is that they are not "right" or "wrong"; they are "worth the effort" or "not worth the effort" for a particular job to be done. (Granted, some architectures actually are wrong. But those are the ones that really aren't worth the effort!)
There will generally be multiple architectures that work in a given domain. They can be compared with each other. You can say what's good about them and what's bad. And you can pick one that's worth the effort, knowing the limitations that come with it. So, although the approaches outlined here are appropriate in many different circumstances, be ready to adapt them, or even discard them, in favor of something that is "worth it" for your particular purposes. Above all, I hope to present an example of what "worth it" looks like, from a specific vantage point.
Enough high-flown concept-mongering, already! What are some general strengths that an architecture should endow our Flex applications with? For my part, I would like to:
To my mind the first goal, isolation and encapsulation, is the key to the others. Isolating concerns opens the door to accommodating change, because it puts boundaries on the ripple effects of changes within the code. Isolation makes modular development and testing easy. Isolation makes it easy to have different people work on different pieces. Finally, isolation makes it easier to envision ways of improving the user experience that are generalized and powerful.
Keeping in mind the above goals, I now introduce an architecture that can deliver it to us. It's called the "Model-View-Controller-Service" approach, or MVCS for short.
The MVCS architecture is centered on that first, primary goal: "isolate state, presentation, action, and communication." Essentially, you break the application into four clean, separable large-scale components, one to isolate and encapsulate each of those four concepts. (I'm using the term component here in an architectural sense, meaning a family of related objects that share an overall responsibility; I don't mean an MXML component.) Table 1 summarizes those big MVCS pieces.
| Component | Isolates concept | Encapsulates responsibility |
|---|---|---|
| Model | State | Model objects are what the application knows, comprising its state and its information about the world. When this state changes, events are dispatched to notify the View to update. |
| View | Presentation and Interaction | View objects are the interactive surface of the application, presenting live information in the Model to the user, and responding to user gestures by invoking actions in the Controller. |
| Controller | Action | Controller objects implement what the application does, modifying the Model to reflect those actions, invoking Services to communicate with the outside world, and making use of Views as needed. |
| Service | Communication | Service objects are how the application talks to the outside world, populating some objects in the Model with information received back from that world. |
Figure 1 shows the same pieces and the major interactions between them.

Figure 1. Visual representation of the MVCS architecture
But... this is already way more abstract than I'd like. It's time to fill in those empty boxes with objects that do real, tangible stuff, and introduce our sample application.
To help put our ideas in very concrete terms, I've written a sample application as a companion to this article. It's called ReviewTube (see Figure 2). A working example with the complete source code is available at joeberkovitz.com/blog/reviewtube. I recommend taking several minutes to play with it, since I'll often refer to its features. For completeness, and to set the stage for some later topics, here's a lightning tour of its features.

Figure 2. ReviewTube application
In short, ReviewTube is a site that allows users to create, edit, and view time-based comments for videos hosted by YouTube.com. Any visitor to the site may view such a video; the comments automatically appear and disappear beneath the video at the correct times during playback as closed captions. Only registered users may create new comments. Note that any registered user may add captions to any video, making ReviewTube a kind of collaborative annotation system.
Figure 3 shows a simple block diagram of the ReviewTube application.

Figure 3. Block diagram of the ReviewTube application
Video browsing and searching. ReviewTube videos may be accessed through the following:
Video display. Upon selecting a video from one of the above lists or supplying a specific YouTube URL, the video is loaded into a player. Under the player is an area in which active captions are displayed along with the name of the user who created them. Below that is a strip containing a slider control for adjusting the video playback time, which also shows the load progress and the time offsets of all captions. Finally there is a scrollable list box showing a list of all captions in time order; clicking a caption positions the video at the corresponding time.
Comment editing. A signed-in user may add a new comment at the current playback point. Users may also edit or remove any comment that they have created. New captions, edits, and removals are reflected immediately, even while server database operations are pending. Signing in or out causes the caption editing aspects of the UI to dynamically appear or disappear as appropriate.
Status display. Most ReviewTube actions involve operations on web services on the YouTube or ReviewTube servers, which may take several seconds to complete. While such operations are pending, a blue label appears in the upper right of the screen providing status information on what is happening. This kind of feedback often contributes greatly to making an application feel responsive.
Signin, signout, and registration. At the top of the screen is an interface for signing in, registering as a new user, and signing out. Signing in and registration are carried out through additional pop-up dialog boxes that display error messages and status.
Server communication. The ReviewTube app talks to two different servers: www.youtube.com and the ReviewTube server (a custom Rails application with its own XML/REST web services). YouTube.com supplies the video media and metadata, while the database on ReviewTube only stores the extra information about comments and registered users.
Now I'll return to MVCS, looking at ReviewTube and referring to specific features and objects within it. Our once abstract architecture diagram now looks like Figure 4.

Figure 4. Block diagram of the ReviewTube application
This diagram includes various elements that make up ReviewTube. For instance, VideoInfo is a Model class that represents the information known about some video clip; MediaService is a Service class that talks to YouTube. So I can now start to talk about specific examples of Model, View, Controller, and Service objects in more detail.
When I'm done with examining the MVCS elements of ReviewTube, I'll follow a single user action in ReviewTube all the way through the system, from the beginning to the end.
I'll kick off with our Model (see Figure 5), whose main job is to represent our application's state, and what it knows about the outside world. By walling all such knowledge off within the Model, one makes it easy to change our definition of what this state is, without having to change many other data structures throughout the application.

Figure 5. Application state in the ReviewTube Model
In ReviewTube, the Model includes the following:
In all cases, these are passive objects that hold pure information, with no visual or interactive aspects and no interaction with any other part of the system. Such objects are often referred to as value objects. Any change to the state of these objects should dispatch events so that interested View objects will update themselves.
Look at VideoInfo for an example of some key guidelines:
package com.joeberkovitz.reviewtube.model
{
import mx.collections.ArrayCollection;
/**
* Value object representing all known information about a Video.
*/
[Bindable]
public class VideoInfo extends ValueObject
{
public var reviewTubeId:int = -1;
public var providerId:String;
public var providerMediaUrl:String;
public var providerMediaUrlRequested:Boolean;
public var removed:Boolean;
public var creationDate:Date;
public var title:String;
public var duration:int;
public var description:String;
public var thumbnailUrl:String;
// elements are Comments
public var comments:ArrayCollection = new ArrayCollection();
public var commentCount:int;
public var modificationDate:Date;
}
}
This class illustrates some typical features of a model object:
com.joeberkovitz.reviewtube.model.[Bindable], ensuring that change events will be dispatched when any property is changed. For example, when a comment is added to a VideoInfo, the commentCount property is incremented. Because it's Bindable, a PropertyChangeEvent for commentCount is dispatched. This event causes the corresponding "# of comments" cell in a VideoBrowser's DataGrid to update itself accordingly. ArrayCollections are always used to represent lists, not Arrays. This ensures that additions, updates and deletions of items will dispatch CollectionEvents if change, keeping all Views up to date. For example, when a comment is removed from the comments property (whose elements are Comment model objects), a CollectionEvent is dispatched from that ArrayCollection. The caption display, the time strip, and the comment list all use this same ArrayCollection as a data provider, and the dispatched event causes each view to automatically remove its display of the deleted comment in its own way.In thinking about your Model classes and data structures, it's a really good idea to optimize for the benefit of Views. Many developers take pains to duplicate existing server-side data structures on the client side with virtually no change. I think this is often a mistake, because server-side object design is driven by a very different set of concerns, like getting the data in and out of a relational database, or supporting back-end business operations.
Don't just mirror the model on the server. Consider optimizing your client-side model objects so that the user interface is as easy as possible to construct. This means thinking hard about how events and data bindings will work, because these are the workhorses of successful Model/View communication. There may be some slight rearrangements involved in moving data back and forth, but they will often pay off in much simpler presentation code.
Let's move on to the View part of our architecture (see Figure 6). The View's job is to handle all details of interaction with the application's user. It references objects in the Model (which also notifies it of updates via events), and hands off actions to the Controller. It's not surprising that most View objects are defined in MXML, which is suited for presentation and UI event handling.

Figure 6. ReviewTube View architecture
I'll look at a very simple View object in ReviewTube, namely the LoggedInHeader component. This object is displayed at the top of the screen to a logged-in user, giving feedback on who is logged in ("Welcome, CutiePie14!") and offering a Sign Out button:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import com.joeberkovitz.reviewtube.services.Components;
private function logout():void
{
Components.instance.controller.logout();
}
]]>
</mx:Script>
<mx:Label styleName="userWelcomeLabel"
text="Welcome, {Components.instance.session.currentUser.username}!"/>
<mx:Button label="Sign Out" click="logout()"/>
</mx:HBox>
Some View principles are at work here:
userWelcomeLabel's text is bound to {...session.currentUser.username}. If the session's username changes for any reason, one can be sure that this view will adjust itself accordingly.logout(). There are absolutely no details here of the nature of the logout() operation, which means it can be changed as one pleases without altering the View in any way.Here is a somewhat different View example: CaptionView, which displays an active caption below the video. This view expects a VideoPlayback model object to be set into its playback property. It displays one CommentCaption view instance for each element in that video's comments. It also propagates the current playback time offset into all the CommentCaptions via a binding, allowing the captions to appear and disappear when their time range is matched:
<?xml version="1.0" encoding="utf-8"?>
<!-- Component presenting a subtitle-caption-like view of all comments,
of which only the active one is usually visible. -->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:view="com.joeberkovitz.reviewtube.view.*"
styleName="captionView"
verticalScrollPolicy="off" horizontalScrollPolicy="off">
<mx:Script>
<![CDATA[
import com.joeberkovitz.reviewtube.model.VideoPlayback;
import com.joeberkovitz.reviewtube.model.Comment;
[Bindable]
public var playback:VideoPlayback;
]]>
</mx:Script>
<!-- All CommentCaptions are overlaid within the canvas; however, only ones
whose time range match the current offset will be visible. -->
<mx:Repeater id="commentRepeater" recycleChildren="true"
dataProvider="{playback.video.comments}">
<view:CommentCaption width="100%" height="100%"
comment="{Comment(commentRepeater.currentItem)}"
videoOffset="{playback.offset}"/>
</mx:Repeater>
</mx:Canvas>
Some typical View approaches seen here include the following:
CaptionView does not grab its Model information from a global location. Rather, it accepts a model object from its parent through one or more properties—in this case, the playback property. This makes it easy to include multiple CommentCaptions in future UIs if, say, one wished to support simultaneous playback of more than one captioned video.dataProvider is used, in this case a Repeater. The dataProvider is an ArrayCollection (playback.video.comments), ensuring that additions, removals, and updates of Comment model objects will cause the Repeater to track those changes correctly by adding, removing, or updating CommentCaption view instances.playback.offset to each CommentCaption's videoOffset property will fire every time the offset changes, which is exactly what is needed to make the captions "watch" the offset and appear or disappear at the correct times.CaptionView does not have any knowledge of its container; it is merely passed the information it needs. It also has no knowledge of the internals of its children (CommentCaptions) beyond the properties that they require in order to function. This kind of encapsulation prevents Views from becoming dependent on knowledge of each other's presentation details. One could change the way a CommentCaption looks tomorrow and CaptionView could stay exactly the same.There are numerous other View components in ReviewTube, some of which contain much more complex data bindings and Controller interaction. But despite the extra details, the approach is much like what one sees in these simpler components.
The Controller is in some ways the center of the application: The place where things happen, where application logic and behavior are implemented. It is the only part of the architecture that communicates with all the other parts, thus relieving the View, Model, and Service packages from needing to know too much about each other—that isolation goal again (see Figure 7).

Figure 7. The Controller in ReviewTube
There are often good reasons to have several cooperating Controllers, but for simplicity ReviewTube has a single one (unimaginatively named "Controller"). Controllers are often singletons, meaning there is exactly one global instance in the application with responsibility for "doing stuff."
In general, it's a great idea to have every Controller class implement an interface; in ReviewTube, that interface is called IController. Here it is, with imports and most comments removed:
package com.joeberkovitz.reviewtube.controller
{
/**
* Interface to the ReviewTube Controller component. This interface
* only includes controller methods callable by the view package.
*/
public interface IController
{
// setters providing controller with important View objects
function set browser(vb:VideoBrowser):void;
function set statusLabel(l:Label):void;
// authentication-related actions
function initiateLogin():void;
function initiateRegistration():void;
function cancelAuthentication():void;
function login(username:String, password:String):void;
function register(username:String, password:String, email:String, description:String):void;
function logout():void;
function synchronizeAuthenticationStatus():void;
// search-related actions
function searchByProviderId(providerId:String):void;
function searchByTag(tag:String):void;
function findUser(username:String):void;
function loadReviewComments(video:VideoInfo):void;
// video and video list data retrieval actions
function loadMediaUrl(video:VideoInfo):void;
function getTopVideos():ICollectionView;
function getRecentVideos():ICollectionView;
// comment editing actions
function addComment(video:VideoInfo, text:String, startOffset:int, endOffset:int):void;
function updateComment(comment:Comment):void;
function deleteComment(video:VideoInfo, comment:Comment):void;
}
}
As you can see, this reads almost like a dictionary of all the features of the application. A well-structured Controller interface generally looks like that, because it's effectively a list of all the things that Views need to have done for them (but mustn't do for themselves, because they're just Views). This one is a little long, and it could be argued that it should be broken up into multiple Controller objects.
Now look at an abbreviated, simplified piece of the actual Controller implementation to get a sense of what goes on inside the Controller:
package com.joeberkovitz.reviewtube.controller
{
public class Controller extends EventDispatcher implements IController
{
[Bindable]
public var statusLabel:Label;
// Service references needed by the Controller
public var commentService:ICommentService;
public var mediaService:IMediaService;
/**
* Log out the current server session, returning it to anonymous
* status.
*/
public function logout():void
{
beginAuthentication(new LogoutPopup());
performAuthenticationOperation(commentService.logout());
}
/**
* Get the set of top videos (in terms of # of comments).
* @return a collection that will eventually be populated when
* the operation completes
*/
public function getTopVideos():ICollectionView
{
var dataProvider:ArrayCollection = new ArrayCollection();
performOperation(commentService.loadTopVideos(dataProvider));
return dataProvider;
}
/**
* Perform a remote asynchronous operation, displaying
* progress information and handling its eventual completion
* and possible error outcome.
*
* @param c the Operation to be performed
*/
private function performOperation(c:Operation):void
{
// show name of operation in status label
statusLabel.text = c.displayName;
// handle outcome events
c.addEventListener(Event.COMPLETE, handleOperationComplete);
c.addEventListener(ErrorEvent.ERROR, handleOperationError);
// initiate Operation
c.execute();
}
private function handleOperationError(e:ErrorEvent):void
{
Alert.show(e.text, "Error");
handleOperationComplete(e); // remove event from status display
}
private function handleOperationComplete(e:Event):void
{
statusLabel.text = "";
}
...
}
}
Some of the key ideas in the Controller code above are:
getTopVideos() function is called by the VideoBrowser to get hold of a list of the top videos in the system. The VideoBrowser doesn't have to worry about which services and servers participate in this operation, or about the service APIs that need to be called. The Controller just returns an ICollectionView that the View can use as a dataProvider, and that's enough.getTopVideos() call performOperation(), a workhorse function that takes care of showing status text and displaying error message alerts. Changing this function will globally alter all status and error handling across the application.logout() function (which we saw called from our earlier View example) makes use of a LogoutPopup view to modally block the UI while signout is taking place, and display status. Our LoggedInHeader view shouldn't know anything about these details. What if the app changed from using a popup to showing a message in status bar? This detail of the logout() action is encapsulated and hidden by the Controller, so that LoggedInHeader can stay the same in the face of such changes.Once again, from the original architecture diagram (see Figure 8). Service objects make up a layer of objects that handle all communication with the outside world. In ReviewTube, this means the application uses HTTP to talk to the YouTube and ReviewTube web services.

Figure 8. Service and Operation objects within the MVCS architecture
Walling off all these details within Service objects frees the rest of the application from concerns about how communication takes place, and leaves the communication mechanism free to evolve or change without perturbing other code. (For the most part, only the Controller works with Service objects.)
Besides handling the details of creating requests for remote servers, the service objects also figure out how to deal with the responses that eventually come back. Often these must populate the Model with received data. For example, after the CommentService makes an HTTP request for a list of top videos, eventually an XML response comes back. This XML document is decoded into VideoInfo model objects that populate an ArrayCollection.
As mentioned briefly above, Services act as factories for Operation objects; it is actually these Operations that do the work of communication by initiating requests, handling responses, and dispatching status and completion events. The resulting choreography between View, Controller, Service, Operation and Model is a bit complex but offers tremendous flexibility, and results in extremely simple UI code. Figure 9 offers a step-by-step breakdown of what happens when a VideoBrowser view calls the Controller's getTopVideos() method.

Figure 9. Breakdown of what happens when a VideoBrowser view calls the Controller's getTopVideos() method
There's a lot going on there. I'll break it down step by step. For more detail, you can follow along in the code starting in Controller.getTopVideos():
VideoBrowser calls getTopVideos() on the Controller in order to obtain a Collection of the top videos on the ReviewTube server. These are to be represented by VideoInfo model objects.ArrayCollection() and passes it to the CommentService's loadTopVideos() method.CommentService instantiates a new VideoQueryOperation, which inherits from ReviewTubeOperation, XMLHttpOperation, and Operation. This Operation will do all the work of forming a server request for the top videos and handling the subsequent response or failure. The operation's videos property retains a reference to the ArrayCollection which will eventually receive the data.execute() on the VideoQueryOperation returned by the CommentService. The Operation then makes an XML/HTTP request to the ReviewTube server.ArrayCollection as the result of the VideoBrowser's original call to getTopVideos(). The VideoBrowser uses this as its dataProvider property, although the collection is still empty for the time being.VideoQueryOperation to indicate progress, success, and failure. The Controller receives these and displays status in some number of dedicated status views (for example, its statusLabel which is set by the top-level application).VideoQueryOperation receives an XML response, it decodes it, placing the resulting VideoInfo objects into the ArrayCollection that it is still holding onto. Note that the VideoQueryOperation class provides common response-handling code to a number of similar requests, of which top_videos is one.VideoInfo objects are added to the VideoBrowser's dataProvider, CollectionEvents are fired. These events cause the browser's data grid to display additional rows as needed.This interaction pattern has a number of good things going for it. In keeping with a practical outlook, here are its main strengths as I see them:
Operation object is a useful abstraction of any potentially asynchronous action, to the point that most Controller code dealing with status/progress reporting and error handling can be written without regard to what an Operation actually is or does. Note that an Operation can in fact be synchronous, since its execution begins after interested parties attach listeners to it and call execute().I have sometimes been asked if an Operation object embodies the same idea as a Command object, in the sense of a UI operation that can be executed and, additionally, subjected to undo or redo. The answer is that although they are quite similar concepts, in practice they tend to be quite separate. An undoable Command represents a view's request for an action to take place in a Controller, while an Operation represents a controller's request for an operation to take place in a Service. In general, Operations subserve Commands, and both the execution or undo of a Command (within the Controller) will result in Operations being created and executed at a lower level (within a Service).
Why would you use an interface for the Controller and Services? Many developers struggle with the question of when interfaces are appropriate. Here are some of the main reasons to use interfaces:
One often sees "interface-ridden" code, where too many interfaces are actually getting in the way and providing little in the way of actual value. If the above factors are present (or are likely to be), use an interface; if they're not, maybe you shouldn't!
For example, the IController interface allows you to instantly swap in a mock controller implementation with no change to the rest of the application. Much of ReviewTube was actually developed using just such a mock IController, allowing me to work on the View and Model parts of the application even before the server side existed. A MockController class is included in the source as a simple example of this technique.
The MediaService and CommentService are exposed via interfaces, which makes it very easy to create new implementations while remaining sure that all the Controller's needs are met. If one wanted to expose other video providers besides YouTube and choose between them at runtime, it would be a piece of cake: Simply create alternate implementations of IMediaService and make use of the right one at the right time.
ReviewTube has a number of singletons: classes of which a single dedicated instance exists in the application. Prime examples are Session, Controller, MediaService, and CommentService, which are referenced globally from across the application.
Many of these singletons implement specific interfaces. In keeping with the whole point of using interfaces, other parts of the code should see our singletons typed as those very interfaces, typed as actual classes that implement them. For instance, when the rest of the application gets hold of the Controller, its type should be IController.
ReviewTube also needs to configure these instances by specifying their concrete classes and initial property values. What's needed is a convenient way for the developer to be able to provide these class names and values without writing a bunch of custom code—say, an XML file. (In the Java world, the popular Spring Component Framework is an example of this kind of thing.)
There's a neat way to do this in a Flex application, using a two different source files. The first is an ActionScript class named Components that exposes a static instance property. In it, one declares a property with an interface type for every globally accessible component, for instance:
package com.joeberkovitz.reviewtube.services
{
[Bindable]
public class Components extends UIComponent
{
/** Reference to singleton instance of this class. */
private static var _instance:Components;
public var controller:IController;
public var mediaService:IMediaService;
public var commentService:ICommentService;
public var session:Session = new Session();
public function Components()
{
super();
_instance = this;
}
[Bindable("instanceChanged")]
public static function get instance():Components
{
return _instance;
}
}
}
It's clear that other places in the code can refer to or bind to Components.instance.session, or Components.instance.controller to obtain references to the properties of Components.
A subclass of Components provides the actual definitions of these objects, however, in ReviewTubeComponents.mxml. As you can see, MXML is a really nice language for configuring classes and properties:
<!-- Services/Controller configuration file for ReviewTube -->
<Components
xmlns="com.joeberkovitz.reviewtube.services.*"
xmlns:model="com.joeberkovitz.reviewtube.model.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:controller="com.joeberkovitz.reviewtube.controller.*">
<YouTubeMediaService id="mediaService"
urlPrefix="http://www.youtube.com/"
developerId="[YOUR DEVELOPER ID HERE]"/>
<CommentService id="commentService"
urlPrefix="http://rt.morrisalumni.com/flex/"/>
<controller:Controller id="controller"
mediaService="{mediaService}" commentService="{commentService}"/>
</Components>
This MXML subclass is just what is needed: A nice XML file that sets up all of our singletons, declaring their classes and properties. Note that the id attributes of the various objects declared here correspond to the property names in the Components superclass. Now all one has to do is stick a <ReviewTubeComponents/> element somewhere in the top-level application and all this work is done.
Singletons are very handy access points for information that needs to be globally exposed. But they can be easily abused. It's convenient to add new properties and functions to singletons, when you want to pass information around in your program. Pretty soon, your singleton objects become ugly global grab bags.
Here are some tips:
In this article, I've described the MVCS architecture, which is a large-scale set of organizing principles that can guide and shape a Flex application as it develops, delivering a number of clearly stated goals. Paramount among these goals was the isolation of concerns regarding state, presentation, action, and communication; correspondingly, this isolation is the cornerstone of MVCS.
Although there is some cost in terms of planning and overhead, I have seen this approach return the investment many times over. It's easy to prototype your project even with huge pieces missing. It's easy to change the code with limited ripple effects. It's easier to break up the work into multiple parallel tasks—the resulting View and Model layers are simpler.
Now the accompanying ReviewTube application is my attempt to illustrate in the most concrete possible way how this architecture actually works in a real-life situation. However, MVCS is not a framework. Nor is it a hard-edged specification for specific classes and APIs. Rather, it is an architectural viewpoint, with many possible ways to apply it and to adapt it. ReviewTube shows us one such realization. In our development work at Allurent, we use a different and more complex flavor of MVCS that permits much of the code to be automatically generated from a master model description.
I recommend examining many diverse frameworks such as Cairngorm 2.0, Java Server Faces, and ARP. The rich design pattern literature abounds in ideas and examples. It's all great fodder for us, the developers and architects. Take the best, but be prepared to discard anything that you cannot successfully convince yourself is helpful.
I want to demonstrate a certain attitude toward building complex applications: The attitude that one can find, compare, and rationally select architectures that are "worth it." You can tell when an architecture is worthwhile because it simplifies the entire development process and makes it easy to construct and evolve a complex project. Here's to uncovering many more of those!
Joe Berkovitz is Chief Architect of Allurent, Inc., a company offering a suite of Flex-based online shopping applications. He has spent 28 years in the software profession as an architect, interface designer, and engineer. He was Chief Architect at startups Ruckus Network and Unveil Technologies. Before that he spent eight years at ATG developing many of the core components of that company's products, originating a number of key ideas with patents granted or pending. Prior to ATG, Joe worked at Houghton Mifflin; Stratus Computer; and Bolt, Beranek and Newman, among others. His experience includes consulting as well as product development. Joe has worked with such diverse clients as Harvard Business School, Sony, and BMG Music Club.