nwebb

Flex, Flash, AIR

Flex Architecture Basics – Models & Data Transfer Objects

How you architect your applications will impact upon how easy or difficult they are to extend/maintain. Even small applications can become problematic without a suitable architecture. I want to demonstrate a relatively easy way of setting up a small Flex application using a model (to hold your data) and a data transfer object (a simple bindable object which can be used to 'transport' data around), to help you avoid some common pitfalls.

A Common Pitfall Example

Have you ever displayed multiple views in a TabNavigator? By deault the TabNavigator component doesn't instantiate a view until it needs to (i.e. until the view is first selected). Therefore if you tried to set the dataProvider for a Combo in Tab 2, from your main .mxml file when your application first loads, you would get a null object error. This is because the Combo will not have been created - it won't be created until the user clicks on the second tab.

One solution to this is to set the creationPolicy for the TabNavigator to 'all', which ensures that all views will be instantiated upfront, but this can impact upon the loading time of your application and may not be desirable. It is also not strictly necessary as we will see.  This is just one problem. There are of course many advantages to having a decent architecture.

The Application

Our application is a deliberately simple one comprising of two views. Each view is in its own tab. In the first tab we have a list of people, and below that a form. You can select a person, then create a child for them (in the literal sense - i.e. a baby) using the form. Once you've finished playing God you can switch to the second view to see a list of all the children (if any) belonging to the selected person.

Before we begin view the example then right-click on the application and choose View Source to view/download the source code:

When you have familiarised yourself with the example, create a new Flex project and import the code (or just view the relevant .mxml or .as file as we go along) and read on.

The Model (Model.as)

The idea of the model is that it is a central place to store the data for your application. An important point to note is that I instantiate the model ONCE in the main mxml file and then pass it in to every view. Some people may suggest that we should use the Singleton pattern here (see this post for a related discussion). Whether you do or not, it is still considered best practise to instantiate the model once in the main mxml file and then pass it in to your views to prevent tight-coupling. This way of setting up a class with the necessary data is known as Dependencey Injection.

Another important thing to note here is that the entire Model class has been made bindable by placing the Bindable tag at the top. This ensures that you can bind to any of the public properties and if/when the properties get updated, the components in your view(s) will have their displays updated accordingly.

Our model currently has one public property, selectedPerson. As the name conveys this will be a reference to the currently selected person. This property holds a reference to an object of type PersonDto. We'll look at PersonDto next.

Date Transfer Object (PersonDto.as)

A Data Transfer Object or DTO (also known as a Value Object or VO) is an object used to transfer data around. It is not a special kind of object, but the idea is that you keep it very simple, declaring properties only (no methods) and you make the entire class bindable. DTOs can be really useful when using Remoting to communicate with a server side technology but that's beyond the scope of this post. Even without Remoting it is useful to have typed objects which can be used to pass data around, store it in arrays etc.

Our application deals with both adults and children. We use PersonDto for both. PersonDto has three public properties: name, gender and children. For children we use an ArrayCollection rather than an array because when an ArrayCollection changes it dispatches an event, meaning that any component binding to children will be notified when any changes occur.

Super View Class (SuperCanvasView.as)

As we want to pass a model [reference] in to each view we create, we'll need a bindable getter/setter in each view (or bindable public property if you prefer). Rather than have a getter/setter inside each view I decided to create a superclass. Each view then extends this super class, therefore inheriting the ability to set and retrieve the reference to the model.

View One (ViewOne.mxml)

Our first view displays a list of people as well as a form to associate a child with the currently selected person.

Things to note:
1) Our view extends SuperCanvasView (as does View Two!)

Actionscript:
  1. <views:SuperCanvasView xmlns:views="uk.nwebb.example.views.*"  xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" label="VIEW TWO">

2) Each person in the List is an instance of PersonDto (our data transfer object)

Actionscript:
  1. <mx:ArrayCollection>
  2.     <mx:Array>
  3.         <dto:PersonDto name="Jennifer" gender="Female"/>
  4.         <dto:PersonDto name="Natalie" gender="Female"/>
  5.         <dto:PersonDto name="Paul" gender="Male"/>
  6.     </mx:Array>
  7. </mx:ArrayCollection>

The idea is that when the user selects a person, we populate our Model's selectedPerson property with a referenece to this object. This is done in the onSelectPerson method:

Actionscript:
  1. private function onSelectPerson(event:MouseEvent):void
  2. {
  3.         model.selectedPerson = event.currentTarget.selectedItem as PersonDto;
  4. }

We could have used a binding here - though it isn't good practise to bind anything directly to a visual component, so it would be better to create a bindable property within the view, then bind the model's selected person to that ... but if you take that approach you may as well set it as I do in the example. While binding is cool, it adds overhead, so only use it in cases where you perceive there to be enough benefit.

In View One we don't want to allow users to create a child until they have selected a person, so here we use a binding expression in order to enable or disable the form, based on whether or not anyone has been selected:

Actionscript:
  1. <mx:Form enabled="{model.selectedPerson != null}">

Finally, when the form's 'Submit' button is pressed we want to associate the child with our selected person:

Actionscript:
  1. //called when the form is submitted...
  2. private function addBaby(event:MouseEvent):void
  3. {
  4.         if(nameInput.text.length != 0)
  5.     {
  6.         var baby:PersonDto = new PersonDto();
  7.         baby.name = nameInput.text;
  8.         baby.gender = genderSelector.selectedLabel;
  9.         model.selectedPerson.children.addItem(baby);
  10.        
  11.         //reset form
  12.         nameInput.text = "";
  13.         genderSelector.selectedIndex = 0;
  14.     }
  15. }

