Domain Explosion
There’s an AntiPattern that I still see in the wild far too often: the overly normalized domain. It’s easy to spot – you find over 50 files staring you down after opening the app/models directory in what should have been a basic Rails application.
A simple example is the following domain snippet, showing a Product model with all the trappings that surround it.

Each of Condition, Color, ProductState and ShippingType are separate models, with separate tables, separate factory definitions, unit tests, RESTful controllers, migrations, etc.
And each is responsible for a single name column.
That’s a lot of code. The more code I see in an application, the sorrier I feel for the client. Code is a liability. Like a pack mule, the development process becomes bogged down, cumbersome, and eventually comes to a halt due to an overburden of code.
A much simpler solution is to reduce each of those models into columns on Product, using validations and named scopes to give all the benefits of the domain above:
class Product < ActiveRecord::Base
COLORS = %w(Red White Blue)
named_scope :colored, lambda {|c| {:conditions => {:color => c}}}
validates_inclusion_of :color, :in => COLORS
end
Even the Photo model can be reimplemented this way, if you use the fantastic Paperclip plugin.
Developers, not coders.
It sounds absurd and even self defeating, but our job as developers isn’t to write code. Our job is to produce value for the client, and more often than not that means removing code.
That requires a whole lot of judgment, which translates to a whole lot of experience.
Behavior, not nouns.
The idea of removing classes in favor of values doesn’t fly in the face of good Object Oriented practices. The important distinction is that the classes above have essentially no behavior. A class without behavior is just a glorified value wrapped in maintenance costs.
Behavior, not vocabulary, dictates normalization. The moment you add a shipping_rate method to the Product model, you know you should migrate to a ShippingType class. The moment you have to be able to modify that price at runtime, you move to a model.
Simplest thing possible.
That’s the beauty of the BDD way. BDD teaches you to write your specs first, and produce the simplest thing possible in order to get them to pass. It also keeps you focused on producing business value for your client – something a domain explosion definitely doesn’t do.
The Plug
Chad Pytel and I delve into issues just like this in our upcoming Addison Wesley book, Rails AntiPatterns. Stay tuned for more information on the book, or for more posts like this.
Comments
Very simple, but awesome post man. Obviously the world needs both coders and developers, but a framework like Rails allows developers react to change much quicker. This lets the business not have to wait or slow down for a technical solution to be finished. They can have it as soon as possible, if someone doesn’t go down a rabbit hole and want to use every single method available.
Nice post.
I like the term “data containers” and “god classes” for the two extreme cases of the problem of misplaced responsibility. Your “data container” example is a good one.
Designs with many data containers are at risk for Law of Demeter violations as clients have to navigate chains of intermediaries.
What you’ve done to fix the problem looks to me like a pattern I’ve seen described tersely as “Move Behavior Close to Data.”
Well put, Tammer. As an anecdote to add to your “developers, not coders,” my last job (software at a product company) was in the “Capabilities Development” department. Our goal was not to write code, but to provide capabilities to the business. While this was often achieved with newly written software, it’s about the frame of mind.
All agreed, except your dig at vocabulary. I think it is too much technical domain thinking that leads to a problem like that in your example. The only app that should be talking about ‘colors’ as objects is www.sherwin-williams.com. Thinking about that before even writing the code (implementing the behavior) prevents exactly what you’re talking about. After all what is writing specs other than getting your vocabulary straight before coding? I guess I do VDD (uh…) because I just don’t like the way somehow this strategy has become intertwined with testing, which I think has another more important responsibility.
@dan: Yes, this is definitely an argument against data containers. And I hadn’t even thought of the ensuing LOD violations (or copious accessor code necessary to avoid it).
@floyd: I wasn’t trying to make a dig at vocabulary – I think establishing a correct and consistent vocabulary shared between the developer and client is an incredibly important step in the development process.
What I wanted to get across is that using vocabulary to determine when to move a piece of code into its own class or model is the wrong way to go.
@jason: I’m thinking about changing my title to “enabler” :)
@corey:
Totally (I’ve often been guilty of that). But it’s important to remember the “refactor” step in “red, green, refactor” :)
My point wasn’t about client communication. It was about behavior. I was trying to say the opposite of “Behavior, not vocabulary, dictates normalization.” when I said “Thinking about that [vocabulary] before even writing the code (implementing the behavior) prevents exactly what you’re talking about.”
I do think we probably agree in the end, except you take the point to be that you need BDD, which means writing specs, which means tests as spec + TDD. I’m just trying to imply that your argument raises the possibility that the real insight, in your post and in BDD, is getting your vocabulary straight first improves your implementation. I would just prefer to keep that separate from the process of writing tests, which I think tend to be overburdened by specifying behavior as well as proving the implementation (not that I have a good standardized method or set of tools to offer as an alternative). But maybe that’s because I suck at “true” TDD.
Leave a comment: