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.

9 thoughts on “My best practice for MVC with Adobe Flex3

  1. fer says:

    Thank you! I’ve looking for something like this everywhere! and I finally find a simple application.
    I don’t know why, but everyone else is trying to explain things with some very complex applications…

    Anyway.. you’ve helped me a lot! thanks!..

  2. You are welcome! I am glad that I was able to explain something easy in a simple way. Don’t know why some people like to make things more complicated than they are.

  3. Alex says:

    There’s only one problem ….
    you are referencing the controller in the view (where it’s instantiated) and the view in the controller (in your constructor).
    This causes the garbage collector to never get rid of one or the other.
    I’m building a complex application using modules, and I’m facing this very same problem. Any suggestion?

  4. Yes and it’s actually pretty simple.

    You just have to set one of the references to null, for example controller=null (in the view). Then, the garbage collector will remove the controller from memory unless there is another reference to it somewhere in your program (hello, memory leaks! :-)).
    Same goes for view=null (in the controller).

    If you don’t like this concept of two-way referencing you could also remove one of them:

    The reference of controller->view is unavoidable. Every view component should have a controller.

    The other way round is redundant in most cases and can thus be omitted.
    It is “best practice” anyway.

    The only reason why a view component would need a reference to its controller is that it needs to call certain methods of it.
    Without a reference to the controller, you could fire custom events which are then caught by the controller.
    This concept of loose coupling is a lot better than the hard-wired way of using 2-way references.

    In a nutshell: (Best practice)
    – Give every view a controller, so the controller can access the properties and methods of your view.
    – Don’t create a reference back!
    – Instead, define events which are being fired by the view component and then caught by the controller.

    I must admit that I didn’t think of that when I wrote this article :-)

  5. Ned says:

    First off, I would like to thank you for this simple but very effective design approach to flex. I have tried honestly about a hand full of flex MVC framework from mate to robot legs. but I like the idea of not depending on any third part library code. coming from a Java development background I can say that this approach fits my thinking pattern more closely, well done pal. By the way I am very new to Flex and have been given a huge project to take on. You have set the path for me that I wish to take.

    keep up the good work!

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.