Domain Patterns. Transaction Script, Table Module, Domain Model

by jon on August 11, 2008

I've been going back to Patterns of Enterprise Application Architecture lately to brush up on some concepts in order to apply them to PHP with Zend and Doctrine.

At the beginning of the book, Martin Fowler suggests that there's a fork at the beginning of every development road where each project either ends up attempting to use Transaction Scripts, a Table Module, or a Domain Model. He argues that as project complexity increases, the two former design methodologies start to break down, causing code duplication and unmanageable complexity.

Transaction Scripts

Organizes business logic by procedures where each procedure handles a single request from the presentation. –Martin Fowler

In PHP, a transaction script sounds like a simple function call. It could be something as simple as:

 
saveWishlist($currentWishlist);
 

saveWishlist() could easily grab $currentWishlist, which could be an object containing a wishlist and a user ID and run an INSERT statement against a MySQL database.

This ends up becoming a problem if wishlists start having alternate transactions with similar logic. For example, what if the wishlist belongs to a preferred customer, or someone of a particular region? What if the application grows and now includes wedding lists and birthday lists? Each one of these procedures would perform near identical operations against the database.

Table Module

A single instance that handles the business logic for all rows in a database table or view.–Martin Fowler

Lists are beginning to become an abstract concept in the application. At this point, a list could actually be an object that represents a table row in the database. A Table Module pattern would have a series of objects that would encapsulate rules for particular tables.

We can have a MarriageList object representing a marriage_list table, and a BirthdayList object representing a birthday_list table. In each of these objects, we would put business rules specific to each class / table.

A Domain Model

An object model of the domain that incorporates both behavior and data.–Martin Fowler

Now a one-table-per-list approach to an extent... until the marketing director calls up the CIO and says that it would be really cool if you could "share" lists with friends (since he read that O'Reilly article from two years ago about Web 2.0).

All of a sudden, lists become a complex construct in your application. They start looking more like this:
domain model example using lists
Each List object might have its own business rules based not only on what type of list it is, but also who is asking for it (is it your friend's birthday list that he's sharing with his girlfriend, or is it your own birthday list that you're sharing with your girlfriend?) The object graph above would allow some really neat code like:

 
$myFriends = $currentPerson->getFriends();
 
$myFriendLists = array();
foreach ($myFriends->getFriends() as $friend)
        $myFriendLists[] = $friend->getAllLists();
 

what we would end up with is a collection of all our friend's lists. We could do cross-referencing or reporting to see who likes what and create object graphs with entities or help users visualize common products among friends for each occasion. Note that these objects may or may not (and probably don't) correspond to rows in a database.

This kind of work is nearly impossible with a Table Module approach because modelling the person->friend (one-to-many) and correlating that with the person->lists (one-to-many) doesn't make much sense in the database directly, unless the whole application is based on list sharing. The code above could help form an Entity called FriendLists or we could make an Entity called FriendWeddingLists. Making these distinctions in code is much easier than in a normalized database.

Is a Domain Model Worth It?

I would say that in most cases, the answer to that question is no, however a lot of the tools in the Zend Framework and Doctrine facilitate the building of preliminary Domains. We can create simple object graphs and separate our models from the controller layer (anything inherited from Zend_Controller) through Repositories that aren't REAL Repositories by Evans / Fowler's definition. Regardless, they provide a coarse interface for the Controller to interact with the underlying data model.

Leave a Comment

Previous post:

Next post: