diff --git CHANGELOG.txt CHANGELOG.txt
index 7ad7487..7cab7df 100644
--- CHANGELOG.txt
+++ CHANGELOG.txt
@@ -2,6 +2,13 @@
 
 versioncontrol 6.x-2.x
 ======================
+- Add a 'data' field to the labels tabel, so that backends can more easily
+  plug in additional data on their labels.
+- Introduced 'single backend mode' $conf flag, a global flag indicating
+  whether or not multiple backends are enabled. Useful because single backend
+  mode allows streamlined querying and operations.
+- Created a repository multiple loader function, fashioned after similar such
+  entity multiloaders in D7.
 - Introduced dependency on autoload, both for future dbtng dependency and to
   assist with managing all the classes in the vcsapi system itself.
 - Major rewrite of the 1.x branch in OOP.
diff --git includes/VersioncontrolBackend.php includes/VersioncontrolBackend.php
index dad8dee..338911d 100644
--- includes/VersioncontrolBackend.php
+++ includes/VersioncontrolBackend.php
@@ -10,11 +10,11 @@
  *
  * @abstract
  */
-abstract class VersioncontrolBackend implements ArrayAccess {
+abstract class VersioncontrolBackend {
   /**
    * The user-visible name of the VCS.
    *
-   * @var    string
+   * @var string
    */
   public $name;
 
@@ -22,7 +22,7 @@ abstract class VersioncontrolBackend implements ArrayAccess {
    * A short description of the backend, if possible not longer than
    * one or two sentences.
    *
-   * @var    string
+   * @var string
    */
   public $description;
 
@@ -33,26 +33,49 @@ abstract class VersioncontrolBackend implements ArrayAccess {
    * of VERSIONCONTROL_CAPABILITY_* values. If no additional capabilities
    * are supported by the backend, this array will be empty.
    *
-   * @var    array
+   * @var array
    */
   public $capabilities;
 
   /**
-   * classes which this backend overwrite
+   * Classes which this backend will instantiate when acting as a factory.
    */
-  public $classes;
+  public $classes = array();
 
-  //ArrayAccess interface implementation
-  public function offsetExists($offset) {
-    return isset($this->$offset);
+  public function __construct() {
+    // Add defaults to $this->classes
+    // FIXME currently all these classes are abstract, so this won't work. Decide
+    // if this should be removed, or if they should be made concrete classes
+    $this->classes += array(
+      'repo'      => 'VersioncontrolRepository',
+      'account'   => 'VersioncontrolAccount',
+      'operation' => 'VersioncontrolOperation',
+      'item'      => 'VersioncontrolItem',
+      'branch'    => 'VersioncontrolBranch',
+      'tag'       => 'VersioncontrolTag',
+    );
   }
-  public function offsetGet($offset) {
-    return $this->$offset;
-  }
-  public function offsetSet($offset, $value) {
-    $this->$offset = $value;
-  }
-  public function offsetUnset($offset) {
-    unset($this->$offset);
+
+  public function buildObject($type, $data) {
+    $class = $this->classes[$type];
+    if (!is_subclass_of($class, 'VersioncontrolEntity')) {
+      throw new Exception('Invalid Versioncontrol entity class specified; all entity classes should have VersioncontrolEntity as a parent', $class);
+    }
+    $obj = new $this->classes[$type]($this);
+    $obj->build($data);
+    return $obj;
   }
+
+  /**
+   * Augment a select query with options specific to this backend.
+   *
+   * This method is fired by entity controllers whenever the backend type is
+   * known prior to the issuing of the query.
+   *
+   * @param SelectQuery $query
+   *   The query object being built.
+   * @param string $entity_type
+   *   The type of entity being loaded.
+   */
+  public function augmentEntitySelectQuery($query, $entity_type) {}
 }
diff --git includes/VersioncontrolBranch.php includes/VersioncontrolBranch.php
index 707ec14..98ada86 100644
--- includes/VersioncontrolBranch.php
+++ includes/VersioncontrolBranch.php
@@ -6,15 +6,86 @@
  */
 
 /**
- * Represents a branch of code
+ * Represents a repository branch.
  */
-class VersioncontrolBranch extends VersioncontrolLabel {
-  // Operations
+class VersioncontrolBranch extends VersioncontrolEntity {
   /**
-   * Constructor
+   * The tag identifier (a simple integer), used for unique identification of
+   * this tag in the database.
+   *
+   * @var int
    */
-  public function __construct($name, $action, $label_id = NULL, $repository = NULL) {
-    parent::__construct(VERSIONCONTROL_LABEL_BRANCH, $name, $action, $label_id, $repository);
+  public $label_id;
+
+  /**
+   * The tag name.
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * Indicates this is a branch; for db interaction only.
+   *
+   * @var int
+   */
+  public $type = VERSIONCONTROL_LABEL_BRANCH;
+
+  /**
+   * @name VCS actions
+   * for a single item (file or directory) in a commit, or for branches and tags.
+   * either VERSIONCONTROL_ACTION_{ADDED,MODIFIED,MOVED,COPIED,MERGED,DELETED,
+   * REPLACED,OTHER}
+   *
+   * @var array
+   */
+  public $action;
+
+  /**
+   * The database id of the repository with which this branch is associated.
+   * @var int
+   */
+  public $repo_id;
+
+  /**
+   * Insert a tag entry into the {versioncontrol_labels} table, or retrieve the
+   * same one that's already there.
+   *
+   * The object is enhanced with the newly added property 'label_id' specifying
+   * the database identifier for that label. There may be labels with a similar
+   * 'name' but different 'type' properties, those are considered to be
+   * different and will both go into the database side by side.
+   *
+   * @deprecated FIXME remove this approach, it leads to inefficient single-loading.
+   */
+  public function ensure() {
+    if (!empty($this->label_id)) { // already in the database
+      return;
+    }
+    $result = db_result(db_query("SELECT label_id FROM {versioncontrol_labels} WHERE repo_id = %d AND name = '%s' AND type = %d",
+      $this->repository->repo_id, $this->name, $this->type));
+    if ($result) {
+      $this->label_id = $result;
+    }
+    else {
+      // The item doesn't yet exist in the database, so create it.
+      $this->insert();
+    }
   }
 
+  /**
+   * Insert label to db
+   */
+  protected function insert() {
+    if (isset($this->label_id)) {
+      // The label already exists in the database, update the record.
+      drupal_write_record('versioncontrol_labels', $this, 'label_id');
+    }
+    else {
+      // The label does not yet exist, create it.
+      // drupal_write_record() also assigns the new id to $this->label_id.
+      drupal_write_record('versioncontrol_labels', $this);
+    }
+    unset($this->repo_id);
+  }
 }
diff --git includes/VersioncontrolLabel.php includes/VersioncontrolLabel.php
index fdda96b..fe9b9af 100644
--- includes/VersioncontrolLabel.php
+++ includes/VersioncontrolLabel.php
@@ -26,9 +26,9 @@ abstract class VersioncontrolLabel implements ArrayAccess {
   public $name;
 
   /**
-   * The repository where the label is located.
+   * The id of the repository with which this label is associated.
    *
-   * @var    VersioncontrolRepository
+   * @var int
    */
   public $repository;
 
@@ -55,17 +55,6 @@ abstract class VersioncontrolLabel implements ArrayAccess {
   // Operations
 
   /**
-   * Constructor
-   */
-  public function __construct($type, $name, $action, $label_id = NULL, $repository = NULL) {
-    $this->type = $type;
-    $this->name = $name;
-    $this->action = $action;
-    $this->label_id = $label_id;
-    $this->repository = $repository;
-  }
-
-  /**
    * Insert a label entry into the {versioncontrol_labels} table,
    * or retrieve the same one that's already there.
    *
@@ -78,24 +67,21 @@ abstract class VersioncontrolLabel implements ArrayAccess {
     if (!empty($this->label_id)) { // already in the database
       return;
     }
-    $result = db_query(
-      "SELECT label_id, repo_id, name, type FROM {versioncontrol_labels}
-    WHERE repo_id = %d AND name = '%s' AND type = %d",
-    $this->repository->repo_id, $this->name, $this->type
-  );
-    while ($row = db_fetch_object($result)) {
-      // Replace / fill in properties that were not in the WHERE condition.
-      $this->label_id = $row->label_id;
-      return;
+    $result = db_result(db_query("SELECT label_id FROM {versioncontrol_labels} WHERE repo_id = %d AND name = '%s' AND type = %d",
+      $this->repository->repo_id, $this->name, $this->type));
+    if ($result) {
+      $this->label_id = $result;
+    }
+    else {
+      // The item doesn't yet exist in the database, so create it.
+      $this->insert();
     }
-    // The item doesn't yet exist in the database, so create it.
-    $this->insert();
   }
 
   /**
    * Insert label to db
    */
-  private function insert() {
+  protected function insert() {
     $this->repo_id = $this->repository->repo_id; // for drupal_write_record() only
 
     if (isset($this->label_id)) {
@@ -104,7 +90,7 @@ abstract class VersioncontrolLabel implements ArrayAccess {
     }
     else {
       // The label does not yet exist, create it.
-      // drupal_write_record() also adds the 'label_id' to the $label array.
+      // drupal_write_record() also assigns the new id to $this->label_id.
       drupal_write_record('versioncontrol_labels', $this);
     }
     unset($this->repo_id);
diff --git includes/VersioncontrolRepository.php includes/VersioncontrolRepository.php
index ba3b0cb..7ae9afa 100644
--- includes/VersioncontrolRepository.php
+++ includes/VersioncontrolRepository.php
@@ -8,7 +8,7 @@
 /**
  * Contain fundamental information about the repository.
  */
-abstract class VersioncontrolRepository implements ArrayAccess {
+abstract class VersioncontrolRepository extends VersioncontrolEntity implements ArrayAccess {
   // Attributes
   /**
    * db identifier
@@ -51,8 +51,6 @@ abstract class VersioncontrolRepository implements ArrayAccess {
    */
   public $data = array();
 
-  protected $built = FALSE;
-
   // Associations
   /**
    * The backend associated with this repository
@@ -61,39 +59,14 @@ abstract class VersioncontrolRepository implements ArrayAccess {
    */
   public $backend;
 
-  // Operations
   /**
-   * Constructor
+   * An array of VersioncontrolEntityController objects used to spawn more
+   * entities from this repository, if needed. These objects are lazy-
+   * instanciated to avoid unnecessary object creation.
+   *
+   * @var array
    */
-  public function __construct($repo_id, $args = array(), $buildSelf = TRUE) {
-    $this->repo_id = $repo_id;
-    if ($buildSelf) {
-      $this->buildSelf();
-    }
-    else {
-      $this->build($args);
-    }
-    $this->built = TRUE;
-  }
-
-  protected function buildSelf() {
-    $data = db_fetch_array(db_query("
-      SELECT
-      vr.name, vr.root, vr.authorization_method, vr.data
-      FROM {versioncontrol_repositories} vr
-      WHERE vr.repo_id = %d",
-      $this->repo_id));
-    $this->build($data);
-  }
-
-  protected function build($args = array()) {
-    foreach ($args as $prop => $value) {
-      $this->$prop = $value;
-    }
-    if (is_string($this->data)) {
-      $this->data = unserialize($this->data);
-    }
-  }
+  protected $controllers = array();
 
   /**
    * Title callback for repository arrays.
@@ -103,90 +76,57 @@ abstract class VersioncontrolRepository implements ArrayAccess {
   }
 
   /**
-   * Retrieve known branches and/or tags in a repository as a set of label arrays.
+   * Load known branches in a repository from the database as an array of
+   * VersioncontrolBranch-descended objects.
    *
-   * @param $constraints
-   *   An optional array of constraints. If no constraints are given, all known
-   *   labels for a repository will be returned. Possible array elements are:
-   *
-   *   - 'label_ids': An array of label ids. If given, only labels with one of
-   *        these identifiers will be returned.
-   *   - 'type': Either VERSIONCONTROL_LABEL_BRANCH or
-   *        VERSIONCONTROL_LABEL_TAG. If given, only labels of this type
-   *        will be returned.
-   *   - 'names': An array of label names to search for. If given, only labels
-   *        matching one of these names will be returned. Matching is done with
-   *        SQL's LIKE operator, which means you can use the percentage sign
-   *        as wildcard.
+   * @param array $ids
+   *   An array of branch ids. If given, only branches matching these ids will
+   *   be returned.
+   * @param array $conditions
+   *   An associative array of additional conditions. These will be passed to
+   *   the entity controller and composed into the query. The array should be
+   *   key/value pairs with the field name as key, and desired field value as
+   *   value. The value may also be an array, in which case the IN operator is
+   *   used. For more complex requirements, FIXME finish!
+   *   @see VersioncontrolEntityController::buildQuery() .
    *
    * @return
-   *   An array of VersioncontrolLabel objects
-   *   If not a single known label in the given repository matches these
-   *   constraints, an empty array is returned.
-   */
-  public function getLabels($constraints = array()) {
-    $and_constraints = array('repo_id = %d');
-    $params = array($this->repo_id);
-
-    // Filter by label id.
-    if (isset($constraints['label_ids'])) {
-      if (empty($constraints['label_ids'])) {
-        return array();
-      }
-      $or_constraints = array();
-      foreach ($constraints['label_ids'] as $label_id) {
-        $or_constraints[] = 'label_id = %d';
-        $params[] = $label_id;
-      }
-      $and_constraints[] = '('. implode(' OR ', $or_constraints) .')';
-    }
-
-    // Filter by label name.
-    if (isset($constraints['names'])) {
-      if (empty($constraints['names'])) {
-        return array();
-      }
-      $or_constraints = array();
-      foreach ($constraints['names'] as $name) {
-        $or_constraints[] = "name LIKE '%s'";
-        // Escape the percentage sign in order to get it to appear as '%' in the
-        // actual query, as db_query() uses the single '%' also for replacements
-        // like '%d' and '%s'.
-        $params[] = str_replace('%', '%%', $name);
-      }
-      $and_constraints[] = '('. implode(' OR ', $or_constraints) .')';
-    }
-
-    // Filter by type.
-    if (isset($constraints['type'])) {
-      // There are only two types of labels (branches and tags), so a list of
-      // types doesn't make a lot of sense for this constraint. So, this one is
-      // simpler than the other ones.
-      $and_constraints[] = 'type = %d';
-      $params[] = $constraints['type'];
+   *   An associative array of label objects, keyed on their
+   */
+  public function loadBranches($ids = array(), $conditions = array()) {
+    if (!isset($this->controllers['branch'])) {
+      $this->controllers['branch'] = new VersioncontrolBranchController();
+      $this->controllers['branch']->setBackend($this->backend);
     }
+    $conditions['repo_id'] = $this->repo_id;
+    return $this->controllers['branch']->load($ids, $conditions);
+  }
 
-    // All the constraints have been gathered, assemble them to a WHERE clause.
-    $and_constraints = implode(' AND ', $and_constraints);
-
-    // Execute the query.
-    $result = db_query('SELECT label_id, name, type FROM {versioncontrol_labels}
-                        WHERE '. $and_constraints .'
-                        ORDER BY label_id', $params);
-
-    // Assemble the return value.
-    $labels = array();
-    while ($label = db_fetch_array($result)) {
-      switch ($label['type']) {
-      case VERSIONCONTROL_LABEL_BRANCH:
-        $labels[] = new VersioncontrolBranch($label['name'], NULL, $label['label_id'], $this);
-        break;
-      case VERSIONCONTROL_LABEL_TAG:
-        $labels[] = new VersioncontrolTag($label['name'], NULL, $label['label_id'], $this);
-        break;
-      }
+  /**
+   * Load known tags in a repository from the database as an array of
+   * VersioncontrolTag-descended objects.
+   *
+   * @param array $ids
+   *   An array of tag ids. If given, only tags matching these ids will be
+   *   returned.
+   * @param array $conditions
+   *   An associative array of additional conditions. These will be passed to
+   *   the entity controller and composed into the query. The array should be
+   *   key/value pairs with the field name as key, and desired field value as
+   *   value. The value may also be an array, in which case the IN operator is
+   *   used. For more complex requirements, FIXME finish!
+   *   @see VersioncontrolEntityController::buildQuery() .
+   *
+   * @return
+   *   An associative array of label objects, keyed on their
+   */
+  public function loadTags($ids = array(), $conditions = array()) {
+    if (!isset($this->controllers['tag'])) {
+      $this->controllers['tag'] = new VersioncontrolTagController();
+      $this->controllers['tag']->setBackend($this->backend);
     }
-    return $labels;
+    $conditions['repo_id'] = $this->repo_id;
+    return $this->controllers['tag']->load($ids, $conditions);
   }
 
   /**
@@ -217,13 +157,6 @@ abstract class VersioncontrolRepository implements ArrayAccess {
   }
 
   /**
-   * Let child backend repo classes add information that _is not_ in
-   * VersioncontrolRepository::data
-   */
-  public function _getRepository() {
-  }
-
-  /**
    * Update a repository in the database, and call the necessary hooks.
    * The 'repo_id' and 'vcs' properties of the repository object must stay
    * the same as the ones given on repository creation,
diff --git includes/VersioncontrolTag.php includes/VersioncontrolTag.php
index cab9a11..140ffa3 100644
--- includes/VersioncontrolTag.php
+++ includes/VersioncontrolTag.php
@@ -6,16 +6,86 @@
  */
 
 /**
- * Represents a tag of code(not changing state)
+ * Represents a tag of code (not changing state)
  */
-class VersioncontrolTag extends VersioncontrolLabel {
+class VersioncontrolTag extends VersioncontrolEntity {
+  /**
+   * The tag identifier (a simple integer), used for unique identification of
+   * this tag in the database.
+   *
+   * @var int
+   */
+  public $label_id;
+
+  /**
+   * The tag name.
+   *
+   * @var string
+   */
+  public $name;
 
-  // Operations
   /**
-   * Constructor
+   * Indicates this is a tag; for db interaction only.
+   *
+   * @var int
    */
-  public function __construct($name, $action, $label_id = NULL, $repository = NULL) {
-    parent::__construct(VERSIONCONTROL_LABEL_TAG, $name, $action, $label_id, $repository);
+  public $type = VERSIONCONTROL_LABEL_TAG;
+
+  /**
+   * @name VCS actions
+   * for a single item (file or directory) in a commit, or for branches and tags.
+   * either VERSIONCONTROL_ACTION_{ADDED,MODIFIED,MOVED,COPIED,MERGED,DELETED,
+   * REPLACED,OTHER}
+   *
+   * @var array
+   */
+  public $action;
+
+  /**
+   * The database id of the repository with which this tag is associated.
+   * @var int
+   */
+  public $repo_id;
+
+  /**
+   * Insert a tag entry into the {versioncontrol_labels} table, or retrieve the
+   * same one that's already there.
+   *
+   * The object is enhanced with the newly added property 'label_id' specifying
+   * the database identifier for that label. There may be labels with a similar
+   * 'name' but different 'type' properties, those are considered to be
+   * different and will both go into the database side by side.
+   *
+   * @deprecated FIXME remove this approach, it leads to inefficient single-loading.
+   */
+  public function ensure() {
+    if (!empty($this->label_id)) { // already in the database
+      return;
+    }
+    $result = db_result(db_query("SELECT label_id FROM {versioncontrol_labels} WHERE repo_id = %d AND name = '%s' AND type = %d",
+      $this->repository->repo_id, $this->name, $this->type));
+    if ($result) {
+      $this->label_id = $result;
+    }
+    else {
+      // The item doesn't yet exist in the database, so create it.
+      $this->insert();
+    }
   }
 
+  /**
+   * Insert label to db
+   */
+  protected function insert() {
+    if (isset($this->label_id)) {
+      // The label already exists in the database, update the record.
+      drupal_write_record('versioncontrol_labels', $this, 'label_id');
+    }
+    else {
+      // The label does not yet exist, create it.
+      // drupal_write_record() also assigns the new id to $this->label_id.
+      drupal_write_record('versioncontrol_labels', $this);
+    }
+    unset($this->repo_id);
+  }
 }
diff --git includes/controllers.inc includes/controllers.inc
new file mode 100644
index 0000000..e4d8e75
--- /dev/null
+++ includes/controllers.inc
@@ -0,0 +1,342 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Controller/loader classes. Modelled on the Drupal 7 entity system.
+ */
+
+abstract class VersioncontrolEntityController {
+  protected $entityType;
+  protected $entityCache = array();
+  protected $baseTable;
+  protected $idKey;
+  protected $cache = TRUE;
+  protected $backends = array();
+
+  /**
+   * If set, contains an instance of a VersioncontrolBackend object; this object
+   * provides meta-information, as well as acting as a factory that takes data
+   * retrieved by this controller and instanciating entities.
+   *
+   * @var VersioncontrolBackend
+   */
+  protected $backend;
+
+  /**
+   * A mapping of shortened strings used as keys to query building methods they
+   * should call.
+   *
+   * @var array
+   */
+  protected $typeMap = array(
+    'repo'      => 'Repository',
+    'account'   => 'Account',
+    'operation' => 'Operation',
+    'item'      => 'Item',
+    'branch'    => 'Branch',
+    'tag'       => 'Tag',
+  );
+
+  public function __construct() {
+    $backends = versioncontrol_get_backends();
+    if (variable_get('versioncontrol_single_backend_mode', FALSE)) {
+      $this->backend = reset($backends);
+    }
+    else {
+      $this->backends = $backends;
+    }
+  }
+
+  /**
+   * Indicate that this controller can safely restrict itself to a single
+   * backend type. This results in some logic & query optimization.
+   *
+   * @param VersioncontrolBackend $backend
+   */
+  public function setBackend(VersioncontrolBackend $backend) {
+    $this->backend = $backend;
+  }
+
+  public function resetBackend() {
+    $this->backend = NULL;
+  }
+
+  public function resetCache() {
+    $this->entityCache = array();
+  }
+
+  public function load($ids = array(), $conditions = array()) {
+    $entities = array();
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed. The $ids array is reduced as items are loaded from cache,
+    // and we need to know if it's empty for this reason to avoid querying the
+    // database when all requested entities are loaded from cache.
+    $passed_ids = !empty($ids) ? array_flip($ids) : FALSE;
+    // Try to load entities from the static cache.
+    if ($this->cache) {
+      $entities += $this->cacheGet($ids, $conditions);
+      // If any entities were loaded, remove them from the ids still to load.
+      if ($passed_ids) {
+        $ids = array_keys(array_diff_key($passed_ids, $entities));
+      }
+    }
+
+    // Load any remaining entities from the database. This is the case if $ids
+    // is set to FALSE (so we load all entities), if there are any ids left to
+    // load, if loading a revision, or if $conditions was passed without $ids.
+    if ($ids === FALSE || $ids || ($conditions && !$passed_ids)) {
+      // Build the query.
+      $query = $this->buildQuery($ids, $conditions);
+      $queried_entities = $query
+        ->execute()
+        ->fetchAllAssoc($this->idKey);
+    }
+
+    if (!empty($queried_entities)) {
+      $built_entities = $this->buildEntities($queried_entities);
+      $entities += $built_entities;
+    }
+
+    if ($this->cache) {
+      // Add entities to the cache.
+      if (!empty($built_entities)) {
+        $this->cacheSet($built_entities);
+      }
+    }
+
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->{$this->idKey}] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+
+    return $entities;
+  }
+
+  /**
+   * Build the query to load the entity.
+   *
+   * This has full revision support. For entities requiring special queries,
+   * the class can be extended, and the default query can be constructed by
+   * calling parent::buildQuery(). This is usually necessary when the object
+   * being loaded needs to be augmented with additional data from another
+   * table, such as loading node type into comments or vocabulary machine name
+   * into terms, however it can also support $conditions on different tables.
+   * See CommentController::buildQuery() or TaxonomyTermController::buildQuery()
+   * for examples.
+   *
+   * @return SelectQuery
+   *   A SelectQuery object for loading the entity.
+   */
+  protected function buildQuery($ids, $conditions = array()) {
+    $query = db_select($this->baseTable, 'base');
+
+    $query->addTag($this->entityType . '_load_multiple');
+
+    // Add fields from the {entity} table.
+    $entity_fields = drupal_schema_fields_sql($this->baseTable);
+
+    $query->fields('base', $entity_fields);
+
+    if ($ids) {
+      $query->condition("base.{$this->idKey}", $ids, 'IN');
+    }
+    if ($conditions) {
+      foreach ($conditions as $field => $value) {
+        // If a condition value uses this special structure, we know the
+        // requestor wants to do a complex condition with operator control.
+        if (is_array($value) && isset($value['values']) && isset($value['operator'])) {
+          $query->condition('base.' . $field, $value['values'], $value['operator']);
+        }
+        // Otherwise, we just pass the value straight in.
+        else {
+          $query->condition('base.' . $field, $value);
+        }
+      }
+    }
+    if ($this->backend instanceof VersioncontrolBackend) {
+      // Allow the current backend to augment the query as needed.
+      $this->backend->augmentEntitySelectQuery($query, $this->entityType);
+    }
+    else {
+      $this->queryAlterGetBackendType($query);
+    }
+    return $query;
+  }
+
+  protected function queryAlterGetBackendType($query) {
+    if (!isset($this->backend)) {
+      // Add a join to the repo table so we know which backend to use.
+      $query->join('versioncontrol_repositories', 'vcr', "vcr.repo_id = base.repo_id");
+      $query->addField('vcr', 'vcs');
+    }
+  }
+
+  /**
+   * Transform the queried data into the appropriate object types.
+   *
+   * Empty here because each entity type needs to specify their process.
+   *
+   * @param array $queried_entities
+   */
+  protected function buildEntities(&$queried_entities) {
+    $built = array();
+    foreach ($queried_entities as $id => $entity) {
+      if (isset($this->backend)) {
+        $built[$id] = $this->backend->buildObject($this->entityType, $entity);
+      }
+      else {
+        $built[$id] = $this->backends[$entity->vcs]->buildObject($this->entityType, $entity);
+      }
+    }
+    return $built;
+  }
+
+  /**
+   * Get entities from the static cache.
+   *
+   * @param $ids
+   *   If not empty, return entities that match these IDs.
+   * @param $conditions
+   *   If set, return entities that match all of these conditions.
+   */
+  protected function cacheGet($ids, $conditions = array()) {
+    $entities = array();
+    // Load any available entities from the internal cache.
+    if (!empty($this->entityCache)) {
+      if ($ids) {
+        $entities += array_intersect_key($this->entityCache, array_flip($ids));
+      }
+      // If loading entities only by conditions, fetch all available entities
+      // from the cache. Entities which don't match are removed later.
+      elseif ($conditions) {
+        $entities = $this->entityCache;
+      }
+    }
+
+    // Exclude any entities loaded from cache if they don't match $conditions.
+    // This ensures the same behavior whether loading from memory or database.
+    if ($conditions) {
+      foreach ($entities as $entity) {
+        // FIXME this probably needs to be more complex for our purposes
+        $entity_values = (array) $entity;
+        if (array_diff_assoc($conditions, $entity_values)) {
+          unset($entities[$entity->{$this->idKey}]);
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * Store entities in the static entity cache.
+   */
+  protected function cacheSet($entities) {
+    $this->entityCache += $entities;
+  }
+}
+
+class VersioncontrolRepositoryController extends VersioncontrolEntityController {
+  protected $entityType = 'repo';
+  protected $baseTable = 'versioncontrol_repositories';
+  protected $idKey = 'repo_id';
+}
+
+class VersioncontrolAccountController extends VersioncontrolEntityController {
+  protected $entityType = 'account';
+  protected $baseTable = 'versioncontrol_accounts';
+  protected $idKey = 'repo_id'; // FIXME woah fugly. A lot needs to be reworked b/c it's got two primary keys
+}
+
+class VersioncontrolBranchController extends VersioncontrolEntityController {
+  protected $entityType = 'label';
+  protected $baseTable = 'versioncontrol_labels';
+  protected $idKey = 'label_id';
+
+  protected function buildQuery($ids, $conditions = array()) {
+    $query = parent::buildQuery($ids, $conditions);
+    $query->condition('base', 'type', VERSIONCONTROL_LABEL_BRANCH);
+    return $query;
+  }
+}
+
+class VersioncontrolTagController extends VersioncontrolEntityController {
+  protected $entityType = 'label';
+  protected $baseTable = 'versioncontrol_labels';
+  protected $idKey = 'label_id';
+
+  protected function buildQuery($ids, $conditions = array()) {
+    $query = parent::buildQuery($ids, $conditions);
+    $query->condition('base', 'type', VERSIONCONTROL_LABEL_TAG);
+    return $query;
+  }
+}
+
+class VersioncontrolOperationController extends VersioncontrolEntityController {
+  protected $entityType = 'operation';
+  protected $baseTable = 'versioncontrol_operations';
+  protected $idKey = 'vc_op_id';
+}
+
+class VersioncontrolItemController extends VersioncontrolEntityController {
+  protected $entityType = 'item';
+  protected $baseTable = 'versioncontrol_items';
+  protected $idKey = 'item_revision_id';
+}
+
+/**
+ * Abstract parent class for all the various entity classes utilized by VC API.
+ *
+ * Basically just defines shared CRUD/loader-type behavior.
+ */
+abstract class VersioncontrolEntity {
+  protected $built = FALSE;
+
+  /**
+   * An instance of the Backend factory used to create this object, passed in
+   * to the constructor. If this entity needs to spawn more entities, then it
+   * should reuse this backend object to do so.
+   *
+   * @var VersioncontrolBackend
+   */
+  protected $backend;
+
+  public function __construct($backend = NULL) {
+    if (!$backend instanceof VersioncontrolBackend) {
+      $this->backend = $backend;
+    }
+    else if (variable_get('versioncontrol_single_backend_mode', FALSE)) {
+      $backends = versioncontrol_get_backends();
+      $this->backend = reset($backends);
+    }
+  }
+
+  /**
+   * Pseudo-constructor method; call this method with an associative array of
+   * properties to be assigned to this object.
+   *
+   * @param array $args
+   */
+  public function build($args = array()) {
+    // If this object has already been built, bail out.
+    if ($this->built == TRUE) {
+      return FALSE;
+    }
+
+    foreach ($args as $prop => $value) {
+      $this->$prop = $value;
+    }
+    if (is_string($this->data)) {
+      $this->data = unserialize($this->data);
+    }
+    $this->built = TRUE;
+  }
+}
diff --git versioncontrol.admin.inc versioncontrol.admin.inc
index ebd4f7e..1e87ee8 100644
--- versioncontrol.admin.inc
+++ versioncontrol.admin.inc
@@ -537,7 +537,7 @@ function versioncontrol_admin_account_export_display_form(&$form_state, $reposit
 function versioncontrol_admin_repository_list(&$form_state) {
   $form = array();
   $backends = versioncontrol_get_backends();
-  $repositories = VersioncontrolRepositoryCache::getInstance()->getRepositories();
+  $repositories = versioncontrol_repository_load_multiple(FALSE);
 
   if (empty($repositories)) {
     $form['empty'] = array(
diff --git versioncontrol.info versioncontrol.info
index 8a90e55..914f343 100644
--- versioncontrol.info
+++ versioncontrol.info
@@ -2,6 +2,7 @@
 name = "Version Control API"
 description = "An interface to version control systems whose functionality is provided by pluggable back-end modules."
 dependencies[] = autoload
+dependencies[] = dbtng
 package = Version Control
 core = 6.x
 php = 5.2
diff --git versioncontrol.install versioncontrol.install
index 997106a..7fffcb8 100644
--- versioncontrol.install
+++ versioncontrol.install
@@ -171,6 +171,13 @@ function versioncontrol_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'data' => array(
+        'description' => 'A serialized array of additional per-label data.',
+        'type' => 'text',
+        'size' => 'medium',
+        'not null' => TRUE,
+        'serialize' => TRUE,
+      ),
     ),
     'unique keys' => array(
       'repo_id_name_type' => array('repo_id', 'name', 'type'),
@@ -370,7 +377,7 @@ function versioncontrol_schema() {
         'default' => '',
       ),
       'data' => array(
-        'description' => t('A serialized array of additional per-repository settings, mostly populated by third-party modules.'),
+        'description' => t('A serialized array of additional per-repository settings, mostly populated by backends.'),
         'type' => 'text',
         'size' => 'medium',
         'not null' => TRUE,
@@ -629,3 +636,21 @@ function versioncontrol_update_6300() {
 
   return $ret;
 }
+
+/**
+ * Add a 'data' field to the labels table.
+ *
+ * @return array
+ */
+function versioncontrol_update_6301() {
+  $ret = array();
+  $data_spec = array(
+    'description' => 'A serialized array of additional per-label data.',
+    'type' => 'text',
+    'size' => 'medium',
+    'not null' => TRUE,
+    'serialize' => TRUE,
+  );
+  db_add_field($ret, 'versioncontrol_labels', 'data', $data_spec);
+  return $ret;
+}
\ No newline at end of file
diff --git versioncontrol.module versioncontrol.module
index 6570396..38c08a1 100644
--- versioncontrol.module
+++ versioncontrol.module
@@ -160,6 +160,24 @@ function versioncontrol_autoload_info() {
     );
   }
 
+  // Add controllers.inc contents
+  $controllers = array(
+    'VersioncontrolEntityController',
+    'VersioncontrolRepositoryController',
+    'VersioncontrolAccountController',
+    'VersioncontrolTagController',
+    'VersioncontrolBranchController',
+    'VersioncontrolOperationController',
+    'VersioncontrolItemController',
+    'VersioncontrolEntity',
+  );
+
+  foreach ($controllers as $name) {
+    $items[$name] = array(
+      'file' => "includes/controllers.inc",
+    );
+  }
+
   // Add special cases
   $items['VersioncontrolRepositoryUrlHandler'] = array(
     'file' => 'includes/VersioncontrolRepository.php',
@@ -177,6 +195,16 @@ function versioncontrol_autoload_info() {
 }
 
 /**
+ * Implementation of hook_flush_caches().
+ *
+ * Triggers backend mode determination.
+ *
+ */
+function versioncontrol_flush_caches() {
+  versioncontrol_determine_backend_mode();
+}
+
+/**
  * Implementation of hook_theme().
  */
 function versioncontrol_theme() {
@@ -415,6 +443,16 @@ function versioncontrol_user_access($account = NULL) {
 }
 
 /**
+ * Determine if we are operating in single or multi-backend mode, and set a
+ * $conf variable accordingly.
+ *
+ */
+function versioncontrol_determine_backend_mode() {
+  $single = count(versioncontrol_get_backends()) <= 1;
+  variable_set('versioncontrol_single_backend_mode', $single);
+}
+
+/**
  * Custom access callback, determining if the current user (or the one given
  * in @p $account, if set) is permitted to view version control account
  * settings of the user specified the first user id in @p $vcs_accounts.
@@ -477,8 +515,30 @@ function versioncontrol_perm() {
  * versioncontrol_get_repository() will be a static class method then.)
  */
 function versioncontrol_repository_load($repo_id) {
-  $repository = VersioncontrolRepositoryCache::getInstance()->getRepository($repo_id);
-  return empty($repository) ? FALSE : $repository;
+  $repository = versioncontrol_repository_load_multiple(array($repo_id));
+  return empty($repository) ? FALSE : reset($repository);
+}
+
+/**
+ * Load multiple versioncontrol repositories, given provided constraints.
+ *
+ * This function has two operational modes. If more than one backend type is
+ * present in the system, it defaults to the safer, backend-agnostic load
+ * process. Otherwise, it routes the call to the appropriate backend class,
+ * which takes care of the load call and caching the controller.
+ *
+ * FIXME Putting this in now, but honestly can't remember if it's the right
+ * approach. Need to hack on the entities themselves to remember.
+ *
+ * @param $ids
+ * @param $conditions
+ */
+function versioncontrol_repository_load_multiple($ids = array(), $conditions = array()) {
+  static $controller;
+  if (!isset($controller)) {
+    $controller = new VersioncontrolRepositoryController();
+  }
+  return $controller->load($ids, $conditions);
 }
 
 /**
@@ -504,25 +564,31 @@ function versioncontrol_user_accounts_load($uid, $include_unauthorized = FALSE)
 /**
  * Get a list of all backends with its detailed information.
  *
- * @return
- *   A structured array containing information about all known backends.
- *   Array keys are the unique string identifier of the version control
- *   system.
- *   The corresponding array values are VersioncontrolBackend children
- *   objects.
+ * @param string $backend
+ *   Optional; the backend type's backend object to be returned. If not
+ *   specified, all backend types are returned.
  *
- *   If no single backends can be found, an empty array is returned.
+ * @return mixed
+ *   Either a structured array containing backend objects from each backend,
+ *   keyed on the unique string identifier corresponding to that backend (e.g.
+ *   'cvs', 'svn').
+ *   The backend objects are all descendents of VersioncontrolBackend.
  *
- *   A real-life example of such a result array can be found
- *   in the FakeVCS example module.
+ *   An example of the result array can be found in the FakeVCS example module.
  */
-function versioncontrol_get_backends() {
+function versioncontrol_get_backends($backend = '') {
   static $backends;
 
   if (!isset($backends)) {
     $backends = module_invoke_all('versioncontrol_backends');
   }
-  return $backends;
+
+  if (!empty($backend)) {
+    return isset($backends[$backend]) ? $backends[$backend] : FALSE;
+  }
+  else {
+    return $backends;
+  }
 }
 
 /**
@@ -564,64 +630,6 @@ function _versioncontrol_get_fallback_authorization_method() {
 }
 
 /**
- * Assemble a list of query constraints given as string array that's
- * supposed to be imploded with an SQL "AND", and a $params array containing
- * the corresponding parameter values for all the '%d' and '%s' placeholders.
- */
-function _versioncontrol_construct_repository_constraints($constraints, $backends) {
-  $and_constraints = array();
-  $params = array();
-
-  // Filter out repositories of which the corresponding backend is not enabled,
-  // and handle the 'vcs' constraint at the same time.
-  $placeholders = array();
-  $vcses = array_keys($backends);
-  if (isset($constraints['vcs'])) {
-    $vcses = array_intersect($vcses, $constraints['vcs']);
-  }
-  if (empty($vcses)) {
-    $and_constraints[] = 'FALSE'; // no backends are enabled of those that have been requested
-  }
-  else {
-    foreach ($vcses as $vcs) {
-      $placeholders[] = "'%s'";
-      $params[] = $vcs;
-    }
-    $and_constraints[] = 'r.vcs IN ('. implode(',', $placeholders) .')';
-  }
-
-  if (isset($constraints['repo_ids'])) {
-    if (empty($constraints['repo_ids'])) {
-      $and_constraints[] = 'FALSE';
-    }
-    else {
-      $placeholders = array();
-      foreach ($constraints['repo_ids'] as $repo_id) {
-        $placeholders[] = '%d';
-        $params[] = $repo_id;
-      }
-      $and_constraints[] = 'r.repo_id IN ('. implode(',', $placeholders) .')';
-    }
-  }
-
-  if (isset($constraints['names'])) {
-    if (empty($constraints['names'])) {
-      $and_constraints[] = 'FALSE';
-    }
-    else {
-      $placeholders = array();
-      foreach ($constraints['names'] as $name) {
-        $placeholders[] = "'%s'";
-        $params[] = $name;
-      }
-      $and_constraints[] = 'r.name IN ('. implode(',', $placeholders) .')';
-    }
-  }
-
-  return array($and_constraints, $params);
-}
-
-/**
  * Execute a query with either db_query(), db_query_range() or pager_query().
  * Which one of those is called, and with which parameters, is specified by the
  * @p $options array, see versioncontrol_get_operations() for a description of
diff --git versioncontrol_fakevcs/includes/classes.inc versioncontrol_fakevcs/includes/classes.inc
index 8c957d7..02b75cc 100644
--- versioncontrol_fakevcs/includes/classes.inc
+++ versioncontrol_fakevcs/includes/classes.inc
@@ -3,6 +3,13 @@
 
 class VersioncontrolFakeBackend extends VersioncontrolBackend {
 
+  public $classes = array(
+    'repo'      => 'VersioncontrolFakeRepository',
+    'account'   => 'VersioncontrolFakeAccount',
+    'operation' => 'VersioncontrolFakeOperation',
+    'item'      => 'VersioncontrolFakeItem',
+  );
+
   public function __construct() {
     $this->name = 'FakeVCS';
     $this->description = t('FakeVCS is a version control system that is specifically capable in doing everything that any other version control system might ever do.');
@@ -24,12 +31,6 @@ class VersioncontrolFakeBackend extends VersioncontrolBackend {
         // but also to directories.
         VERSIONCONTROL_CAPABILITY_DIRECTORY_REVISIONS,
     );
-    $this->classes = array(
-      'repo'      => 'VersioncontrolFakeRepository',
-      'account'   => 'VersioncontrolFakeAccount',
-      'operation' => 'VersioncontrolFakeOperation',
-      'item'      => 'VersioncontrolFakeItem',
-    );
   }
 
 }