As you can see, we create a new instance of PersonDto, populate the properties using the values from the form, then add the DTO to our selectedPerson's children collection.

We are retrieving the selected person from the model, not the list. The advantage of storing everything in the model is that each view will have a reference to the model, and so View One need not know anything about View Two (such as how to set the DataGrid), and View Two need not know about View One either. Furthermore the model doesn't need to know anything about either view!

Thinking back to the common pitfall example I mentioned earlier, if we were to try and set the dataProvider for View Two's DataGrid now, we would get a null object error because the DataGrid hasn't been created yet ... but here we are not trying to set the dataProvider in View Two so we don't run in to that issue.

View Two (ViewTwo.mxml)

View Two is failry simple. It consists of a label displaying the name and gender of the selected person...

Actionscript:
  1. <mx:Text text="{'Selected Person: ' + model.selectedPerson.name + ' (' + model.selectedPerson.gender + ')'}"/>

... and a DataGrid to display the children (if any) that the user associated with the selected person:

Actionscript:
  1. <mx:DataGrid dataProvider="{model.selectedPerson.children}" width="100%" height="100%">

We get all our values from the model, not from another view.

So let's look at what is happening here. When the view is created we send in a reference to the model. When the DataGrid and Label are ready they'll already have access to the data they need. Furthermore both the model and DTO are bindable, so any future changes to the data will be reflected in the DataGrid and Label without us having to do any additional work.

To summarise, some of the advantages are:

1) The views know about the model but the model doesn't know about any views. This means that you can add additional views without having to modify any code in the model.

2) When a component within in a view is ready it already has access to the data it needs (from the model). Consider the alternative, where your main application tries to populate a component in one of its views. First this would require it to know something about the view (e.g. what property to set) and second it could cause a null object error if it tries to set data on any component which hasn't been fully instantiated at that point in time.

3) When the data in the model is updated, any other views which rely upon that data will automatically be updated too.

4) Your Person data is nicely encapsulated in objects. You can pass these objects around, store them in arrays, check their type if/when needed.

15 comments

15 Comments so far

  1. hsTed August 2nd, 2008 8:39 pm

    I really enjoyed this tutorial and believe it will help me a lot. It’s hard for me to wrap my head around usually, but your examples & explanations are really helpful. Thanks!

  2. Johan August 2nd, 2008 11:30 pm

    Nice clear example!

  3. Devon O. August 3rd, 2008 1:07 am

    Excellent and crystal clear explanation and demonstration of DTO’s/VO’s. Thank you for taking the time.

  4. john chang August 3rd, 2008 6:10 am

    Simple example that explains an important concept that I’ve always been trying to figure out. Thanks for sharing the great work ;-)

  5. Ian August 3rd, 2008 9:30 am

    nice easy to follow example ..

  6. Nick Lansbury August 3rd, 2008 9:52 pm

    What an eloquent example. Clear and to the point. Thanks Neil.

  7. nwebb August 8th, 2008 9:45 am

    Thanks for the kind comments. Much appreciated :]

  8. [...] Flex Architecture Basics – Models & Data Transfer Objects Demonstrates a relatively easy way of setting up a small Flex application using a model and a data transfer object http://nwebb.co.uk/blog/?p=228 [...]

  9. [...] you’ve watched it you may then want to (re)read a couple of my earlier posts here and here. You should now be able to see the advantages and limitations with both of these approaches, and [...]

  10. [...] Flex Architecture Basics – Models & Data Transfer Objects Demonstrates a relatively easy way of setting up a small Flex application using a model and a data transfer object http://nwebb.co.uk/blog/?p=228 [...]

  11. Paul October 13th, 2008 3:44 pm

    Nice example! Any chance this can be extended so that the data (the list of names) is not static, but instead comes from a call to a Java class on Tomcat/LCDS? And maybe the grid is filled the same way potentially after a database update? Well, maybe SOMETHING like that! :-) I’ve been trying to ap my head around how something like that works for a bit now…
    Paul

  12. Flex???????????????? | ??Flex?? July 21st, 2009 1:07 am

    [...] Flex ???? – ??????????????????????????????????Flex??http://nwebb.co.uk/blog/?p=228 [...]

  13. zhou August 25th, 2009 8:55 am

    nice tutoril,but i wonder

    when form added,the model was intilized ,and then the property['selectedPerson'] was null ,and then null['children '] should be error,but it works great,i don’t understand…

  14. nwebb August 25th, 2009 9:43 am

    @paul – sorry, not a Java guy, but on the result call, when data has been returned you would simply update the model and binding would take care of the rest -there is very little difference in what’s going on.

    @zhou – Yes, dataBinding is capable of swallowing up certain errors, which is essential to the process – this allows you to bind to properties of objects which may not exist at the time the binding is declared, yet as soon as the property does have a value, that value will be reflected in the destination.

  15. zhou August 28th, 2009 4:40 pm

    thx,nwebb.yes, i traced the open source of dataBinding,which certified your comment.
    it catch the null error and let the program
    run smoothly,So we can just bind the dataSource
    ,worry no more

Leave a reply

Bad Behavior has blocked 517 access attempts in the last 7 days.