My best practice for MVC with Adobe Flex3

I often read in Flex-tutorials and -books, that it’s difficult to completely implement a project using the concept of the MVC (Model-View-Controller) pattern. It is being said that the fact that MXML files inherit both, application logic (through ActionScript) and the declarative way of building graphic user-interfaces using MXML syntax, makes it almost impossible to separate view and controller from each other. Thus, they say, both seem to melt together, which results in a Model-(View/Controller) design.

NOTE: THIS TUTORIAL WAS ORIGINALLY WRITTEN FOR ADOBE FLEX 3.
SINCE THE RELEASE OF FLEX4, THE CONCEPT OF MVC IS VERY WELL INTEGRATED INTO THE WHOLE FRAMEWORK.
IF YOU WANT TO USE FLEX4 AND MVC, I SUGGEST YOU HAVE A LOOK AT THE NEW SPARK COMPONENT MODEL!
HOWEVER, THE BASIC CONCEPT OF MVC, AS SHOWN IN THIS ARTICLE, STILL APPLIES (NOT ONLY FOR FLEX, BUT FOR MOST OTHER PROGRAMMING LANGUAGES).

In order to use the MVC concept in Flex projects, authors often refer to 3rd-party frameworks, like PureMVC.

I say, that’s not necessary, because:

  1. If you use frameworks, you rely on 3rd party code, which I really would only recommend if absolutely necessary.
  2. If you set-up your project correctly and think about its infrastructure before coding, you can easily build a Flex application without breaking with the idea of MVC.
  3. No framework in the world can replace good project planning. If you plan well, your application will automatically go MVC.
  4. KISS

So, in order to use MVC in a Flex application, the following will be a short explanation how I set up my projects. This is not a ultimative best practice guide. It’s just something that I want to share from my personal experience.

The concept itself is based on data-binding. DataProviders are being bound to view components. A controller keeps updating these providers. Thus, there should be no application logic required in any of our view-component files (except for a few lines of event-dispatching).

This is how it works:

  1. Create the model, view and controller components
  2. Assign a controller for each view component
  3. Bind the dataproviders to the view components
  4. Add event-listeners to the view components (e.g. to catch user-interactions), which are being handled by the controllers.

Let’s visualize this with a simple example. Image, you’d want to write a program which lists fruits and their prices in a supermarket. With a click on a buy-button the selected fruit is being removed from the list:

In order to create this, you need the following:

  1. The Model (Fruit.as)
    A file, which inherits the two properties name and price.
  2. The View (FruitMarket.mxml)
    A component, which displays the data we want. We’ll use a HBox for this, which wraps a DataGrid and a Button.
  3. The Controller (MarketController.as)
    An ActionScript file in order to provide the application logic, which is associated with the view component (FruitMarket.mxml).

The full project structure should look like this:
Project structure for the MVC-Example FruitMarket in Flex

Here is the UML diagram for our mini project:
UML diagram for FruitMarket example
The basic idea is to assign a controller for each view (1:1 relation) and keep a dataProvider (fruits:ArrayCollection) inside the MarketController and bind it to the FruitMarket component. Since one market can have multiple fruits, the relation between controller and model is 1:n.

You might have noticed some additional files:

  • FruitTable.as – This is a simple DataGrid, which displays the data (as a part of FruitMarket.mxml).
  • FruitBoughtEvent.as – This event will be fired from the FruitMarket.mxml component when the user clicks the buy-button.

This is all that’s needed regarding project setup.
Let’s have a look at the implementation details.

In order to catch user interactions, the FruitMarket.mxml component needs to fire events, which are being caught by the MarketController.as:

FruitMarket.mxml (part)

// Will be called if the user presses the buy-button
private function fruitBoughtEvent(event:MouseEvent):void {
	dispatchEvent(new FruitBoughtEvent(FruitBoughtEvent.FRUIT_BOUGHT_EVENT));
}

The MarketController.as listens to this event:

market.addEventListener(FruitBoughtEvent.FRUIT_BOUGHT_EVENT, buyFruit);
private function buyFruit(event:Event):void {
	var selectedItem:Fruit = market.fruitTable.selectedItem as Fruit;
	
	if (selectedItem != null) {
		var fruitToBuy:Fruit = selectedItem;
		removeFruit(fruitToBuy);
		Alert.show("Thank you for buying the fruit!");
	}
}

Now, in order to get this working, we need to assign the controller to the FruitMarket component (in FruitMarket.mxml). In order to do so, our FruitMarket.mxml calls the function below on creationComplete event (like a constructor):

private var marketController:MarketController;
private function creationComplete():void {
	marketController = new MarketController(this);
}

For this example, it’s not necessary to keep a reference to the controller but you never know. It can become handy in a “real” project.

Last but not least, bind the dataProvider to the view component (shown in the code below). Any changes to the dataProvider (fruits:ArrayCollection) will automatically lead to view-updates. The full controller-source looks like this:

public class MarketController {
	[Bindable] private var fruits:ArrayCollection;
	private var market:FruitMarket;
	
	public function MarketController(market:FruitMarket) {
		this.market = market;
		var tmpArray:Array = [new Fruit("Banana", 0.99), new Fruit("Strawberry", 3.99), new Fruit("Ananas", 3.99),];
		fruits = new ArrayCollection(tmpArray);
		market.fruitTable.dataProvider = fruits;
		market.addEventListener(FruitBoughtEvent.FRUIT_BOUGHT_EVENT, buyFruit);
	}
	
	private function buyFruit(event:Event):void {
		var selectedItem:Fruit = market.fruitTable.selectedItem as Fruit;
		
		if (selectedItem != null) {
			var fruitToBuy:Fruit = selectedItem;
			removeFruit(fruitToBuy);
			Alert.show("Thank you for buying the fruit!");
		}
	}
	
	private function removeFruit(fruit:Fruit):void {
		fruits.removeItemAt(fruits.getItemIndex(fruit));
	}
}

Now, the controller has full access to the view component. It listens to any user interaction events, which are being delegated from the view component, and reacts on these.

This is basically all. Download the full source here.

The full project is now structured into Model (Fruit.as), View (FruitMarket.mxml) and Controller (MarketController.as). The only ActionScript-code inside the FruitMarket.mxml is for assigning the controller to it and dispatch the event if the user clicks on the buy-button. All the rest of the application logic is done by the controller.
If you ever want to replace the view component with a new one, you just have to make sure, that all necessary events are being dispatched and then assign the controller to the component. That’s it.

Of course you could make this perfect by defining an interface for the view component. For the purpose of this demo I was to lazy, but if you deal with real projects, I’d definately recommend doing so.