The battle strikes right to the heart of the three elements of Object Oriented Programming:
- Encapsulation: data and methods are grouped together
- Information Hiding: implementation details are hidden
- 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. There 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.
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.
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.
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.
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 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!
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.