Drupal architecture – how to implement loosely coupled communication across modules

Drupal is a free and opensource Content Management System which is used for building Websites. It is based on LAMP Architecture using PHP as implementation language. You can read success stories from the website and from the author Dries Buytaert ‘s personal blog. Most of the success of Drupal derives from his modular architecture which simplifies development, collaboration and user contribution. Drupal’s history can be read here.

Bird’s eye view of Drupal architecture can be sketeched by following diagram:

At the ground level there is Drupal API. It implements the basic functionality of the module system and of theCMS. Physically it is made up by a folder called includes/ which contains a set of php files (named with .incextension). Each of this php file implements an API that can be exploited by upper levels modules.
Core package contains all the Drupal Modules that implement the CMS engines. In this package you can find modules for node management, blogging, commenting, forum, menu and so on. Here is a list of Drupal v7 core modules as they appear on filesystem:

On top of Core modules there are Community Modules. These are modules contributed by the opensource community which are not in the main distribution. For example you can fine modules for Adsense, Amazon integration, Voting and many more (here you can find a complete list of community modules available).
At end there are User modules which are custom private modules built by developers for implementing specific project’s needs. A typical website is deployed using Drupal API and Core modules.

Drupal API

Following diagram reports how drupal API is structured (content of the /includes directory):

There is an entry point file which is called index.php. This is called on every Drupal Request. According the request it includes several .inc files. Every file implements a particular features: for example database.inc exports the API to access database, module.inc the hidden work of the module system, theme.inc implements the theming subsystem and so on. Every include exports a set of constants and functions in the form of:

<?php
// module sample.inc
define('SAMPLE_CONSTANT', 'sample value');
......

function a_method_1($param1, $param2) {
	// code here ...
}

function a_method_2() {
	// code here ...
}

function a_method_3($input_param1, $input_param2, $input_param3) {
	// code here
}
...................
...................

As an example this is the code extracted from includes/bootstrap.inc:

Here you can find a description on how drupal boots and processes each request.

Drupal Modules

A module is a set of PHP and configuration files deployed on a particular folder of Drupal installation. These files follows a particular convention according the following:

each has a

  • .info file (example: mymodule.info) which contains version and dependency information about the module
  • .module file (example: mymodule.module) which contains PHP code implementing module functionality. Generally module uses Drupal API
  • .api file (example: mymodule.api) which contains hooks implemented by module: event to which module is interested
  • .install file (example: mymodule.install) which contains code to execute when module is installed/uninstalled

One of the first things I questioned myself is how they implemented loosely coupled communication between modules. At an abstract level they used Observer/Observable pattern principally based on a file name convention. Every module notifies a set of internal events which carry out data. These events are called Hooks. When a module is interested in an event, it implements a particular method whose name contains the event name plus a prefix, which will be automatically called by the module subsystem. Dynamics is sketched by following diagram:

When Module_A wants to send an event to other interested modules, it invokes method invoke_all() from Drupal API (file modules.inc). invoke_all finds all modules implementing that particular hooks, calling for each a method called <modulename>_<hookname>(params).