Software & Databases: You're All Missing The Point
SQL.  NoSQL.  Java.  Rails. Oracle.  MongoDB.  C++.  perl/python/ruby/scala/...  Etc., etc., etc.
Do you really think choice of query syntax, ACID compliance, and language are 
the reason why software design succeeds or fails?  It's not.
Software engineering and then ultimately system flexibility and performance 
suffers because we routinely and inappropriately allow databases and 
database APIs to nudge our designs to 
mix three things together that need to be kept very separate: data, key, and domain.
To begin, let's start with objects, not the database.  If you start a design in
terms of tables (or key/value or document collection; this isn't a SQL v. noSQL
issue) then you're already going down the wrong path.
Let's make a simple class:
    class MyClass {
        private String name;
        private int age;
    
        public MyClass() { // things.... }
        public MyClass(MyClass a) { // the copy constructor! }
    
        //  usual barrage of get/set, etc...
    }
- Is this class going to observe value semantics?
- Is this class immutable?
- What are the space and performance considerations for this class?
- What are the logical and physical dependencies for this class (get ready for the next
rant on open source version management....)
- How can this class adapt and change over time?
If we wanted stricter adherence to the philosophy of design-by-contract
then our simple class
would have a subtle difference in its initial documentation:
    class MyClass {
        // various internal state
        public MyClass() { // things.... }
        public MyClass(MyClass a) { // the copy constructor! }
        public String getName() { // }
        public void setName(String a) { // }
    }
So having defined a tidy MyClass, we can create instances of it, 
put it in Lists, etc.
    // perform operations on o
    MyClass o = new MyClass();
    o.setName("Bob");
    // Perhaps if modeled as immutable, no setters; data at construciton only:
    MyClass o2 = new MyClass("Bob", 26);
    // perform operations on o2
    //  Put them in containers:
    for(int i = 0; i < 8; i++) {
        list.add(new MyClass());
        set.add(new MyClass());
    }
    Map m = new HashMap();
    MyClass o = new MyClass();
    m.put("K1", o);
- Key "K1", from an information architecture perspective, has nothing to do with 
object o.  K1 is a key in domain m to find object o; that's all.   Under no 
circumstances can object o rely on the fact that string K1 is being used to index it
in domain m.
- Key "K1" is unique only in the domain of the HashMap.
This is fairly straightforward implementation and just about all programmers are familiar
with HashMaps and key/value entries.  
The Problem
Now let's see what typically happens when we switch the implementation of
domain from a HashMap
to a database.   We will use RDBMS semantics here but the issue is not isolated to 
RDBMS nor is it related to SQL:
    Statement px = conn.createStatement() ;
    ResultSet resultSet = px.executeQuery("select id, name, age from people");
    while(resultSet.next()) {
        MyClass o = mapResultSetToMyClass(resultSet);
	// etc...
        o.getName(); // OK
        o.getKey(); // ?!?   BAD!  What is key doing in MyClass?
    }
    resultSet.close();
    px.close();
    MyClass mapResultSetToMyClass(ResultSet r) {
        MyClass mc = new MyClass();
        String key = r.getString(1); // 1 is position of id in result
        mc.setKey(key);
        String name = r.getString(2); // 2 is position of name in result
        mc.setName(name);    
        //...
    }
    MyClass o = mapResultSetToMyClass();
    
    MyClass o2 = new MyClass(o);  
    o2.getKey();  // ...so now BOTH o and o2 have the same key?
Depurifying the object by embedding a key also means you cannot create and 
manipulate the object independently of the domain.  Technically, you can
but what does this mean:
    MyClass o2 = new MyClass();  
    o2.getKey();  // I guess the key has to be ... null?
    // and later...
    o2.setKey(someThing);  // ?  Huh?  
The Solution
The path to goodness is actually pretty simple but requires diligence in design 
and code factoring.  When reading and writing a database, one has to keep the objects
and keys separate but associated -- just like any other domain:
    Map m = new HashMap();
    while(resultSet.next()) {
        String key = resultSet.getString(1); // same as above
        MyClass o = mapNonKeyPartsOfResultSetToMyClass(resultSet);
        m.put(key, o);
    }
Sometimes, though, we can't use a HashMap, which means we also lose the component
that is acting as the collection for the key space.   Let's say we need to use
a List of data instead.   No problem:
    List list = new ArrayList();
    while(resultSet.next()) {
        String key = resultSet.getString(1); // same as above
        MyClass o = mapNonKeyPartsOfResultSetToMyClass(resultSet);
        // NO SHORTCUTS!  Use a Pair class to associate key and data and
        // store that in the list!
        MyPair<String,MyClass> p = new MyPair<String,MyClass>(key, o):
	list.add(p);
    }
    // Get everything from the persistor:
    while(resultSet.next()) {
        String key = resultSet.getString(1); // same as above
        MyClass o = mapNonKeyPartsOfResultSetToMyClass(resultSet);
        MyPair<String,MyClass> p = new MyPair<String,MyClass>(key, o):
	list.add(p);
    }
    // Now add in things from the GUI:
    while(grab things from GUI as a Map or JSON or XML) {
        MyClass o3 = convert things to a MyClass();
        MyPair<String,MyClass> p = new MyPair<String,MyClass>(null, o): // ah HA!
     	list.add(p);
    }
    analyze_everything(list);
There is a nice 
Pair class
available at commons.apache.org.
Ultimately, this is just another example of the important of factoring and separation
of concerns.  Objects need to be defined independently of the persistence and keying
frameworks that manage them.
Like this?  Dislike this?  Let me know
Site copyright © 2013-2025 Buzz Moschetti.  All rights reserved