1. Bases▲
Dans cet article, je vais commencer par évoquer les bases de conception d'une application modulaire, c'est-à -dire, définir ce qu'est une application modulaire, ce qu'est un module et bien entendu ce que ça apporte. Dans les prochains chapitres, je vais essayer de décrire les différentes problématiques inhérentes au sujet et leurs résolutions. Les exemples de code seront en Java, mais les concepts décrits dans cet article peuvent s'appliquer à tous les langages.
Bien entendu, je ne me considère pas comme un spécialiste dans les applications modulaires et les différentes informations que je vais donner dans cet article ne découlent que de ma façon de voir, il y en a certainement d'autres et des meilleures, donc si vous n'êtes pas d'accord avec moi ou si vous pensez qu'on peut faire mieux, n'hésitez pas à me contacter pour me donner votre avis. Toute l'expérience que j'ai réside dans le développement d'une application modulaire comme projet perso.
Pour commencer, qu'est-ce qu'une application modulaire ? Une application modulaire est constituée de 2 parties bien distinctes :
- Le coeur : Comme son nom l'indique, il s'agit de la partie centrale de l'application. Cette partie doit être complètement indépendante des modules.
- Les modules : Il s'agit cette fois des parties qu'on va ajouter dynamiquement au cœur de l'application pour lui rajouter des fonctionnalités. On leur donne d'autres noms : extensions, briques, plugins, addons, ..., mais le concept est toujours le même.
Il y a à mon avis 2 sortes d'applications modulaires.
- Le premier type est une application somme toute normale qui propose une série de fonctionnalités et qui propose en plus d'étendre ces fonctionnalités par le biais de modules.
- Le second type est une application qui propose un coeur complètement dénué de fonctionnalités. Toutes les fonctionnalités sont proposées par les modules, c'est donc une application dans laquelle "tout est module".
La différence principale est que la première peut être utilisée tout à fait normalement sans aucun module, alors que la deuxième ne propose pas (ou presque pas) de fonctionnalités sans module.
Au contraire d'une application normale, il faut donc prévoir des points d'extensions que les modules pourront utiliser pour ajouter des fonctionnalités à l'application ou pour modifier l'existant.
Je me suis basé sur le deuxième type réaliser mon logiciel. Cela permet d'avoir un socle solide qu'on peut éventuellement réutiliser pour d'autres applications sous réserve de quelques modifications (le mieux serait bien entendu de penser ce core de manière tout à fait générique dès le début).
Comme application modulaire connue, on peut citer Eclipse qui est entièrement modulaire et qui est du deuxième type.
Maintenant, la grande question est : C'est bien beau tout ça, mais ça sert à quoi ? Tout d'abord, du point de vue du développeur, cela permet de séparer clairement les différentes parties de l'application. On a donc des modules distincts plus faciles à maintenir qu'une grosse application. Cela permet également d'être très souple pour rajouter des fonctionnalités à l'application, en effet, pour cela, il suffit de créer un nouveau module, d'y coder la fonctionnalité et de rajouter ce module dans l'application. Du point de vue de l'utilisateur, cela ajoute de la souplesse au programme. En effet, l'utilisateur va pouvoir choisir quels modules il veut lancer, rajouter de nouveaux modules, voire même en créer s'il en est capable. Il pourra donc personnaliser son application. Néanmoins, cela va peut-être compliqué la tâche de l'utilisateur. Par contre, cela va beaucoup simplifier le travail en équipe sur une application. En effet, si un module ne nécessite pas plus d'une personne, il sera très aisé de travailler chacun sur un module et de ne faire les tests qu'avec son module et ensuite de tester l'application avec les modules des autres, une fois l'application terminée.
Bien entendu, cela ne se fait pas tout seul et ce n'est pas des plus simples à concevoir et à implémenter. En effet, cela pose beaucoup de problématiques qu'il faut régler et cela implique beaucoup de questions qu'il faut se poser.
C'est ce que nous allons voir maintenant dans les prochains chapitres.
2. Modules▲
Après avoir décrit dans le premier chapitre en quoi consistait une application modulaire et ce qu'était un module, nous allons maintenant nous pencher plus spécialement sur les modules.
Nous avons dit qu'un module permettait de rajouter des fonctionnalités à l'application principale. Mais avant de se lancer dans le moindre code, il va falloir définir exactement ce que seront nos modules, ce qu'ils pourront faire, s'il y aura plusieurs types de modules, ...
2.1. Extension par les modules▲
La première question à se poser est : "Que vont pouvoir faire mes modules ?". C'est-à -dire, qu'est-ce qu'un module pourra ajouter à l'application principale.
Nous avons dit qu'un module rajoutait des fonctionnalités à l'application principale, mais il faut maintenant spécifier ce que les modules pourront ajouter et la manière dont ils vont pouvoir les ajouter. En plus d'ajouter, un module pourra éventuellement modifier l'application principale.
Ce que les modules pourront faire va dépendre également du type d'application :
- Dans le cas du premier type (application normale avec possibilité d'extension par module), le module va rajouter des fonctionnalités de types spécifiques, c'est-à -dire en accord avec l'application. Prenons le cas d'une application qui permet de regarder la télévision via internet. Dans ce cas-là , les modules pourront par exemple rajouter une chaîne à l'application ou rajouter un format de conversion vidéo pour l'enregistrement. Il peut aussi rajouter une fonctionnalité complète comme des statistiques qui indiqueraient le nombre d'heures pendant lesquelles on a regardé la télé sur ce programme et la chaîne la plus regardée.
- Dans le cas du second type (application vide de base et modules qui rendent l'application utilisable), les modules ne vont pas rajouter des fonctionnalités à l'application principale étant donné qu'elle ne permet rien, mais vont définir ce que va faire l'application. Cette fois, les modules, c'est l'application. Prenons mon application (JTheque) qui est destiné à gérer des collections diverses. J'ai pour le moment un module Films et un module Livres. J'ai ensuite des plus petits modules comme le modules Stats ou un module qui permet de gérer une liste de films à acheter. On voit qu'on a déja des modules beaucoup plus large. En plus, avec quelque chose de ce type, on pourrait imaginer un module qui ferait quelque chose de complètement différent de l'application de base, comme un module Calculette qui ajouterait une calculatrice à l'application, ce qui n'est pas du tout le but de l'application de base mais qui est rendu possible par les possibilités d'extension du coeur de l'application.
Dans ces deux cas, il faut donc définir certains points d'extension. Dans le cas de JTheque, voici les points d'extension que j'ai créé pour mes modules :
- Rajouter des onglets dans la vue principale
- Rajouter des composants dans la barre d'état
- Rajouter des éléments dans le menu de la vue principale
- Rajouter des onglets dans la vue de configuration
A partir de ces points d'extension, les modules peuvent faire beaucoup de choses. Par exemple, si on veut faire une calculette, on peut imaginer rajouter un onglet principal avec une calculette, ou alors faire une fenêtre dediée et rajouter un élément dans le menu qui ouvrirait cette fenêtre.
Ceci est donc laissé au libre choix du développeur, mais il faut y penser avant et bien réfléchir à ce que ça implique.
2.2. Services fournis aux modules▲
En plus de points d'extension, le coeur de l'application fournit aussi une série de services aux modules. Ces services peuvent être de simples classes utilitaires ou alors un gestionnaire de persistence d'entités, un gestionnaire de log ou encore un gestionnaire permettant aux modules d'enregistrer des informations (exceptions par exemple) pour les retrouver après l'arrêt de l'application.
Là encore ces points sont laissés à la discrétion du développeur. Ces services ne sont pas obligatoires, mais cela permet d'éviter que chaque module doivent tout refaire. En plus, cela permet également une standardisation. En effet, les modules vont (normalement) utiliser le même type de persistence, le même type de log, ... Ce qui est plus facile à gérer ensuite. Bien entendu, rien n'empêche un module d'utiliser son propre système de log si celui du coeur ne lui convient pas.
Pour la petite histoire, voilà quelques services que j'offre à mes modules dans JTheque:
- Gestionnaire de logs : Permet aux modules d'obtenir des loggers (Log4J) pour logger différentes actions.
- Gestionnaire d'erreurs : Permet aux modules d'afficher des erreurs facilement
- Gestionnaire de vue : Permet aux modules d'afficher des messages, de demander quelque chose à l'utilisateur, ...
- Gestionnaire de beans : Permet aux modules d'utiliser de l'IOC (Spring).
- Gestionnaire de ressources : Permet aux modules de bénéficier de l'internationalisation (Spring) ainsi que d'un cache d'image
- Gestionnaire de persistence : Permet aux modules de gérer des entités persistentes (JPA/Hibernate).
- Gestionnaire d'états : Permet aux modules de stocker des états.En général, il s'agit de la configuration du module.
Vous pouvez bien sûr ajouter autant de services que vous le désirez. Mais pensez néanmoins à ne par surcharger le core. Quitte à le rendre modulaire au niveau des services. En effet, certaines applications n'auront peut-être pas besoin de certains services, alors pourquoi les charger ?
2.3. Types de module▲
On peut aussi réfléchir si l'on veut un seul ou plusieurs type de module ou s'il y en aura plusieurs. Dans mon cas, j'ai fait une petite distinction entre des modules dits principaux et des modules dits secondaires. Les fonctionnalités sont exactement les mêmes sauf qu'un seul module principal peut être lancé au même moment. Par exemple, le module films est un module principal et le module ajoutant une liste de films à acheter est un module secondaire.
Vous pouvez bien sûr imaginez encore d'autres types de module, mais pensez à définir clairement chacun des types.
2.4. Dépendance entre modules▲
Le dernier point que je vois à traiter au niveau des modules est la dépendance entre modules. On peut permettre ou non qu'un module dépende d'un autre module. Cela ajoute un niveau de difficulté supplémentaire au niveau de l'implémentation étant donné qu'il faudra vérifier que la dépendance est remplie avant de pouvoir lancer le module. Cela implique également que le module principal doit être lancé avant le module qui en dépent. Et que faire des dépendances circulaires ? Bref, cela est également laissé au choix du développeur qui peut permettre les dépendances intra-modules ou non. Dans mon cas, je l'ai permis, j'ai par exemple, le module Stats qui dépend du module Films étant donné qu'il a besoin des informations sur les films, acteurs et réalisateurs pour générer ses statistiques.
En plus de permettre ou non les dépendances entre modules, il faut voir si l'on ajoute des fonctionnalités permettant de communiquer entre les modules. Pour cela, on pourrait demander au gestionnaire de modules l'instance d'un module (en fonction de son nom par exemple ou d'une interface qu'il implémente) et ensuite utiliser cette instance pour effectuer des actions sur le module. Ce peut être simplement de récupérer un dao pour enregistrer un listener, mais ce peut être des modifications, dans ce cas, il faut prévoir que le module pourra être accédé de l'extérieur et pas et ce qui sera faisable sur un module depuis un autre module. Bref, cela n'entre pas dans le cadre de cet article, mais dans le cas d'une application complexe, il est possible que vous ayiez à considérer ce point.