Monday, April 30, 2012

A Covert War

A covert war is being waged.  A battle between competing forces.  In all likelihood, it is taking place in your own code base!  The outcome has the potential to change the very way you think and develop!  It's influence will reach all layers of your application, from data access, to domain, to UI, and beyond!  It is the war between databases and objects!

The battle strikes right to the heart of the three elements of Object Oriented Programming:

  1. Encapsulation: data and methods are grouped together
  2. Information Hiding: implementation details are hidden
  3. Inheritance/Polymorphism: an object may derive from another object, inheriting its data and methods/object may override those methods, and objects can be referred to interchangeably through a uniform interface
The antagonists generally take the form of Object Relational Mappers.  You can find this in both Data Mapper frameworks (like Hibernate), and Active Record frameworks (like Ruby's Active Record).  They are clever.  They come to you with promises of wonderful things: time savings, less maintenance, elimination of boiler plate code, and sometimes even database independence.  Their siren song is hard to resist.

But the developer must be wary!  For while their promises are sweet, their true sinister goal is the complete annihilation of object oriented code.  Perhaps you are skeptical?  To truly see the danger, we must understand how these so called ORMs are eroding the core values of object oriented programming.

Encapsulation
The ORM is leading a strong assault against Encapsulation.  This is possibly it's most sinister move.  Encapsulation is about putting data and behavior together.  The ORM doesn't prevent that.  But it does cause you to break data apart into separate objects that you might otherwise have kept together.  It does this because it tricks you into thinking about the constraints of your physical storage instead of thinking about the design of your objects.  Typical examples are objects that represent link tables.  Or parent/child tables represented directly as objects, where the foreign keys are filled in manually by the consuming code, instead of being capsulated by an object.

Information Hiding
The ORM also subtly attacks information hiding.  They make you a simple proposition: in return for automatic persistence of your data, all they ask is that your objects publicly expose each data field!  On the surface, it seems so harmless, but it's a cunning attack at Information Hiding.  No longer can your object hide away the details of the data, exposing an abstract interface of methods over that data.  No!  Now all that data is available to anyone!  

You may feel this pain in simple ways.  Like when you decide to change a few booleans to a single string flag.  It seems simple enough, until you discover that, while you weren't looking, a large amount of code has come to depend on those booleans!  Tragic rabbit-hole-refactoring then ensues.

Or the pain may be more subtle, but also more malevolent.  Like when you are forced to write terribly awkward code in the setters of your fields in a vain and complicated attempt to keep your object in a valid state.

Inheritance/Polymorphism
On another front of the war, we find Inheritance being beaten back by the powerful onslaught of the ORM.  Here the ORM is more direct in its methods, going so far as to tell you exactly what "kinds" of inheritance you may use.  And coupling that inheritance directly to the structure of the database tables.  Thus severely limiting any "Open Closed" type flexibility you might have hoped to gain!  And woe to the poor developer that still attempts to leverage inheritance in a extensible type hierarchy sort of way, for the ORM will combat them at every turn!  It's best to just give in, and follow the narrow table structure driven  inheritance rules the ORM has laid out for you.

Now we see how the ORMs are attacking the core of our object oriented lifestyle.  But, cunning as they are, it may still not be clear what an adverse affect this can truly have on your day to day development experience.  In an attempt to shed some light on this issue, lets broaden our view to some typical coding patterns.

DDD Syndrome
The first, I will call the DDD (Domain Driven Design) Syndrome.  With DDD Syndrome, the developer writes rich domain models, but attempts to persist those same models with the ORM.  The pressures of the ORM end up overpowering the practices of DDD.  A number of symptoms may immediately appear.  For example, you find a model which has been forced to make all it's information public, but still attempts to expose an abstract interface.  This leads to confusion in the code base about what methods and properties consumers of the object are really supposed to use.  

You may also see well defined constructors settings up valid state, but at the same time a default constructor which bypasses these rules so the objects can be inflated by the ORM.  Or worse still, overly complex and redundant code that attempts to keep the object in a valid state.  This code frequently appears in property setters.  

And due to the attack on encapsulation, DDD Syndrome often exhibits complex event based code that attempts to keep multiple objects (each representing separate tables) in some consistent state.  This leads to logic being duplicated on different objects, and makes it very difficult to understand how the objects behave.

Service Syndrome
Service Syndrome is the opposite of DDD Syndrome.  Instead of trying to put your logic in the persisted objects, you leave those objects anemic.  This is a clever counter strike against the ORM in some ways, as you are letting it exist, but limiting it's involvement.  

Unfortunately, the downside is you've moved your logic out of the objects, and into "services."  Classes that have no state of their own, take persisted record objects as input, and house your logic.  These classes are very procedural in nature and don't take advantage of any of the core OO elements!

Fight On!
This war is still being waged.  For the moment, it appears that the ORMs are winning.  But we must not lose hope!  Micro-ORMs are a recent development, their influence on this battle is not yet fully known.  Perhaps if wielded in the right manner they could allow the developer to relegate persistence concerns out of their true domain objects?  It remains to be seen.

2 comments:

  1. I think the best of both worlds is more of a hybrid between the two extremes of "Service" and "DDD" syndromes. I have found that ORM classes should be dealt with by simply allowing them to be wrapped by your higher-level Domain Entity classes. The ORM classes are not your domain classes at all, nor should they be, and talking about them like they are is what causes your confusion. The DDD patterns imply an "infrastructure layer" that takes care of persistence, which would include these classes that map one-to-one with database tables. Separating your actual concrete persistence from your actual domain classes prevents a host of problems you have mentioned. It is subtle distinction, but still an important difference to say, "my Domain classes are constructed with one-to-many persistence classes that they operate on" instead of, "my logic is in services classes that take anemic domain classes".

    ReplyDelete
  2. The solution was called orthogonal persistence. You code using domain objects exclusively, and the runtime system worries about persisting them.

    ReplyDelete

Note: Only a member of this blog may post a comment.