Revenir
en haut

ZF – Structure du projet

13/10/2010 1

« Zend Framework propose mais n’impose pas »

Zend Framework propose par défaut une organisation qui sépare déjà le moteur de l’application des ressources accessibles par le client HTTP. Et nous trouvons dans la partie « applicative » une autre séparation permettant de répondre au paradigme MVC.

Au fil de nos développements, j’ai fini par adapter cette structure pour la rendre plus modulable. Mais cette personnalisation est aussi sémantique et j’ai profité de la souplesse du framework pour m’approprier une structure sur-mesure.

Certains me diront que c’est une erreur de se détacher de la « normalisation » de Zend, je leur répondrai que oui et non. Effectivement ça peut être un gain de conserver la structure proposée par Zend car il sera plus facile pour un nouveau développeur de prendre en main le projet à l’aide de la documentation du framework. Mais tout ceci est relatif. L’essentiel lorsque nous travaillons en équipe et de conserver une organisation et des conventions, mais rien ne nous empêche de définir les notres, au contraire.

Application modulaire

La structure de dossiers proposée par le ZF inclue la notion de « module ». Cette notion peut paraître vague tant le champ d’application d’un module peut être variable. Pour éclaircir certains esprits, un module (tout comme un plugin) ne peut vivre sans le coeur de l’application. Ils peuvent soit être indissociables de l’application qui a nécessité leur création soit être considérés comme des « extensions » totalement indépendantes et pouvant être réutilisables d’une application à l’autre. Dixit la documentation, une mini-application dans une application.

Pour rendre cela possible, il faut isoler leur structure. C’est ce que propose la documentation du framework. Mais il existe, je pense, un défaut dans cette structure qui inclue dans l’application en elle même le couple vues/contrôleurs par défaut.

Le dossier de l’application est un tronc commun qui doit porter le moteur du système, c’est à dire le coeur du métier. Le modèle, éventuellement une couche de services (API autour du modèle), une couche d’accès aux données (etc.). Les contrôleurs et les vues sont un moyen d’interagir avec le système d’information et ne sont qu’une interface permettant de le piloter. Leur présence n’est pas donc justifiée dans le dossier « application ».

Ou placer ces fichiers alors ? Quels que soient vos choix d’organisation, vous tomberez toujours sur un cas particulier qui remettra tout en question. La bonne solution est celle qui vous convient le mieux (sans perdre de vue une certaine logique).

Pour ma part, j’ai décidé de conserver malgré tout le dossier « modules » dans le dossier « application » mais d’isoler la structure vues/contrôleurs dans un module qui sera considéré comme celui par défaut. L’idée de module « par défaut » n’a de toute façon d’importance que pour la configuration (routes).

Le dossier « application » ne contient donc que des classes motrices indissociables du système que chaque module peut étendre ou compléter, et chaque module reprend la structure de l’application (si besoin) en y ajoutant la couche web (formulaires, vues, contrôleurs, etc.).

Arborescence proposée

Voici l’arborescence que nous utilisons le plus souvent et que j’ai subtilement découpé en deux colonnes :

J’ai renommé pas mal de dossiers. Question d’habitudes, car je préfère les noms courts (plus rapide à atteindre dans les consoles) et je suis habitué au système de fichier linux. Rien ne vous empêche évidemment de conserver les noms des dossiers d’origine (application, configs, etc.).

Les fichiers de configuration

Vous remarquez le dossier etc (configs) dédié au module frontend. Les fichiers de configuration de chaque module doivent donc être chargés au moment de l’initialisation de l’application.

Une bonne solution serait de créer un fichier de config modules.ini (par ex.) qui permet de compléter la configuration en ajoutant les chemins vers les fichiers de config de chaque module. Ces fichiers devraient surcharger la configuration initiale de l’application, c’est ce qui permettra à un module de venir compléter ou d’étendre les fonctionnalités du système.

Une autre solution consiste à respecter des conventions de nommage pour chaque fichier de configuration et de laisser le système (en modifiant Zend_Application en conséquence) charger automatiquement les fichiers. Le défaut de cette approche est que le nom des modules devient important car ils seraient initialisés un après l’autre dans l’ordre alphabétique. L’avantage par contre est de ne pas avoir à modifier quoi que ce soit dans la configuration de l’application, il suffit de plugger le dossier du module dans l’arborescence et il sera automatiquement pris en compte. Autre défaut par contre, l’application doit parcourir les dossiers pour trouver les fichiers de configuration, ce qui peut avoir un impact sur les performances.

