9 Building MVC Architectures

Parsley is different from several other Flex or Flash MVC Frameworks in that it does not provide very much explicit support for the various pieces of an MVC architecture. This was a side effect of our primary goal being to allow to design a fully decoupled architecture. Decoupled in a sense that not only the parts of your application are decoupled from each other, but also decoupled from the framework itself. For example providing base classes for controllers or mediators in the framework that application classes have to extend is a bad idea if you want to support such a decoupled architecture.

For that reason the Messaging Framework in Parsley which we already described in 5 Messaging is pretty generic. It does not assume a particular programming style or architectural pattern. Nevertheless Parsley still falls into the same category as many other MVC Frameworks since the Messaging Framework can easily be used in a way that helps building an application based on the MVC architectural pattern. In this chapter we will provide some examples on how to use the Messaging Framework in such a context.

9.1 MVC Event Flow Example

The following diagram shows an example flow for a particular use case (user adds item to shopping cart) in the context of a Parsley MVC application:

Explanations for the numbered steps from the diagram:

From the example above many core elements will have been setup and wired in a Parsley Context including the view as described in 7 Dynamic View Wiring. This usually includes all controller actions, their dependencies like remote services, and the registration for particular messages in the MessageRouter.

If you think this diagram smells like over-engineering, it merely serves as an example for a full blown MVC architecture of a large and complex application. In smaller and simpler applications you can of course simplify the design presented above. For example the Mediator may be omitted in simpler use cases where the view itself dispatches and receives application messages.

9.2 Example Command Implementation

Finally we'll pick one of the pieces presented in the diagram above and show how the implementation and configuration of such an application part may look like. We'll chose the AddToCartCommand that participates in the application flow shown in the diagram.

package com.bookstore.actions {

import mx.rpc.AsyncToken;
import mx.rpc.Fault;
import mx.rpc.remoting.RemoteObject;

import com.bookstore.events.CartEvent;
import com.bookstore.services.CartService;
import com.bookstore.model.LineItem;

public class AddToCartCommand {


    [Inject(id="cartService")]
    public var cartService:RemoteObject;

    
    public function execute (event:CartEvent) : AsyncToken {
        return cartService.addItem(event.item);
    }
    
    public function result (item:LineItem) : void {
        /* modify or cache the result */
    }
    
    public function error (fault:Fault) : void {
        /* handle fault */
    }
    
}

} 

And this is how you can configure such a command in a Parsley configuration class (in this case MXML):

<DynamicCommand 
    type="{AddToCartCommand}" 
    selector="addItem"
/>

We'll now examine the various parts of this class in detail. First there is the method that executes the command:

public function execute (event:CartEvent) : AsyncToken {

In this example we are using regular Flash Events as application messages, but you are not forced to do so as described in 5.1 Dispatching Messages.

The method parameter tells the framework that we are interested in CartEvents. MessageHandler selection happens based on message type, so in a large and complex application you won't run into problems having to maintain event type constants which are unique across the whole system.

But since we are not interested in all CartEvents that get dispatched we specify a selector attribute in the MXML configuration which in case of Events points to the type property of the Event. In this case we are intereseted in CartEvents with the type addItem which was dispatched by the View or the presentation model as a result of an UI event.

In the body of that method we are using the injected CartService:

[Inject(id="cartService")]
public var cartService:RemoteObject;

In this example we are using a RemoteObject. We usually prefer to use injection by type but this is not applicable for RemoteObjects which can only be distinguished by id. For details see 12 Remoting.

Finally there are the result and error handlers:

public function result (item:LineItem) : void {
    
public function error (fault:Fault) : void {

We are following a naming convention here so we can avoid any metadata configuration altogether. Based on the method names the framework knows which method executes the command and which handles results or faults. The result and error handler are both optional.