Showing posts with label ORM. Show all posts
Showing posts with label ORM. Show all posts

2008-02-21

Why do we write software?

I feel I have to ask: Why do we write software?

I ask this because I think the why gets lost too easily. I've recently worked on a project that modeled a name and email address in JSON like this:

[
["PRXYY_298_NAFR","Bob",""],
["PRYXY_LARFN","Roberts",""],
["PRH_2810_EMA","bob@email.com","email"]
]

... why do that?

I suspect that this kind of tri-associative array allows for type information to be included in a JavaScript model. The problem is that the data thingy (because that sure ain't no object) is no longer human readable.

But what is worse is exposing this data model in an API to use as a service. This is basically screwing over the integration developer. I can see how you could have easily modeled the JSON object as:

{
PRXYY_298_NAFR: "Bob",
PRYXY_LARFN: "Roberts",
PRH_2810_EMA: "bob@email.com",
}

... and then had a server-side mapping map "PRH_2810_EMA" to "email" on the fly. Even better would have been to use human readable names like "email" and map those to values like "PRH_2810_EMA" if you needed to server side.

Why do it?

I suspect this makes the original system designer's job easier on some level. No thought was given to system integration or how to make data import/export easy. Instead we are working with something that was easy to code but hard to use.

And, that circles back around to "Why do we write software?"

It clearly isn't to make the programmer's life easier is it? The programmer should be trying to make their user's life easier. After all isn't that why a programmer even has a job?

I mean "programmer" from the perspective of the one writing the program or API... and user being the person who is going make use of this product later. That means as a programmer I'm the user of PHP, Python, Perl, Ruby, Java, Scala, Groovy, or other technology. It means that if I write Rails, Grails, Maven, CakePHP, JSF, or other framework I'm using a programming language and creating tools that other programmers will use themselves. The role of programmer and user can switch. I could write an API then have to use it later.

So you would think that being a user of programming tools themselves the designer of our tri-associative array would have some sympathy... empathy... care for... the user of their tool since they are so very similar people. But instead of thinking about why the tool exists and who is going to use the tool our programmer chose the path that would give them the easiest success.

I don't like that very much.

As programmers our job is to do the hard things so our users don't ever have to care. The simpler we can make very hard things seem the better we are doing. For example Google probably has one of the most powerful and complex search engines on the planet. It is clustered and redundant and complex and beyond many programmer's or user's comprehension. Yet to use it you type text in one box and hit one of two buttons.

The user experience is simplified to almost a ridiculous level.

The Macintosh is like this too in places. And, so are some places in Windows. And, so are some places in Linux. Each one of these systems has traded easy in one place for hard in another. For Macintosh... hardware is harder... for windows security is harder... for Linux desktop user experience is harder. In Macintosh what does work works well and without a manual... in Windows you never wonder "will it work with windows?"... in Linux you never wonder if it will scale well or stay secure.

Each has taken a specific user experience and simplified that particular "most important" case.

So I ask you: What is your most important case? Why are you writing this software? Why will anyone bother to pay you? What would cause them to not pay you?

Features should be designed and implemented by order of priority.

So maybe my tri-associative-array friend never saw integration as a priority. But, we should have. And that should have meant we didn't pay him and instead paid for someone who aligned with our priorities.

And that is my big thought for today: Ask why you are writing software and ask whether you should write it at all first.

2008-01-19

the vessel of ideas

Earlier I said:
The word is the vessel of ideas. The written word is the preservation of ideas. The computerized word is the execution of ideas.

Programming is about thinking. It is nothing else. Programs only execute the rules we believe to be true as we have written them down. What do we know? What do we believe?

Take the idea of person, parent, or child these are very basic ideas on the surface but surprisingly few people understand in context the ironic trap that the idea of an
Object then introduces. In the sense of a program...

An Object is a thing.

An Object may have things.

So it seems natural that a parent is a person and a child is a person. That makes sense. A parent has children. A child has parents. That all makes perfect sense. In the now.

We might say:

class Person {
def name
def birthday
def gender
}

... to say a person has a name, a birthday, a gender...

class Parent extends Person {
static hasMany = [children:Person]
}

... to say a parent has one or more children ...

class Child extends Person {
Person mother
Person father
}

... to say a child has one and only one mother and one and only one father... (in the real world we would probably fold Child attributes back into Person)

The terms I've used so far are more or less permanent. Once you become a parent you are never not one. Once you become someone's child that status is never removed. But sometimes there are statuses that are more flexible that aren't modeled correctly by using the "is a" idea. This becomes more apparent when dealing with a term such as employee.

A person is an employee as long as they are employed. You might be employed, unemployed, and re-employed multiple times with the same employer. So should "person is a parent" be modeled the same as "person is an employee" or is being an employee different?

Could it be that ...

class Employee extends Person {
static hasMany = [employers:Employer]
}

... such that we we would add and remove employers over time to an employee?

But that doesn't really get at the idea of being an employee. You are one from one date to another and you might be again. So really you would want to say:

class Employment {
Person employee
Employer employer
Date start
Date end
}

So that a person is an employee if a person has an employment that has a start before now and an end after now. So many other relationships turn out share this same kind of temporal basis. Sadly, marriage does too... some of the data I have to model deals with people who have children and get divorced. The parent-child relationship is unbreakable and begins on the date of birth of the child. However, marriages don't work that way and at least once we've had the problem of modeling a marriage, divorce, and re-marriage. The systems can't handle that idea because they weren't built to hold it. They are poor vessels for these ideas.

It stands to reason that the attribute of being something when it comes to people is not in fact the same as being something when it comes to objects. People are objects in every sense but the being is not the same. A person has attached to them attributes ... like tags more than attributes ...which makes them one thing or another.

The relationship itself is an entity or an object.

The person is an employee by the virtue of possession of a relationship. Once in existence the relationship of employment carries its own being. It has its own data, attributes, and validity independent of the employee or employer.

Likewise the idea of a relationship between people carries similar intrinsic existence independent of the people. For example parent. The person as an object is not really modified but rather a new external relationship is created. In this case a child is created that then points back to the parent.

That means the "class Parent" is actually redundant. A person is a parent if any other people point back to them as a mother or father. If you wanted you could add a deceased date that would remain unused inside the person class and leave it undefined during life and set after death. But that's getting into the morbid.

Consider the employee again. This is a person object who has a record of employment. The employment is distinct and independent of the person. The relationship itself can have attributes without involving the people.

This is all pretty basic but if you continue to expand these ideas shifting your attitude about what an object is you find that mapping objects and relationships starts to get easier. You get to a model that has fewer impedance mis-matches with a relational database for example.

If the vessel will not hold your ideas change the vessel not your ideas.

2007-10-22

Issues of Concurrency

I have a problem. I have two databases. One old one and one new one. The new one is fully normalized and the old one is not. Both databases drive their own front ends. One is a Perl front end the other is a Spring Java front end. The data between both interfaces has to stay in sync. Our DBA has elected to use triggers to keep the databases in sync.

Hibernate uses its own Version column to keep up with which version got committed to the database and when it happened. There are articles dealing with JPA, Hibernate, and Concurrency... my problem is a little different.

Here's my example of the problem.

Let's say you have a web application using an ORM engine like Hibernate. There is Entity version 1 in the database. User Alice checks out version 1. User Bob checks out version one.

Alice edits her copy of version 1 and sends it back. The ORM engine checks the object version against the database version and they match and on commit of Alice's changes the database version number is set to 2.

Bob makes changes to his copy of version 1 of the Entity and checks his version in. The ORM checks the database version and sees version 2 in the database. Version 1 is old and Bob gets an error reporting mid-air collision.

Now lets say you introduce a second database. This second database uses different Relational mappings but is itself an OLTP. Let's pretend both databases have exact copies of the data initially and an update trigger mechanism keeps the two OLTP's in sync.

If Alice checks out version 1 from the first database and the first interface and Bob checks out version 1 from the second database and the second interface. When Alice makes her change and creates first version 2 (2' or two prime) and Bob commits his changes to create second version 2 or (2'' or two double prime) the trigger from the first database to update the second will fire simultaneously as the trigger from the second.

In other words Alice's OLTP is unaware of changes made on Bob's OLTP and both changes are in route to the other's.

How should the two databases proceed?

Should last saved win? Should both databases error and roll back? Should the systems attempt to "merge" changes? Should one system be elected the "authority" for this data electing that Alice's interface and OLTP are always the right ones?