Because no one in Web Dev uses interfaces

One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development. Interfaces are my go-to example for this. How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another? “Well, it’s Tuesday, so we need to load the Oracle layer instead of the Access layer.”

To prove myself wrong, I built interfaces into this term’s Advanced Database Structures final exam practical. The course has changed a bit since I started teaching it almost 2 years ago (holy crap!). The core objectives are the same, but the underlying technology has been migrated to CouchDB and MongoDB.

From the exam instructions:

You will be refactoring an existing application to work against a MongoDB database and a CouchDB database. The codebase has been set up so that the application works as-is against a MySQL database and you can easily switch back and forth between each of the databases without having to modify anything other than the two Model classes you are responsible for.

The application I give them is a really simple proof-of-concept link shortening service called “fsa.il” and works the same way any other does: you input a long URL and your name, and you get a short URL back. You can also come back later and track click-through and referring URL statistics.

Along the top of the page are tabs for three persistence layer choices: MySQL, MongoDB, and CouchDB. The code they are given to start has a service that keeps track of your preferred data layer in your Session and handles all of the object creation for you. The students, who built the MySQL version of the app for last month’s final exam, are given a working MySQL class, an interface for the persistence layer, and two stubbed-out skeleton classes for the other two layers. Oh, and data dumps for quick import into all three layers.

That’s it. That’s the exam. The students just need to make the app work against the other two databases. The interface definition is only five functions, and ends up yielding ~150-200 lines of code they need to bang out over 3½ hours.

I think it’s a fun exam, but more importantly it’s a great piece for each student’s portfolio. How much of a rock star would you look like on an interview if you could show a piece of code that worked against both relational and non-relational persistence layers without a truckload of third-party code?

It’s interesting writing code that will work against all three layers at the same time. Naming conventions play a big part. It’s easy in SQL to write a query that aliases column names to something friendlier to code:

SELECT
    id AS linkID,
    long_url AS longURL,
    creator_name AS fullName,
    created_date AS createdDate
FROM link_links
WHERE (id = :linkId)

And while you could use Map functions in CouchDB and MongoDB to alias your document field names, that seems like a heckuva waste. Instead, you have to think about it a little bit harder beforehand. If you don’t, you end up doing translation of array key names at the Model layer, which again seems sub-optimal.

The seemingly-minor differences between the implementations of Map/Reduce in CouchDB and MongoDB also some into play. For example, when using the group() method in MongoDB you get an array back instead of a cursor, which means you can’t chain the sort() or limit() methods and have to do those in your Model.

Sorting in CouchDB can also be tricky. CouchDB assumes that you want to sort by your key and doesn’t give you any way to sort by value, making Top 10 lists a real pain to do. You’re back to sorting in your Model.

String manipulation functions are another friction point. SQL has a bevy of them, such as LEFT() and even the dreaded LIKE. No such luck in either MongoDB or CouchDB. You have to fake them in JavaScript. Compare:

WHERE (LEFT(:link, link_len) = link_start)

Versus:

function(obj, prev) {
    if("http://".substr(0, obj.linkLen) === obj.linkStart)
        prev.count++;
}

While that’s doable, and arguably potentially more powerful, it certainly isn’t what I would call elegant. Similarly, CouchDB’s startkey and endkey parameters are nice and speedy, but there are times when I’d kill for a keycontains parameter.

More to the point, I think the course is teaching great skills. Not only do students get to play with a variety of databases1, but they also get to see how easy it is to decouple layers of an application. It’s nice that they get to muck around in Map/Reduce and REST APIs and what not, but the architectural and problem-solving lessons will take them so much further.

Oh, and just to head anyone off at the pass, the exam is done in PHP because working with MongoDB in PHP is stupid easy, while in ColdFusion it’s not for the faint of heart2. The underlying Java library is usable, but requires a whole lot of syntactic icing. That is, a metric ton of javaCast calls and type paranoia. While that might be great for the students to encounter on a take-home assignment, it’s not something they need to be tripping over on a timed exam. And yes, there are some great MongoDB libraries available for ColdFusion, but the point of the exam is to have the students show that they can do it themselves.


  1. If I had another 3 hours on the exam I would totally make the students add in layers for Sqlite and XML.

  2. JSON-related quirks aside, using CouchDB requires an equivalent effort level from both PHP and ColdFusion. Yay REST API.