Je travaille actuellement sur un framework d'extensibilité à utiliser avec ASP.NET MVC. Mon framework d'extensibilité est basé sur le célèbre conteneur Ioc : Structuremap.
Le cas d'utilisation que j'essaie de remplir est simple : créer une application qui devrait avoir des fonctionnalités de base pouvant être étendues pour chaque client (= multi-locataire). Il ne devrait y avoir qu'une seule instance de l'application hébergée, mais cette instance peut être adaptée pour chaque client sans apporter de modifications au site principal.
J'ai été inspiré par l'article sur le multi-locataire écrit par Ayende Rahien : http://ayende.com/Blog/archive/2008/08/16/Multi-Tenancy--Approaches-and-Applicability.aspx. Une autre source d'inspiration a été le livre d'Eric Evans sur la conception pilotée par le domaine. Mon framework d'extensibilité est basé sur le modèle de répertoire et le concept d'agrégats racines. Pour pouvoir utiliser le framework, l'application hébergée doit être construite autour de répertoires et d'objets de domaine. Les contrôleurs, les répertoires ou les objets de domaine sont liés à l'exécution par l'ExtensionFactory.
Un plug-in est simplement une assemblée qui contient des contrôleurs, des répertoires ou des objets de domaine respectant une convention de nommage spécifique. La convention de nommage est simple, chaque classe doit être préfixée par l'ID client, par exemple : AdventureworksHomeController.
Pour étendre une application, vous copiez une assemblée de plug-in dans le dossier d'extension de l'application. Lorsqu'un utilisateur demande une page sous le dossier racine du client par exemple : http://multitenant-site.com/[customerID]/[controller]/[action], le framework vérifie s'il existe un plug-in pour ce client particulier et instancie les classes personnalisées du plug-in sinon il charge celles par défaut. Les classes personnalisées peuvent être des contrôleurs, des répertoires ou des objets de domaine. Cette approche permet d'étendre une application à tous les niveaux, de la base de données à l'interface utilisateur, en passant par le modèle de domaine, les répertoires.
Lorsque vous souhaitez étendre certaines fonctionnalités existantes, créez un plug-in, une assemblée qui contient des sous-classes de l'application principale. Lorsque vous devez créer de toutes nouvelles fonctionnalités, ajoutez de nouveaux contrôleurs dans le plug-in. Ces contrôleurs seront chargés par le framework MVC lorsque l'URL correspondante est demandée. Si vous souhaitez étendre l'interface utilisateur, créez une nouvelle vue dans le dossier d'extension et référencez la vue par un contrôleur nouvel ou hérité. Pour modifier un comportement existant, créez de nouveaux répertoires ou objets de domaine ou sous-classez ceux existants. La responsabilité du framework est de déterminer quel contrôleur/répertoire/objet de domaine doit être chargé pour un client spécifique.
Je conseille de jeter un œil à structuremap (http://structuremap.sourceforge.net/Default.htm) et en particulier aux fonctionnalités du DSL Registry http://structuremap.sourceforge.net/RegistryDSL.htm.
Voici le code que j'utilise au démarrage de l'application pour enregistrer tous les contrôleurs/répertoires ou objets de domaine des plug-ins :
protected void ScanControllersAndRepositoriesFromPath(string path)
{
this.Scan(o =>
{
o.AssembliesFromPath(path);
o.AddAllTypesOf().NameBy(type => type.Name.Replace("Controller", ""));
o.AddAllTypesOf().NameBy(type => type.Name.Replace("Repository", ""));
o.AddAllTypesOf().NameBy(type => type.Name.Replace("DomainFactory", ""));
});
}
J'utilise également une ExtensionFactory héritant de System.Web.MVC.DefaultControllerFactory. Cette factory est responsable de charger les objets d'extension (contrôleurs/répertoires ou objets de domaine). Vous pouvez brancher vos propres factories en les enregistrant au démarrage dans le fichier Global.asax :
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(
new ExtensionControllerFactory()
);
}
Ce framework, avec un site d'exemple entièrement opérationnel, peut être trouvé sur : http://code.google.com/p/multimvc/