Some months ago, Bob Martin tweeted "If the first thing you type, when writing a ruby app, is: 'rails', you've already lost the architecture game." He didn't mean that we should all be using Sinatra or Express or Play. No, what he meant was that our applications should exist other than as webapps.
He recently gave a
talk at Ruby Midwest 2011 about architecture in Rails which I recommend all Railsists watch.
Taking cues from Ivar Jacobsen's Object-Oriented Software Engineering, Martin explains how to design an application's core that "screams" about the domain it is involved with. We should have Interactors (Jacobsen's Controllers rebranded to avoid nameclashes) and ResponseModels and ViewModels and Presenters and Entities which are POROs (Plain Old Ruby Objects) and not ActiveRecord-based.
Martin contends that the database is a detail, and the web is a detail. We don't want these to define our application's architecture. What should define our architecture is our use cases (user stories that have been elaborated to sufficient detail - with acceptance tests).
An insight (for me at least) was his way of explaining architecture - the art of drawing lines with the interesting rule that once you have drawn the lines all the dependencies that cross that line go in the same direction. Following this, we can extract our code into a library (gem, jar, egg, dll, ...) and test our library. Cucumber isn't slow when it isn't waiting on a browser and HTML response rendering. We must integration test these as well, but don't need to test every case through a GUI interface.
This all sounds good - simpler tests, faster tests, more decoupling... but at what cost? Is it more code? Yes, but you won't be sad when waiting for your tests to run! Being able to easily add another delivery mechanism (thick client, perhaps) on top of your application's core is nice - but how often does that actually occur? It hasn't for me.
So currently it boils down to test performance vs coding time. And we all already have our own ideas about that!
Some choice moments from the talk:
Audience: Where can we find the gem that does this for us?
Uncle Bob: It's between your ears, son.
Audience:Are there any examples of a rails app or a software application that also uses rails in the way that you're talking about?
Uncle Bob: I haven't seen it yet.
Who's got one? I do have one, but i can't show it to you. The rails code that I've seen without exception does not follow this pattern.
Uncle Bob: I know what you're thinking. Yeah, but I gotta get stuff done @ 38:40
Clean Code Episode 7
blogpost on 8thlight's blog