July 28, 2014

...Learn TDD with Codemanship

More Rage On ORM Abuses & How I Do It

So, a day later, and the rage about my last blog post, about how I see many developers abusing Object-Relational Mapping frameworks like Hibernate to build old-fashioned database-driven architectures, continues.

My contention is that we don't use what is essentially a persistence layer to build another persistence layer. We've already got one; we just have to know how to use it.

The litmus test for object-relational persistence - well, any kind of object persistence - is whether we're able to write our application in such a way that if we turned persistence off, the application would carry on working just dandy - albeit with a memory that only lasts as long as the running process.

If persistence occurs at the behest of objects in the core application's logic - and I'm not just talking about domain objects, here - then we have lost that battle.

Without throwing out a platform-specific example - because that way can lead to cargo cults ("Oh, so that's THE way to do it!" etc) - let's illustrate with a pseudo-example:

Mary is writing an application that runs the mini-site for a university physics department. She needs it to do things like listing academic staff, listing courses and modules, and accepting feedback from students.

In a persistence-agnostic application, she would just use objects that live in memory. Perhaps at the root there is a Faculty object. Perhaps this Faculty has a collection of Staff. Perhaps each staff member teaches specific Course Modules. Hey, stranger things have happened.

In the purely OO version, the root object is just there. There's only one Faculty. It's a singleton. (Gasps from the audience!)

So Mary writes it so that Faculty is instantiated when the web application starts up as an app variable.

She adds staff members to the faculty, using an add() method on the interface of Faculty. Inside, it inserts the staff member into the staff collection that faculty holds.

The staff listing page just iterates through that collection and builds a listing for each member. Simples.

Clicking on the link for a staff member takes you to their page, which lists the course modules they teach.

So far, no database. No DAOs. No SQL. No HQL. It's all happening in memory, and when we shut the web app down, all that data is lost.

But, and this is the important point, while the app is running, it does work. Mary knocks up a bit of code to pre-populate the application with staff members and course modules, for testing purposes. To her, this is no different to writing a SQL script that pre-populates a database. Main memory is her database - it's where the data is to be found.

Now, Mary wants to add persistence so she can deploy this web app into the real world.

Her goal is to do it, ideally, without changing a single line of the code she's already written. Her application works. It just forgets, is all.

So now, instead of building the test objects in a bit of code, she creates a database that has a FACULTY table (with only one record in it, the singleton), a STAFF_MEMBER table and a COURSE_MODULE table with associated foreign keys.

She creates a mapping file for her ORM that maps these tables onto their corresponding classes. Then she writes a sliver of code to fetch Faculty from the database. And, sure, if you want to call the object where that code resides a "FacultyRepository" then be my guest. Importantly, it lives aside from the logic of the application. It is not part of the domain model.

That's pretty much it, bar the shouting. The ORM takes care of the navigations. If we add a staff member to the faculty, another sliver of code executed, say, after server page processing persists the Faculty and the ORM cascades that down through it's relationships (providing we've specified the mapping that way.)

By using, say an Http filter (or an HttpModule, if that's what your tech architecture calls it) we can remove persistence concerns completely from the core logic of the application and achieve it as a completely separate done-in-one-place-only concern.

Hibernate's session-per-request pattern, for example, allows us to manage the scope of persistent transactions - fetching, saving, detaching and re-attaching persistent objects and so on - before and after page processing. Our Http filters just need to know where to find the persistent root objects. (Where is Faculty? It's in application state.) The ORM and our mapping takes care of the rest.

And so it is, without changing a line of code in any of her server pages, or her domain model, and without writing an DAOs or that sort of nonsense, Mary is able to make her application state persistent. And she can even, with a quick change to a config file, turn persistence on and off, and swap one method of persistence with another.

The controllers - the server pages - in Mary's web app need know nothing about persistence. They access state in much the same way they would anyway, either locally as variables on each server page, or as session and application variables. For them, the objects are just there when they're needed. And new objects they trigger the creation of are automatically inserted into the database by the ORM (ditto deletions and updates).

Now, we can mix and match and slice and dice this approach. Mary could have used a pre-processing Http filter to load Faculty into session state at the start of a session, making it a thread-specific singleton, and the persistence Http filters could be looking in session state for persistent objects. Or she could load it anew on each page load.

The important thing to remember is that the server page is non the wiser. All it needs to know is where to look to find these objects in memory. They are just there.

This is my goal when tackling object persistence. I want it to look like magic; something that just happens and we don't need to know how. Of course, someone needs to know, and, yes, there is more to it than the potted example I've given you. But the principles remain the same: object persistence should be a completely separate concern, not just from the domain model, but from all the core application code, including UI, controllers, and so on.

Posted 3 years, 5 months ago on July 28, 2014