The Model View Controller Pattern

Description Générale

Bon donc, le but du MVC est de séparer une application en trois couches: le Model, le Controller, et la View. Très bien. La couche Model contient tout la logique d'accès aux données, la couche Controller contient la logique applicative (je crois bien que c'est ce qu'on appelle également la logique métier de l'application), et la couche View contient la logique d'affichage des données.

Dans le cas d'une application web standard, on peut assimiler le serveur web (apache par exemple) à une partie du Controller. En effet, le rôle du Controller consiste à analyser la requête de l'utilisateur et à demander l'execution de la tâche ad hoc. Dans la même veine, le navigateur (oui je veux bien ne pas dire browser, mais non je ne dirais pas butineur) peut être assimilé à une partie de la couche View, puisqu'il est le mécanisme qui assure le rendu de la page web finale.

On va continuer avec une brève description de chaque terme employé dans le schéma.

FrontController

Le FrontController reçoit la requête HTTP (notée HTTPRequest sur le schéma) et la passe au RequestHandler pour analyse.

Selon la réponse du RequestHandler, le FrontController décide de l'action à invoquer, puis execute une série de filtres (InterceptingFilter), avant d'executer l'action précédemment sélectionnée.

RequestHandler

Le RequestHandler reçoit la requête HTTP depuis le FrontController, l'analyse et retourne les informations utiles au FrontController.

Il peut être étendu pour gérer différents types de requêtes (XMLRPCRequestHandler par exemple).

InterceptingFilter

Les filtres d'interception encapsulent tout le traitement pré-métier: gestion de session et d'authentification par exemple.

Chaque filtre prend en argument le filtre suivant, qu'il executera une fois son traitement propre terminé, constituant ainsi une chaine de responsabilités (Chain of Responsability).

Action

L'object action encapsule tout le traitement métier. On en trouvera donc en règle générale une grande quantité de ces objets (UserAddAction, UserDelAction, etc).

L'Action peut faire appel à d'autres objets qui ne sont pas inclus dans le schéma pour des raisons de clarté.

En cas d'erreur dans le traitement, l'Action invoque un ErrorHandler.

Une fois le traitement terminé, l'Action invoque une vue.

ErrorHandler

Le gestionnaire d'erreur traite les erreurs qui lui sont passées par l'Action.

Une fois l'erreur traité, le gestionnaire d'erreur invoque une vue.

LogHandler

Exemple d'objet aidant le gestionnaire d'erreur, le LogHandler permet d'écrire les erreurs dans des fichiers journaux.

View

La vue (View) détermine les données renvoyées au client. Cela peut-être l'affichage d'une interface graphique (HTMLView, XULView, etc), mais aussi des données formatées (CSVView, XMLRPCResponseView).

La vue à utiliser est déterminée par l'Action, et reçoit ses données des objets de transfert (TransfertObject).

TransfertObject

Les objets de transfert sont de simples conteneurs d'informations. Ils reçoivent leurs informations des objets de données (DataObject) et les distribuent aux vues (View).

Leur utilité première est de maintenir la séparation entre la couche modèle (Model Layer) et la couche vue (View Layer): ils font le lien entre les deux sans contenir aucune logique métier.

DataAccessObject

L'objet d'accès aux données encapsule toute la logique d'accès aux données. Il peut être étendu pour utiliser d'autres sources de données que celle initialement prévue (IniDataAccessObject par exemple).

DataObject

Les objets de données encapsulent la logique de manipulations des données. Ils contiennent par exemple les actions CRUD (Create Read Update Delete).

Au final

Au final, ce pattern est plus simple que je ne l'appréhendais, disons que la majeure difficulté de l'apprentissage de la POO est, à mon sens, la terminologie spécifique employée. Une fois qu'on a saisi le sens des termes, c'est tout de suite plus clair.

Bon sinon, j'étais parti pour implémenter ce joli petit schéma, et puis je me suis rendu compte d'une chose à propos des filtres d'interception. A posteriori, je pense qu'ils auraient plus leur place dans la continuité du RequestHandler plutot que dans celle du FrontController. Pourquoi ? Et bien ces filtres sont, à priori, spécifique à un type de requête. Par exemple, on ne va pas gérer l'authentification d'un utilisateur de la même manière selon qu'on reçoit une requête HTTP standard ou (par exemple) une requête XMLRPC. Non ? Vous en pensez quoi vous ?