Reflex #1: une micro-architecture pour Flex… simple !

Après un temps de test, de mise en place et re-factorisation en tout genre, j’ai décidé de partager la petite micro-architecture que j’ai mis en place à l’université, et que j’utilise dans mes projets.
Let’s gamble !
Encore un MVC ?
Oui.
Plus sérieusement, l’écriture de cette micro-architecture est partie d’un besoin de simplicité et de robustesse.
Souvent dans le monde de Flex, les équipes ne sont pas forcement homogènes. Certains sont habitués à la rigueur de Java, d’autres sont plus “devsigners freestyle” venant du monde Flash. Les premiers aiment les grosses mécaniques comme PureMVC, ou Prana Framework “Spring like”. Les seconds connaissent au mieux Cairngorm, au pire confondent MVC et VMC - ce qui n’a rien a voir, sauf si vous faites une application riche sur le thème de la ventilation.
Il fallait donc trouver la solution la plus efficace pour avoir un flux de production le plus optimal.
Bon vous me direz “ba Cairngorm c’est pas mal” ! Oui mais voilà. Cairngorm est plus une ligne de conduite qu’une micro-architecture. Il suffit en gros de suivre les interfaces et d’écrire son MVC. C’est simple, mais pourquoi diable utiliser ces #?~! d’événements synchrones pour déclencher des commandes ! Et il devient assez pénible de dupliquer le travail déjà fait côté serveur… sans compter le temps de compilation à rallonge. C’est rigolo pour se faire les dents, mais au bout d’un moment faut être sérieux.
Donc Reflex.
Le MVC en 2 mots
MVC, pour Model View Controller, est le design pattern le plus connu. Le but de ce pattern est de décomposer la représentation graphique (la vue), la couche métier et le modèle de données.
Le modèle MVC de Reflex peut se visualiser comme suit :

