Passer au contenu principal

Note : cet article ne couvre que les applications web Java/JEE, autrement dit celles qu’on pourrait retrouver dans des Tomcat ou des Jetty, mais pas les EJB ou les nouveaux serveurs web tels que vert.x même si certaines des idées évoquées ici s’y appliquent bien. Par ailleurs les idées présentées ici s’appliquent à des applis ayant déjà un certain niveau de complexité.

Ah et dernier point ici : on ne couvre pas l’affichage ni le transfert de données entre le client et le serveurs, on s’arrête juste avant. On ne couvre pas non plus ici les problématiques d’exploitation de l’appli, du genre mettre une partie des pages en cache dans un serveur de type Varnish.

Dans de nombreux projets web en Java/JEE les applications deviennent de plus en plus lourdes à maintenir au fil du temps. En effet les architectures ne sont pas toujours bien définies à la base, et dans ce cas les implémentations ne suivent pas. En effet une mauvaise architecture résultera toujours en un mauvais code, par contre une bonne architecture n’implique pas nécessairement qu’elle sera codée proprement ensuite. Et une mauvaise architecture peut avoir des impacts également au niveau des performances, mais aussi de la sécurité. Pourtant, il existe des principes qui permettent de bien découper le problème et d’avoir une application relativement simple à gérer, c’est l’objet de cet article.

Architecture d’une application web Java/JEE

Au sein des applications web on retrouve généralement trois couches :

  1. la première contient les servlets ou les contrôleurs si vous utilisez un framework MVC, chargés d’assurer le dialogue entre l’application et les services extérieurs.
  2. la deuxième contient les services, bref le code métier
  3. la dernière contient les accès aux données, que ce soit en base avec des DAO, à des fichiers ou encore à des services externes.

Cela dit, il est possible d’affiner davantage ce modèle comme ci-dessous :

  1. la première couche contient toujours les servlets ou les contrôleurs, comme ci-dessus
  2. Juste en dessous, on met une couche de sécurité. Celle-ci est en charge de vérifier avant ou après l’invocation des services que l’utilisateur a bien le droit d’effectuer l’opération qu’il est en train de faire.
  3. La troisième couche contient des services de haut niveau, bref le code applicatif.
  4. La quatrième couche contient des services de bas niveau. Ceux-ci servent en particulier à valider les paramètres fournis à la cinquième couche, mais également à interagir avec les éventuels caches de cette dernière couche.
  5. La dernière couche contient les accès aux données.

Dans la suite nous détaillons le contenu de chacune des couches.

La couche des servlets ou contrôleurs

La couche des servlets ou contrôleurs n’a que deux rôles : le premier est de décoder les paramètres envoyés par le client pour les rediriger vers les bons services, et le deuxième est d’encoder les réponses des services pour qu’elles puissent être lues ou affichées par le client.

Très important : cette couche doit être stupide, et surtout ne contenir aucun code métier. En effet, rien n’indique que plus tard le code métier de votre appli ne devra pas être aussi embarqué dans un client lourd, et dans ce cas ce serait dommage de devoir tout recoder. Et dernier point, cette couche est la seule qui doit contenir des références aux HttpRequest, HttpSession et aux objets Java/JEE propres aux webapps, toujours pour la même raison.

La couche de sécurité

La couche de sécurité sert à garantir le fait qu’un utilisateur qui effectue une action a bien les droits suffisants pour le faire. Nous avons détaillé plus largement les principes de cette couche ici, aussi dans cet article nous ne fournissons que quelques astuces en terme d’implémentation.

Tout d’abord, la couche des servlets ne doit jamais être en contact direct avec les services. Toute opération doit nécessairement passer par la couche de sécurité pour vérification, sinon on se retrouve dans le cas parfaitement illustré dans cet article (qui n’existe plus, est-ce étonnant ?).

Ensuite, en terme d’implémentation, pour les rôles le plus simple est de procéder par AOP. Des frameworks comme Spring permettent de le faire très simplement.

Pour les ACLs, il y a deux possibilités : soit on implémente une façade de la couche service, soit on procède par l’intermédiaire de proxys ou d’AOP. C’est à voir suivant les besoins. Quoiqu’il en soit dans les deux cas les ACLs devront être gérées par un service dédié, et la façade ou les proxys se contenteront d’appeler ce service pour valider les droits.

Dernier point : cette couche sera développée en TDD ou aura une couverture de tests unitaires de 80% minimum. En effet pour garantir la sécurité d’une application il faut être en mesure de pouvoir mettre rapidement à jour les middlewares qu’elle utilise si souci à ce niveau, et les tests automatisés permettent de vérifier très rapidement que l’application continue à bien fonctionner après mise à jour de ceux-ci.

