[Welcome] [TitleIndex] [WordIndex

Separate User Interface from Domain Objects

Domain objects are objects relating to the application's domain: catalog items and order invoices for a shopping site, projects and bugs for a bug tracker, users and messages for a bulletin board. (Sometimes the term "business objects" is used instead.)

The user interface is all the code that generates HTML and processes forms.

Don't mix the two. Sometimes it's tempting to be sloppy, to write PTL code that knows a lot about how the data is stored:

def _q_index [html] (request):
    db = ... ; cursor = ...
    cursor.execute('select * from products')
    for row in cursor.fetchall():
        '<td>%s</td>' % row[0]   # Product name

The result is that the user interface is intricately tangled with your data representation. If you change the database tables, or some columns go away or reappear, a lot of code will need to be fixed.

Instead, have a separate Python package containing classes for the domain objects. These classes contain no code relating to the web -- not even an .as_html() method. Instead they just represent the object and enforce whatever the domain's constraints are -- if all invoices require at least one purchase item, it's the job of the domain object to ensure this. (I find it helpful to imagine writing a command-line set of tools for manipulating the object. If the command-line tool doesn't need a method, it doesn't belong in the domain object. This actually isn't unrealistic -- you might well want a script to list all unpaid invoices or all new messages.)

It's the UI code's job to display domain objects as HTML and to process forms that result in new objects being created. A common pattern is to use a subpackage for the UI code:

           /invoice.py    # Contains Invoice class
           /product.py    # Contains Product, PerishableProduct, etc. classes
              /pages.ptl  # Generally-used PTL templates
              /catalog.ptl# Templates for the catalog pages 

2010-09-22 22:14