11. Implementing associations: hasOne, hasMany

Defining associations. Associations / relationships are sugar on top of the basic data source implementation. The idea is that you can predefine the associations between models, for example, that a post hasMany comments. This might be described as:

function Post(args) {
  Model.apply(this, args);
  this.definition = {
    tags: Tags,
    comments: Comments
    };
}

We can fetch stuff manually without assocation support. For example, assume that posts.comment_ids is an array of ids:

db.tag(post.comment_ids, function(tags) {
  tags.forEach(function(tag)) {
    // ...
  });
});

But given several levels of nesting (post has comment has author), this gets old pretty fast.

It’s the age-old problem of dealing with callbacks - which turns out to be pretty trivial once you add a couple of control flow patterns to your repertoire. The fundamental ones are “series”, “parallel” and “parallel but with limited concurrency”. If you are unfamiliar with those, go read Chapter 7 - Control Flow of my previous book.

Don’t pretend to have a blocking API. Some frameworks have taken the approach that they pretend to provide a blocking API by returning a placeholder object. For example:

var comments = post.get('comments');
// we do not have the data for comments,
// but we'll return a placeholder object for it

This is a very, very leaky abstraction. It just introduces complexity without really solving the issue, which is that you have to wait for the database to return results. I’d much rather allow the user to set a callback that gets called when the data has arrived; with a little bit of control flow you can easily ensure that the data is loaded - or build a higher level mechanism like we will be doing.

APIs that appear not to incur the cost of IO but actually do are the leakiest of abstractions (Mikeal Rogers). I’d much rather opt for the simple callback, since that allows me to explictly say that a piece of code should run only when the required data has arrived.