How I escaped the trap of the table-object binding

I really can’t recall the moment when I started thinking that an object was to be persisted as a table (hence we have a table for every object). I think it was on my first job, using an ORM, but I’m not sure. So what? mine is not a unique story: almost every software developer I know has been through this. I blame schools for this.

So where was I? oh! the table per object idea. That’s absurd. But I digress, let’s move forward. Once upon a time, I worked for a lending company. I was trying to model a loan application process. Something like:

So here it is. Imagine you have to code this. In case you’re not familiar with state machines diagrams, the lines are actions and the circles are states. Before moving forward, get a piece of paper and design a system to automate this process. Just outline the objects and their interactions. Go on, I’ll wait here.

Now, there are 2 different approaches here that will yield very different designs. Let’s review them.

Data centric

The data-centric approach aka database first is probably the most common approach. Basically, you use the relational paradigm to define objects… this has just one caveat: the result is not objects but tables. You create tables and represent them on code. This was my first approach and I ended with something like:

Good. Does your design look like this? If so, you’re in for a fun ride. Let me show you what I mean. Suppose you want to implement this design. It’ll probably look like:

class LoanApplication
{
   ...
   public string Status {get; private set;}
   public void StartReview()
   {
     if(Status !="Awaiting Review") throw new NotImplementedException();
     //do something here
    Status = "Reviewing";
   }

   public void SubmitDocs()
   {
     if(Status !="Docs Required") throw new NotImplementedException();
     //do something here
    Status = "Reviewing";
   }

   public void Withdraw()
   {
     if(Status !="Awaiting Review" && Status !="Reviewing" && Status !="Approved") 
           throw new NotImplementedException();
     //do something here
    Status = "Cancelled";
   }
 ...
}

You can see where this is going. The actions depend on the current state of the process. So with every new state, you will have to modify the guard clauses for the actions that you want to enable. But if you follow a data-centric approach, you probably won’t be bothered by this. You will assume that’s the only way. Luckily for me, the fact that I had to change different methods every time I introduced a new state, led me to further experimentation.

The behavior centric approach

As the name implies this approach focuses on object behavior. That means that the organizing principle is behavior rather than data. The result is rather radical:

So now you have objects that represent the states in the loan application process. The main benefit is that the code is simplified as only the actions allowed in the current state are advertised. See the difference? The behavior associated with each state is what drives the design. I loved this design. It was simpler. I no longer had to check the state before trying to send a message to the object. The only problem left was how to persist this?

Interaction between paradigms

So now, you’re probably wondering if you have to create many tables to store the data for each object. I have talked about having different models for an application before. This basically means that we can use a data-centric approach for the database design, which is basically the Relational paradigm. At the same time, we can use a behavior-centric approach to design the objects. This behavior-centric approach is at the heart of the Object-Oriented paradigm. Having different models interact with each other is just part of dealing with the impedance mismatch problem. The way I dealt with it was by using the repository pattern. Something like:

This way I had several objects being stored and loaded from a single table. The result was nice relational and object-oriented designs that we’re able to evolve independently.

Closing toughts

Probably the most important idea I learned from this experience is that a concept in the problem space can have several representations on the solution space.

Open your mind to this idea and try experimenting with it on your own project. As always, let me know your thoughts.