- La vue demande au contrôleur la récupération de données.
- Le contrôleur lance la méthode avec les arguments associés, et capte les retours du serveur.
- À la réception des données, le contrôleur met à jour le modèle de données.
- Le modèle notifie à la vue son changement et la met à jour.
Préparation de votre projet d’exemple
Une page de ressources pour Reflex est disponible sur le Blog.
Commencez par récupérer le swc reflex.
Les sources, et la documentation sont aussi disponibles en lignes.
Pour cette exemple, nous allons utiliser un petit service Java. Je vous laisse télécharger le projet d’exemple “reflexTuto1”, et regarder :
- le répertoire “java” : les sources Java ( UserDo : l’object de donnée, UserDs : la couche métier ).
- Les fichiers de configuration dans le répertoire WebContent/WEB-INF/flex/remoting-config.xml : la configuration des services distants.
Pour cette exemple vous aurez besoin d’un environnement Flex avec les WTP installés ainsi qu’un serveur Tomcat (pour lancer les exemples).
Petit rappel :
-
Pour installer les WPT ( qui vous donne accès à la perspective J2EE et à la vue server) :
- Ouvrez le menu Help > Software updates > Find and install…
- Faites “Search new features to install”
- Dans la fenêtre qui apparaît, cochez “Europa Discovery Site”
- Cliquez sur “Finish”
- La recherche de plugins commence. Vous devrez choisir votre miroir. La fenêtre de résultats s’affiche. Déployez “Europa Update Site” et cochez “Web and J2EE Development”
- Cliquez sur le bouton “Select Required”
- Faites “Next”
- Cochez “I accept…”
- Faites “Next” de nouveau
- Faites “Finish”
-
Pour installer un serveur TOMCAT
- Téléchargez un Tomcat 6
- Décompressez l’archive dans le répertoire de votre choix
- Vous devez configurer votre serveur dans Flex à partir de la vue Server : “Windows > Other View…” puis “Servers“
- Un fois configuré (clic droit dans la vue servers et “New…“), n’oubliez pas d’ajouter votre projet sur votre serveur ! (clic droit dans la vue servers et “Add and remove projects…“)
Au départ : un service et un objet de données
Tout d’abord nous devons représenter l’objet de données du serveur dans votre projet.
L’objectif est d’avoir une image identique des données côté serveur et côté Flex, afin que les 2 parties “parlent la même langue” : cette classe contient une référence vers son “image” côté serveur.
package dataObject
{
[Bindable]
[RemoteClass(alias="User.UserDo")]
public class UserDo
{
public function UserDo()
{
}
public var login:String;
public var name:String;
}
}
Nous voulons discuter avec la couche server : nous allons donc utiliser un service de type RemoteObject, faisant référence à notre couche serveur, défini dans le fichier remoting-config.xml.
<mx:RemoteObject id="remoteUser" destination="javaFacadeUser" showBusyCursor="true" />;
Le Modèle
Le modèle est notre réceptacle de données.
Cette classe étend BaseModel, qui permet l’enregistrement automatique du modèle dans le ModelLocator.
C’est lui qui sera mis à jour par le contrôleur, et qui, via le tag [Bindable], mettra a jour là vue. Dans notre exemple nous allons stocker le UserDo, résultant de la requête serveur login().
package model
{
import com.lafabrick.reflex.model.BaseModel;
import dataObject.UserDo;
public class UserModel extends BaseModel
{
[Bindable]
public var userLogged:UserDo;
}
}
Le Contrôleur
C’est l’intelligence du système. C’est lui qui invoque le service, récupère et met à jour les données.
package controller
{
import com.lafabrick.reflex.controller.BaseController;
import com.lafabrick.reflex.model.ModelLocator;
import dataObject.UserDo;
import model.UserModel;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
public class UserController extends BaseController
{
public function login( log:String, pass:String ) : void
{
executeMethod( "login", onLoginResult, onLoginFault, log, pass);
}
public function onLoginResult( event:ResultEvent ) : void
{
(ModelLocator.getModel( UserModel ) as UserModel).userLogged =
event.result as UserDo;
}
public function onLoginFault( event:FaultEvent ) : void
{
trace("Bad result ..."+event.fault);
}
}
}
- Cette classe étend BaseController, qui permet l’enregistrement automatique du contrôleur dans le ControllerLocator.
- La méthode login() contient la mécanique de déclenchement du service (méthode de BaseController). La méthode executeMethod est définie par les propriétés suivantes :
- method:String : le nom de la méthode ( “login” ),
- resultFunction:Function : la fonction déclenchée lorsque le serveur renvoie un résultat (onLoginResult)
- faultFunction:Function = null : la fonction déclenchée lorsque le serveur renvoie une faute (onLoginFault)
- … args : tous les arguments à passer à la méthode distante…
- Le modèle est mis à jour dans le fonction de résultat. Pour récupérer l’instance du modèle, il suffit d’utiliser le ModelLocator :
// Renvoie l'instance UserModel enregistré dans ModelLocator trace( ( ModelLocator.getModel( UserModel ) as UserModel ) );
NOTE : les “locator” ModelLocator et ControllerLocator n’enregistrent qu’une et une seul instance d’un même modèle / contrôleur. Si deux instances d’un même modèle / contrôleur sont déclarées, une erreur ReflexError est levée. Les classes étendant BaseController et BaseModel sont automatiquement référencées dans ModelLocator et ControllerLocator.
La Vue
Construisons maintenant notre vue.
<?xml version="1.0" encoding="utf-8">
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:controller="controller.*"
xmlns:model="model.*">
<mx:RemoteObject
id="remoteUser";
destination="javaFacadeUser";
showBusyCursor="true" />
<controller:UserController id="userControl" service="{remoteUser}" />
<model:UserModel id="userModel" />
<mx:VBox>
<mx:TextInput id="login" />
<mx:TextInput id="password" displayAsPassword="true" />
<mx:Button label="connect"
click="{userControl.login( login.text, password.text )}" />
<mx:Label x="208" y="44"
text="User Name : {userModel.userLogged.name}"/>;
</mx:VBox>
</mx:Application>
- Nous créons une instance RemoteObject permettant de pointer sur le service côté serveur. La propriété destination est l’identifiant du service dans le fichier remoting-config.xml.
- Nous créons une instance de UserController, avec comme propriété service l’identifiant du RemoteObject “remoteUser”.
- De la même manière, nous créons l’instance du modèle UserModel.
- Au final, la vue appel la méthode login() du contrôleur, qui se charge d’envoyer et de réceptionner les données au serveur. Au retour, le contrôleur met à jour le modèle, en “poussant” le UserDo provenant du serveur. La vue est automatiquement mise à jour via le principe de Binding des données.
Conclusion
Reflex n’a pas la prétention de concurrencer des architectures reconnues comme PureMVC ou MATE. L’objectif, vous l’aurez compris, est d’aller à l’essentiel, sans se perdre dans la forêt des Design Pattern, pour une productivité la plus efficace possible. A vous d’implémenter cette architecture selon vos besoins.
Le prochain tutoriel ira un peu plus loin dans cette micro-architecture : comment mieux découper son projet, et comment associer un fichier de configuration XML externe (package IOC).
J’attends vos retours !
Tags: architecture, Flex, MVC, Tutoriels

--> 13 janvier 2009 ( 21:01 )
Miam, j’avoue que Cairngorm, sous son aspect très structuré me paraissait un peu lourd à l’usage.
Donc, c’est partial, mais j’assume : Refex rocks !
--> 14 janvier 2009 ( 10:43 )
J’avoue, que c’est simple, efficace. Cairngorm ou PureMVC me parraissaient aussi bien lourd et pas toujours utils pour certains projets.
Je l’essayerai dans mes prochains projets.
Merci, beau boulot.
--> 14 janvier 2009 ( 13:00 )
…et en plus ça marche !
Il y a juste 2 paths à modifier et l’exemple tourne tout seul (fichier .actionScriptProperties ligne 3 et .flexProperties ligne 2, le path serverRoot)
Merci pour partager Reflex et les explications détaillées avec nous tous, chapeau !
A bientôt !
--> 16 janvier 2009 ( 9:14 )
Enfin une archi qui se demande pourquoi chercher midi à 14h.
--> 10 février 2009 ( 22:04 )
Petite mise à jour pour corriger une “coquille” sans effet direct… Merci à Palleas pour le retour.
--> 11 février 2009 ( 12:44 )
Wouaa cool j’ai même un backlink, merci !
--> 21 janvier 2010 ( 18:02 )
Très bonne idée de framework, ça permet d’architecturer une appli proprement sans passer par des usines à gaz. Le fait que la vue appelle des méthodes du contrôleur me semble préférable à l’envoi d’évènements de la vue vers le contrôleur (contrairement à beaucoup de frameworks).
donc le contrôleur ne contrôle jamais la vue? (qui contrôle les vues?)
est-ce que 1 contrôleur ne peut utiliser qu’un seul service?