WebIssues/MVC Pattern
Contents
Introduction to MVC
The architecture of many modern web applications is based on a common MVC pattern consisting of three layers:
- The Model represents the information on which the application operates its business logic.
- The View renders the model into a web page suitable for interaction with the user.
- The Controller responds to user actions and invokes changes on the model or view as appropriate.
Each layer can be further divided. The model usually consists of a database abstraction layer and a data access and manipulation layer. The view consists of a layout common to all pages, templates of individual pages and logic which glues these two together. The controller contains a front controller, which does the work common to all controllers, and actions which contain only the controller code specific to one page.
An example of a framework implementing such model is symfony, which I took as a reference because it has very good documentation and clean code. There are however some important differences between the architecture of symfony-based applications and the design of the WebIssues Server and it's custom framework.
Multiple Entry Points
Many applications, including symfony, use URL rewriting so that all request are handled by a single entry point and the action to invoke is determined by the URL, for example:
www.example.com/article/view/123
URL rewriting also allows to use user-friendly URLs and make the dynamic application resemble a static website, for example:
www.example.com/2009/07/introduction-to-mvc.html
Such approach is not needed in case of an application like WebIssues, which doesn't need to be indexed by search engines and doesn't require user-friendly URLs. So the WebIssues framework will use traditional URLs, for example:
www.example.com/client/viewfolder.php?id=10&page=4
The advantage is that we don't need to set routing rules and configure mod_rewrite. It also makes WebIssues easier to deploy on Windows with IIS where URL rewriting is usually not available.
Page/View Coupling
In many traditional MVC applications, including symfony, actions and views are not tightly coupled; the same view can be rendered by many different actions, and on the other hand, one action can by rendered using different views depending on the result (for example success/failure) or context.
Though this is a powerful mechanism, it can also be difficult to manage and can make the code less clear. In WebIssues, each Page object implements a single action and has a single associated view template. Page logic and template are stored in two files with the same location and names, but different extension. This makes it easy to find a template related to a page and vice versa.
Application
The WebIssues Server doesn't have a single entry point, but it still requires a single object which implements all logic common to all pages. This object is called the Application. It serves as the front controller which executes the appropriate page action and outputs the rendered view. The Application class can be extended to implement special behavior for a group of pages, for example to assign a common decorator to them or to implement security for the administration area.
The application also creates and hold references to all other objects forming a single context of execution, such as the request, response, session, logger, etc. The reference to the global application object is available anywhere in the code; in addition references to frequently used components such as the request and response objects are available as members of all components.
Components
Usually to render a whole page more that one templates are needed. A view template can include other templates, for example common controls or blocks which appear on multiple pages. It can also be decorated with another template, for example page content can be decorated with the layout template common to all pages.
The common parts and decorators also consist of a logic layer and a view layer, similar to pages. The WebIssues framework introduces a concept of a Component, which is an object associated with a view template. A Page is simply a special case of a Component and actually its subclass. The template associate with a page can use other components and their templates can subsequently use their own components.
The example object tree of an application can look as follows:
Application | +- Page | +- PageView | +- DecoratorComponent | | | +- DecoratorComponentView | +- PartComponent | +- PartComponentView | +- SubPartComponent | +- SubPartComponentView
A decorator acts just like a regular component, except that it's contents is not inserted into the containing view, but rather the contents of the containing view is inserted in an appropriate placeholder of the decorator. This allows for example to wrap page contents in a layout or wrap block contents in a frame. So the final contents of the rendered page looks as follows:
+- DecoratorComponentTemplate ------------------------------------------------------+ | | | +- PageTemplate ------------------------------+ | | | | | | | +- PartComponentTemplate ----------+ | | | | | | | | | | | +- SubPartComponentTemplate -+ | | | | | | | | | | | | | | | | | | | | | | +----------------------------+ | | | | | | | | | | | +----------------------------------+ | | | | | | | | | | | +---------------------------------------------+ | | | +-----------------------------------------------------------------------------------+