My last post on Emergent design got me thinking about other common architectural patterns (in addition to ORM) which may not necessarily be the best solution for the types of problems to which they are commonly applied.
I believe N-tier architecture where N=3 is a common choice, is yet another example of Big Upfront Design locking us into a unnecessary straight-jacket even before we look closely at our problem. Now the purpose of each tier in such an architecture is to provide a service interface to subsequent tiers. The goal being that tiers are loosely coupled and hence inter-changeable. From a clients perspective the tier you will most likely want to change is the presentation tier. So a PDA client will want a different presentation tier then a desktop client, for example. But both clients would want to share everything else from the service tier down.
All of this is common wisdom and as spawned a whole architectural vernacular of its own around the term Service Orientated Architecture. So what is wrong with SOA you ask? Well nothing, other than the term is not very specific and it tells you nothing about the type of problem to which it applies. SOA is the proverbial solution looking for a problem. So lets start this discussion again from the other direction. Lets choose a concrete problem and then identify a clean working solution.
The problem: My users want to store a bunch of related data records in a database. They also want to create, retrieve, update and delete (CRUD) these records remotely through a web browser interface .
OK. They want to store related data records. Sounds like a job for a relational database to me. They then want to view these records over HTTP. Well the verbs in HTTP (POST, GET, PUT, DELETE) sort of map to CRUD operations so why not make use of this? Also my user will view data records via HTML web forms presented to the browser. So there is inherent data coupling between my web presentation and my entity/domain model. Why not except this coupling as intrinsic?
From an Emergent design perspective, the simplest thing that can possibly work is an architecture that allows you to create data record hashmaps from HTML presentation views, and persist them in a relational database. Better still from an OO perspective, it would be even better if those hashmaps could persist themselves, and perhaps do other useful house keeping like data record validation that only they should know how to do. Viola - The simplest CRUD architecture possible. A Controller to handle the HTTP verbs and map them to actions on our database. An ActiveRecord that models database records as an hashmap and is capable of persisting itself, and a View that translates the model to an HTML presentation ready to be sent back to the browser over HTTP.
Sounds very much like Ruby on Rails to me. Now how do you think DHH got there? Did he use big upfront design, or did he choose to focus on his problem afresh and apply emergent design principles? Well it should come as no surprise to find out that Rails was harvested from a real word CRUD application that evolved over time. It should also come as no surprise either to find out that 37 Signals are big advocates of TDD. In fact Rails is the only web framework I know that has TDD built in.
One last thing. The astute amongst you are probably asking what happened to our nice separation of concerns and SOA? Well if you look at my problem statement I didn't need it (YAGNI). My client was using a web browser. I've saved myself a heck of a lot of unnecessary code and shipped my release 3 months early. Its now out there making a ROI for my customer whilst those N-tier folks are still deciding how many tiers they need. Hang-on I hear you cry. So what happens when your customer does decide that he needs another client to the system:
Problem2: Not only does the system need to support CRUD data entry by people, but in addition, we have recently gone into partnership with another company who wants to access the same data records using a remote system. So we now need some way to support this too.
The world as changed so we need to change our system to suite. Unlike the BUFD guys we will only make such an investment if driven to do so by our problem. OK. We understand how to map HTTP verbs to database actions, we also know how to map our model to HTML. Given this it makes a lot of sense to re-use this infrastructure and choose a service interface that makes the most of HTTP, we settle on a RESTful approach. Our previous CRUD based controllers still apply, but now the presentation needs to be machine readable XML instead of HTML. We could put a lot of if.. else.. logic in our controllers to determine what type of presentation to create, but we decide that this would represent a lot of code duplication, so we choose to encapsulate this logic in one place which all controllers can use. This approach is so successful that we then decide to factor it out as part of our home-grown framework. Sounds pretty much like how Rails 1.2 was born.
So now we have a Restful Application that is fully SOA aware and is built on our existing technology base. The only new technology is XML, and given that XHTML is a variant of XML, we could argue that we haven't had to introduce any new technologies at all. So we have managed to introduce a Service tier when we needed it at very little cost.
Some following this discussion may come to the conclusion that DHH just got lucky. To them my answer would be that you create your own luck. By gaining a deep understanding of the problem, keeping the design clean and simple and deferring unnecessary design decisions rather than prematurely locking yourself into false assumptions; then 9 out of 10 times evolutionary paths will present themselves without you having to plan (guess) for them upfront.
In the 1 out of 10 cases where there is no low cost option, then what your problem is telling you is that your application requires diverse personalities, and whether you plan for this upfront or evolve it later, producing such an application will be expensive. The benefit with emergent design is that by applying the YAGNI principle, you will not incur this cost unless you really need to.