La couche de services de haut niveau

La couche de services de haut niveau définit les opérations métier qui peuvent être effectuées dans l’application. C’est aussi au niveau de cette couche qu’on va définir quelles opérations doivent être transactionnelles et lesquelles ne le sont pas. De manière générale les opérations en lecture doivent supporter les transactions sans pour autant les requérir, cf. la javadoc, tandis que les opérations en écriture doivent requérir ces transactions.

Le support des transactions se définit typiquement par AOP avec des frameworks comme Spring, cf. cette page.

Cette couche doit nécessairement valider tous les paramètres qui lui sont fournis en entrée, cf. l’article de Xavier comment faire une API. Une telle validation peut se faire soit en « boîte blanche », à savoir que chaque méthode lève les exceptions si elle détecte que des paramètres sont incorrects. J’avoue préférer cette dernière approche, mais il est vrai qu’elle nécessite d’écrire un peu plus de code.

Des services de haut niveau peuvent s’appeler entre eux, mais également appeler les services de bas niveau lorsqu’il s’agit d’accéder aux données.

De même que pour la couche de sécurité, cette couche de services devra être développée en TDD ou avoir une couverture de tests d’au moins 80%.

La couche de services de bas niveau

La couche de services de bas niveau ne sert qu’à deux choses : valider les paramètres qui sont transmis à la couche d’accès aux données, et éventuellement faire appel à un service de cache comme EhCache.

Contrairement aux services applicatifs, les services de bas niveau ne doivent pas s’appeler entre eux, ils servent juste de surcouche « transparente » pour la couche d’accès aux données. En fait chacun des services de cette couche ne doit pouvoir appeler que le service de cache et un service d’accès aux données.

De même qu’au dessus, la validation des paramètres peut s’effectuer en boîte noire ou en boîte blanche. Et cette couche devra également être couverte par des tests unitaires, comme précédemment.

Dernier point : les caches ne doivent idéalement être utilisés que pour des données accessibles uniquement en lecture. Par ailleurs ils requièrent l’implémentation de MBeans permettant de les purger si besoin. En effet, pour les données en écriture, mieux vaut tuner les accès à la source de données (requêtes SQL, appels externes, etc) plutôt que de tenter de gérer des caches. Ces derniers pourront poser de gros problèmes dans le cas où vous avez plusieurs serveurs derrière un load balancer.

La couche d’accès aux données

La couche d’accès aux données sert, comme son nom l’indique, à permettre à l’application d’interagir avec ses sources de données. Parmi celles-ci on trouvera généralement une base de données, mais également des accès aux fichiers ou des interrogations de services externes.

Cette couche doit être aussi stupide que possible car il s’agit d’une couche « jetable ». En effet plus tard il se peut que l’application soit par exemple amenée à passer d’une base de données à une autre, ou d’autres cas envisageables.

Par contre, pour chacun des accès, il est important que cette couche soit la plus rapide possible. En effet en terme de performance les accès à des sources de données entraînent des accès disque ou réseau, qui sont extrêmement coûteux.

Cette couche étant jetable, elle n’a pas besoin d’être testée unitairement mais ne doit contenir aucun code métier.

Quelques frameworks pour vous simplifier la vie

Le framework qui devrait revenir pour vous simplifier la vie est Spring. Celui-ci est vraiment très complet, et permet de faciliter une implémentation de bout en bout de votre application. La couche haute utilisera Spring MVC. La couche sécurité utilisera Spring Security pour les rôles. Les autres couches utiliseront essentiellement l’AOP, et la couche d’accès aux données utilisera par exemple Spring Data.

Si vous êtes réfractaire à Spring, vous pouvez utilisez d’autres conteneurs plus légers tels que Pico Container en combinaison avec du Struts 2 et de l’Hibernate ou du MyBatis. Attention néanmoins à la gestion des transactions car l’API JTA n’est pas la plus simple à maîtriser.

Besoin de tester ton niveau en développement informatique ?

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

Rejoignez la discussion 2 Commentaires

  • Alain dit :

    Pffff, spring !
    Comment partir avec des années de retard …

  • gojul dit :

    Ca reste tout de même un des seuls frameworks qui permettent vraiment de gérer de bout en bout la partie serveur d’une application. Bon après il y a aussi les EJB notamment, qui même s’ils ont fait de gros progrès avec les EJB3, restent très lourds à mettre en oeuvre en terme de développement.

    Et sinon il y a des serveurs web nouvelle génération comme vert.x qui fonctionnent par bus d’événements plutôt que comme un Tomcat. J’avoue ne pas l’avoir encore trop regardé mais il représente une bonne solution d’avenir, même si les principes de DAO et services subsistent encore.

Laisser un commentaire