C’est à vous de déterminer ce qui vous convient le mieux et de charger intelligemment chaque module, en permettant par ex. d’activer/désactiver une « extension ».

Quelques indications sur la structure

  • bin est utilisé pour stocker les batch et les différentes commandes des CLI.
  • notez bien le sous-dossier Impl dans services qui contient les implémentations des classes de services tandis que les interfaces se trouvent à la racine.
  • var contiendra toutes les données volatiles de l’application.
  • www est le seul dossier rendu accessible de l’extérieur (virtualhost).

Une structure en fonction de l’architecture du projet

La structure peut évidemment évoluer en fonction des besoins. Par exemple, dans un de nos projets, nous devions réunir certaines classes métier dans un même « package ». C’était très facile, notamment grâce à l’autoloader de modules qui permet d’ajouter des types de ressources avec leurs namespaces respectifs, ex. :

$autoloader->addResourceType('model', 'domain/models', 'Model');
$autoloader->addResourceType('repository', 'domain/repositories', 'Repository');

A placer dans le bootstrap lors de l’initialisation de l’autoloader :

protected function _initAutoload()
{      
    $autoloader = new Zend_Application_Module_Autoloader(array(
        'namespace' => '',
        'basePath'  => dirname(__FILE__),
    ));
       
    $autoloader->addResourceType('model', 'domain/models', 'Model');
    $autoloader->addResourceType('repository', 'domain/repositories', 'Repository');
    $autoloader->addResourceType('service', 'core/services', 'Service');
    $autoloader->addResourceType('assembler', 'core/assemblers', 'Assembler');
    $autoloader->addResourceType('dto', 'data/DTOs', 'DTO');          
    $autoloader->addResourceType('dao', 'data/repositories', 'DAO');        
    $autoloader->addResourceType('data', '/../data/tables', 'Data');
   
//  OU
//        $autoloader->addResourceTypes(array(
//            'model' => array(
//                'namespace' => 'Domain_Model',
//                'path'      => 'domain/models'
//            ),
//            'repository' => array(
//                'namespace' => 'Domain_Repository',
//                'path'      => 'domain/repositories'
//            ),
//            // etc...
//        ));

    return $autoloader;
}

Ce qui prouve une fois de plus la souplesse apportée par Zend_Application (depuis la version 1.8), qui reste pour moi une des meilleures initiatives de l’équipe de développement du ZF :)

Ce n’est qu’un exemple des possibilités offertes par le composant et j’écrirai peut-être des billets dédiés à certaines fonctionnalités, comme les plugins de ressources par exemple.

Conclusion

La structure du projet peut changer en fonction de l’architecture ou des besoins de l’application, mais c’est un gain de déterminer une bonne organisation sur laquelle la plupart des applications seront bâties afin de conserver des habitudes de travail.

Certains d’entre vous décideront que ces changements ne sont pas justifiés, d’autres y trouveront leur compte ou tout simplement un point de départ de réflexion. Quoi qu’il en soit, c’est une organisation qui nous convient, à vous de l’adapter selon votre vision.

C’est un sujet largement débattu dans tous les langages qui mélange philosophie et architecture. Il faut trouver le juste milieu car la structure parfaite n’existe pas et il n’existe aucune règle définie pour vous aider dans cette démarche, et je fais un petit clin d’oeil à ceux qui ont déjà changé plusieurs fois leur organisation (ou à ceux qui le feront) et n’arrivent pas à se fixer sur une solution :)

1 commentaire :

  1. nico0702 :

    Merci pour cette explication car j’étais dans le brouillard. Je découvre Zend et surtout je découvre que tout le monde a une méthode perso. Cela me perturbe dans mon apprentissage.

    J’aimerai trouver un bon tuto qui explique tout ça de A à Z avec des exemples concrets. Mais c’est pas évident car les méthodes diverges et souvent l’on a qu’un bout de code présentant une fonction.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

*

Tags HTML autorisés : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Tag code : [cc lang="langage"][/cc] (ex. [cc lang="php"][/cc])