We're busy creating experimental implementations of WebSimpleDB to both 
understand what it takes to implement and also to see what the developer 
experience looks like. 

As we started to write "application code" against the API (particularly the 
async one) the first thing that popped is the fact that you need two levels of 
nested callbacks for everything. While the current factoring of the API makes 
sense on the design board, it's kind of noisy in app code. For example:

// assume you already have a database opened in dbReq 
var html = "<ul>"; 
var storeReq = new ObjectStoreRequest(dbReq.database);
storeReq.success = function() {
    var cursorReq = new CursorRequest(storeReq.store);
    cursorReq.callback = function(key, cursor, value) {
        html += "<li>" + value.Name + "</li>";
    }
    cursorReq.onsuccess = function(r) {
        document.getElementById("output").innerHTML = html + "</ul>";
    }
    cursorReq.open();
}
storeReq.open();

One option that we would like to explore is to "flatten" the API, so most 
common methods are straight in the database class. This trades off some of the 
factoring in favor of usability for common cases using the async API.

The change would span a couple of aspects:

1. Move operations from object store interface and the index interface into the 
Database interface.

Accessing indexes and stores through specialized objects is problematic for the 
following reasons:
- It's always the case that we need to consider when objects are invalidated 
because something changes from underneath them, for example a schema change. So 
for example, if there is an explicit store object, then when the store is 
dropped we need to consider what is valid/invalid and what its failure points 
and modes are. By not having a standalone store object, we significantly reduce 
the "gotchas" to consider.
- From a usability perspective, it's simpler to work with a store in a single 
step, rather than having to open it first and then work with it (see patterns 
below with a single request and one DBRequest object).
- With no "two-step" access pattern, the API has one less level of 
asynchronicity, as effectively the table lookup + operation are atomic within 
the store. This also consolidates all operations with an async variant in a 
single interface (the Database), which is a great simplification for 
discoverability.

var html = "<ul>";
var request = asyncDb.forEachStoreObject("contacts", function(row) {
   html += "<li>" + row.Name + "</li>";
});
request.onsuccess = function(r) {
  document.getElementById("output").innerHTML = html + "</ul>"; }

In moving the operations, it's probably best to rename them to something more 
descriptive, so we can have for example 'getFromStore(storeName, key)' and 
'getFromIndex(storeName, indexName, key)'. This also helps in that 'delete' 
won't collide with the Javascript keyword.

Note that the store and index interfaces are still around to provide metadata, 
but at this point they behave as simple read-only snapshots.

2. Generalize the use of DBRequest, add a 'result' member to it and have all 
asynchronous operations be initiated from a DatabaseAsync interface.

As a result of the previous changes, all operations that have an async 
counterpart should now exist on the DatabaseAsync interface. Rather than having 
multiple types of requests depending on the target object, it is possible to 
have operations on a DatabaseAsync interface that provide a uniform invocation 
and handling programming pattern.

This gives a nice pattern for understanding how a sync API maps to an async API.

So for example:

var record = db.getFromStore("store", key); // use record...

Becomes:

var request = asyncDb.getFromStore("store", key); request.onsuccess = 
function(req) {
  var record = req.result;
  // use record...
};

We could include more data in DBRequest or DBRequest.result as needed if in 
some cases a method produces more than just a simple result. Further 
specializatons of DBRequest (subtypes) are still possible in the future if we 
need to introduce special cases for specific operations.

Similarly, we would have something like asyncDb.forEachStoreObject() that 
queues a task to call a callback for each element in a store/index, potentially 
within a range if specified. The pattern scales well to all the other APIs 
present in db/store/index today.

If this seems like a good idea to folks, we'd be happy to write up a more 
complete version that articulates the tweaks across all the WebSimpleDB APIs to 
make this happen.

Regards,
-pablo


Reply via email to