Plugin Architecture in Web Apps (Examples or Code Snippets?)

I am trying to learn to develop a web application (preferably NodeJS/MongoDB, although I used PHP and Python before) that is highly extensible and customizable via plugins to enable disabled functionality.

One possible option is to use Wordpress with hooks for plugins and widgets to hook into, its however lacking proper separation of view and logic code. That remains to be one option to learn from. Are there any other options?

Do you have any code snippets or example application I can learn from? Language or framework is not so important, I could probably roughly make out the concept behind

Answers


A good plugin architecture is difficult to achieve from scratch, but offers its own rewards. It makes the software flexible and simple to maintain by localising complexity. The foremost skill it requires is the ability to write loosely coupled code. This demands a very firm grasp of polymorphism, Demeter's Law and the related Hollywood principle.

I recommend that you initially gain a good familiarity with those, then the following design patterns, which will reduce the difficult significantly :

  • Command Pattern : Gives Plugin Modules a consistent entry point, allowing them to be readily swapped in and out, a Web Based example from IBM.
  • Memento : Capture, hold and externalise state without violating encapsulation, allows plugins to be configured by the container.
  • Call Back : Allows the Plugin Modules to access 'services' from the container/environment.
  • Dependency Injection : A way to loosen the coupling of Plugin Modules from their environment.
  • Abstract Factory Pattern : Installing and instantiating the Plugin in the environment.
  • Builder Pattern : Required for any non trivial plugin architecture where Plugin Modules are dependent on each other.

Once you grasp those, study some of the existing Plugin Framework implementations and architectures to see how they've been used. Apache have several in Struts, Geronimo custom server assemblies and Tomcat JNDI Resources; Also the Eclipse plugin framework.


We've not got a plugin architecture as such, but I'll explain how we are keeping our client code loosely coupled, and maybe it will give you some ideas.

We are using asp.net. We deliver a main.aspx page which first includes a mediator javascript file. It defines a global object - call it mediator - which is the only global object that we define.

The mediator exposes a simple interface with publish and subscribe messages:

mediator.subscribe(messageName, callback);
mediator.publish(messageName);

After the mediator.js file, the main page includes a number of other javaScript files, each of which consists of an immediate function which registers it's functionality with the mediator. More about this pattern can be found here, or see a previous question of mine here.

You could follow a similar approach - define a single global object (say pluginFramework) which offers an interface for plugins to register their functionality. When you build the html page to deliver to the client, first include the pluginFramework file and then dynamically include the desired JavaScript plugin files, which could depend on the user or the device (e.g. different plugins if the device is touch enabled?). These plugin files would add their functionality to the pluginFramework with an immediate function.

Here is an example of how to allow plugins to add functionality to a menu in the UI:

pluginFramework.js:

var pluginFramework = (function() {
    var menuItems = [];
    function addMenuItemPrivate(itemName, callback) {
        // e.g. add itemName and callback to menuItems
    }
    return {
        addMenuItem: addMenuItemPrivate;
    }
})());

photoPlugin.js:

(function() {
    function addPhoto() {
        //...
    }
    pluginFramework.addMenuItem('add photo', addPhoto)
})());

Hope this was in some way helpful!


Your comment suggests that what you're building is a multi-tenant architecture.

This is a complex requirement - and one that's usually quite hard to retro-fit; it largely depends on where you're starting from.

Firstly, you need to have a decently factored web application in the first place; if you're using an MVC framework, you've got a starting point.

Secondly, you need to decide where you're going to support extensions; for instace, can a client have a complete separate skin for their UI? If so, you need a skinning framework. Can clients change everything, or just plug in different components at key points (e.g. a custom payment provider, or a custom authentication scheme). It's a lot easier to support a limited set of extension points than designing a framework that allows anything to be extended.

Next, your data strategy; there a good article on multi tenancy on MSDN; it's geared at relational databases, but provides ideas you can apply to Mongo.

Finally, you have to come up with a component architecture that supports the extension points you need. As you're dealing with a web application, you need to be able to modify Model, View, Controller and persistance; the best solution is to use the way your MVC framework wants to work, and put the "for tenant x, do y; for tenant z, do w" logic in the controller layer. Play with this, make it work for some of the cases you need, and then work out what's wrong about that and fix it.


If you yourself are doing the client customization, Django sounds ideal for this. Create your core application that handles all the basic functionality you would like. Then for each logically separated thing you need to do/plugin create a new app that you can plug into your main one, simply by adding that app to your installed apps in settings.

This handles skinning (just plop in a new base_site.html template, have all your others inherit from it), multiple database support, plugins, and gives you the ability to pretty quickly develop and deploy new plugins/features.

See merengue or pinax for more generic examples of what your internal system could be.


Need Your Help

Convert rune to int?

go type-conversion idioms rune

In the following code, I iterate over a string rune by rune, but I'll actually need an int to perform some checksum calculation. Do I really need to encode the rune into a []byte, then convert it t...

Python: Importing urllib.quote

python python-3.x import urllib

I would like to use urllib.quote(). But python (python3) is not finding the module.