Implementing the application tier in Django
Django is a web framework. It implements the Model-Template-View architecture, as discussed on the Architecture page. Our intention is to use some new technology to re-implement Publishers' Assistant software for a variety of platforms (see the Goals page). Publishers' Assistant has both desktop-GUI-based and Web-based products. Thus, in order to use Django as the framework for re-implementing PubAssist, it is necessary to either:
- choose to implement a Web interface only; or
- add some additional functions to allow the same Django models and application logic (i.e., views) to use a GUI-based presentation layer
This page discusses the implications of these two options for how we implement the application tier (the business logic) in Django.
Contents
Web-only interface
This is the simpler of the two options, because it would avoid any development at the framework level and only require that we implement business logic in the form of view functions, which are Python functions with a very specific interface: they
accept HttpRequest objects as a first argument (and possibly other arguments)
return an HttpResponse object, or raise an exception
With this approach, our application tier could be "monolithic" in the sense that every request is handled by exactly one function in the application tier (which may of course make calls to other methods, especially on models); this one function would provide end-to-end processing of the business logic before returning control to the presentation tier.
Advantages
This approach has several advantages, namely:
- it's simple
- it uses Django only as it's intended to be used
it nicely divides client-server interaction into requests and responses; there's no need to deal with the possibility of arbitrary events happening at any time
- it keeps business logic on the server side, making it easier to control and maintain than an approach which puts business logic (in potentially different versions) on individual clients
Disadvantages
The advantages of a Web-only interface come at a price, though:
- users have an existing expectation of a desktop product
- it requires that users work online
it is constrained to what can be done with HTML forms and JavaScript/AJAX
For these reasons, a Web-only interface is undesirable, at least as a long-term plan for the application. (In the shorter term, though, a Web-only interface is still the best solution for a new product, since it will allow people to start using it as quickly as possible.)
Providing both a desktop GUI and a Web interface
We probably eventually want to provide both kinds of interface to users (and, if I had my druthers, we'd also have a command line/scripting interface).
Advantages
Providing a GUI in addition to a Web interface has the potential to make up for the limitations of a Web-only interface, since:
- it meets users' expectations of having both products
- it allows users to work offline
- it provides an alternative way to do things (e.g., multi-step accounting processes) which are difficult to do with HTML forms
Disadvantages
On the other hand, creating and maintaining both types of interface increases the complexity of the code and development process:
- we need an additional type of presentation tier that Django provides no framework for
- business logic must deal with the idiosyncrasies of each type of interface
- we must be careful to separate business logic specific to a particular interface from "common" business logic
- we must run tests against both types of interface
Implications for the architecture
In addition to the inescapable fact that multiple interfaces means more code, providing multiple interfaces has implications for how we think about the application's architecture and how we design functions, particularly in the application tier.
As discussed on the Architecture page (see the comments), I think it makes sense to consider that business logic contains logic specific to the presentation of data. Just to re-iterate the point: suppose we store a book's retail price as a floating point number. It's not the job of either the database tier or the presentation tier to distinguish between the meaning of that data (a dollar amount) from the meaning of a different floating point number (e.g., a height measurement). Thus, it's the application tier's job to translate that data in a way which respects its meaning (e.g., ensuring that it has only two decimal places).
But now we have a problem. It's at least conceivable that different presentation tiers, such as a desktop GUI and a web interface, expect to receive data in different formats for display. The Web interface might expect pure Unicode strings, while the GUI might expect a number. (In reality, the requirements are not likely to be so strict; Django's templating system, at least, will implicitly convert and Python data type to an HTML-escaped Unicode string, unless you tell it to do otherwise.) Thus, there's a need for two business logic functions that translate floating point numbers into the format required by each presentation layer. At a lower level, both of those functions need to deal with floating point numbers that represent dollar amounts, so we now have three functions, where a Web-only interface had only one:
|----|
|GUI | <-> [GUI-specific business logic] <-|
|----| |
|-> [Common dollar-amount logic] <-> | Data tier |
|----| |
|Web | <-> [Web-specific business logic] <-|
|----|This implies that, by adding another interface, we have lost the "monolithic" character of the application tier: it now has at least two layers, one which provides business logic in common to all interfaces, as well as a layer containing separate "translation" modules which handle the business logic specific to particular interfaces. Again, this picture is based on the assumption that the presentation tier is always dumb, i.e., that it is not responsible for keeping track of the meaning of different types of data or converting from one type to another for display and validation purposes. This may be an unreasonable assumption; it's worth discussing.
Having a multi-layered application tier in turn means that development of application tier functions is a multi-step process. First, we have to define exactly what needs to happen across the whole application tier for that function; second, we need to separate the parts of the function which are specific to a particular interface from the parts which are common to all interfaces; third, we need to define the parameters for the common function; fourth, we need to implement each of the interface-specific functions and the common function; and finally, we need to test each interface-specific/common function pair.
