Like most web frameworks, Drupal has three easily-identifiable architectural layers:
- Data, including the DB abstraction layer and some contrib modules such as Views
- Logic, consisting mostly of module code
- Presentation, representing by the theme layer
When thinking about Drupal, it’s therefore pretty easy to apply some familiar (outside of Drupal-world) concepts and principles. And the three-tier architecture is popular precisely because it’s a good model that is widely applicable. The three-tier approach can easily become the MVC model, which most other web frameworks explicitly adopt as their fundamental architecture, though the variation in quality of these “MVC” frameworks suggests that they don’t always adopt it correctly.
So, at a trivial level, it’s possible to describe Drupal as having a three-tier architecture. But there are two important things that this fails to capture:
The Presentation-Abstraction-Control model (or Hierarchical MVC)
Drupal doesn’t just have a “flat” MVC model: it has a “hierarchical” MVC model more similar to the PAC model (I apologise for the use of these buzzwords, but I think it’s better to try to use existing terms that some people might be familiar with than to create new ones). In the PAC model, different components have their own Presentation-Abstraction-Control triads, and these form part of the overall output. This contrasts with a “flat” MVC model which involves a controller manipulating a model in order to create some kind of data structure which is then handed over to the view for complete rendering. Below is a conceptual diagram of a block within a page, which contains a view, which in turn contains two nodes. Each is rendered separately, and the output is incorporated within the output of its container.
As an example, consider a Drupal page which contains some blocks. Each block is rendered separately, with its own model (data), view (theme) and controller (module implementation of hook_block()). The block returns its finished product to the page, which simply incorporates it. Under flat MVC, the block might return a data structure that the higher-level view must transform into HTML.
So, whilst there are three layers in each of these processes, what we actually have is a series of decoupled MVC triads. Many MVC frameworks have to invent new concepts in order to squeeze this idea into their otherwise “flat” MVC implementation (e.g. Action Helpers in Zend Framework).
Modules are Vertical, not Horizontal
A module actually represents a vertical “slice” of the three-layer framework. A module can define a data schema, provide business logic, and define and implement theme functions necessary to present the data and logic to the user. Modules (or groups of tightly-related modules) provide whole “free-standing” units of functionality. They also (often) provide APIs allowing other modules to access that functionality, and plug their own behaviours into it.
This can break some assumptions about three-layer architectures. For example, one assumption might be that any part of the logic layer can talk directly to any part of the data layer. But if we already have some logic for accessing that part of the data layer (an API implemented by a module) then it’s really important that this always be used – we should never really update, say, the node table directly in a SQL query when node_save() does a much better job – and ensures that other modules get a chance to react to your changes via hook_nodeapi() (or its Drupal 7 replacements).
In “Domain Driven Design” terminology, we might say that each module represents a “bounded context”, an area of your application which should only be accessed via exposed interfaces rather than, say, direct mucking about with the data layer. In Drupal terms, this means that we only make changes to a module’s data using the APIs provided by that module itself. Direct changes, made either by other modules or by external processes, can result in inconsistent behaviour, because the module won’t even know that they have happened.
If treating modules as vertical “slices” of an application, and putting a ban on direct updates to the data owned by this “slice” sounds like it’s a restriction for no real gain, also consider the benefits of doing this. Drupal sites are complex interwoven lattices of data and functionality. Well-written modules expose a lot of their internal behaviour to the rest of the system, making it easy for other modules to rely on the original module’s behaviour. So we can write a module that always performs a certain operation whenever some other module performs a data update (say, something that emails a list whenever a piece of content is updated). This relies on the module providing hooks for other modules to react to, but this is standard practice in well-written modules. It also relies on the module’s API always being used. In our previous example, if someone changed the content by writing directly to the DB, no hooks will be fired and our email will not be sent. The whole architecture is undermined, and it becomes impossible to build our new functionality. The cost of using the correct APIs is more than outweighed by the ease with which it becomes possible to add functionality in future developments.
Neither of the two major concepts I’ve outlined above are really explained by referring to Drupal as a three-tier architecture. Although it’s a true description of Drupal as a “framework”, it’s not a sufficient description of how applications built with Drupal really operate. Part of the challenge of communicating how Drupal works to a non-Drupal audience is explaining the real benefits of following the basic patterns of Drupal implementation as a matter of principle, without taking short-cuts to “easier” solutions that undermine much of the existing functionality, never mind the future developments.