Many (or most) application frameworks have adopted a Model-View-Controller (MVC) architecture, for example, Django (they call it MTV but it’s very similar), Pylons, Ruby on Rails, and Struts.
In The Helsinki Declaration (IT Version), Toon Koppelaars makes the case for a different method to architect and implement database applications. Instead of using MVC for what he calls “Window-on-Data” (WoD) applications, Toon suggests using the Helsinki approach, which using acronyms could perhaps be named the UI-BL-DL architecture. It consists of three layers:
- User Interface (UI): this is equivalent to the View and Controller of MVC. It can be implemented with the front-end technology “du jour.”
- Business Logic (BL): this is the Model code that implements queries (the read-BL sublayer) or transactions (the write-BL sublayer) in a manner required by the enterprise using the application.
- Data Logic (DL): this includes the database design as well as integrity constraints.
In the last point, integrity constraints are meant in the broadest sense, as used in Chris Date’s An Introduction to Database Systems (8th edition), Chapter 9, i.e., much more than primary and foreign keys. Toon has in mind something like the SQL Standard CREATE ASSERTION, but he’ll settle for TRIGGERs as an implementation vehicle.
The Helsinki approach appears eminently suitable for the design of a generic database user interface (in this context, the latter is more than the UI layer listed above). Therefore, let me apply these concepts to the minimalist command line interface (a technology “du jour” at some distant time in the past) I presented before.
As a first step, I factored all the calls to Psycopg2 objects out of dbapp.py and into their own module named dblib.py. Then I went further and split out of the latter the application-specific code into a “business logic” module named bl.py, shown below:
# -*- coding: utf-8 -*- from dblib import fetchall, fetchone, execute def get_all(dbconn): return fetchall( dbconn, "SELECT id, title, release_year FROM film ORDER BY id") def get_one(dbconn, id): return fetchone( dbconn, "SELECT id, title, release_year FROM film WHERE id = %s", (id,)) def insert(dbconn, film): return execute( dbconn, "INSERT INTO film VALUES " "(%(id)s, %(title)s, %(release_year)s)", film.__dict__) def update(dbconn, film): return execute( dbconn, "UPDATE film SET title = %s, release_year = %s " "WHERE id = %s", (film.title, film.release_year, film.id)) def delete(dbconn, id): return execute(dbconn, "DELETE FROM film WHERE id = %s", (id,))
As can be seen, segregating this code into its own module means it could easily be re-used by a web (or some other kind of) interface. If you look at the refactored dbapp.py (v0.1.1), you’ll notice that now you’d be hard-pressed to tell which database technology is behind the application. Finally, the dblib.py module is the start of a generic low-level PostgreSQL library.
There are still some rough edges and “magic.” For example, the edit function in dbapp.py validates the release year redundantly with the CHECK constraint in the database. As I understand it, according to the Helsinki method, the UI ought to be calling the DL layer to perform these validations. Furthermore, there is magic in the BL API: the structures (film, row) passed as arguments or returned lack proper definition. Toon recommends using Bertrand Meyer’s Design by Contract (see Object Oriented Object Construction) to specify the UI to BL interface. We’ll revisit these and other topics in forthcoming posts.
As before, the code is available at GitHub.