Domain Explosion

Average reading time is

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

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.

Feel free to submit corrections via github