diff --git a/dbtng_example/dbtng_example.install b/dbtng_example/dbtng_example.install
index dda29aa..213e632 100644
--- a/dbtng_example/dbtng_example.install
+++ b/dbtng_example/dbtng_example.install
@@ -7,7 +7,7 @@
 /**
  * Implements hook_install().
  *
- * We will create a default entry in the database.
+ * Creates some default entries on this module custom table.
  *
  * @see hook_install()
  * @ingroup dbtng_example
@@ -16,7 +16,7 @@ function dbtng_example_install() {
   // Outside of the .install file we would use drupal_write_record() to
   // populate the database, but it cannot be used here, so we'll use
   // db_insert().
-
+  //
   // Add a default entry.
   $fields = array(
     'name'    => 'John',
@@ -40,22 +40,11 @@ function dbtng_example_install() {
 }
 
 /**
- * Implements hook_uninstall().
- *
- * @see hook_uninstall()
- * @ingroup dbtng_example
- */
-function dbtng_example_uninstall() {
-  // Nothing.
-}
-
-
-/**
  * Implements hook_schema().
  *
  * Defines the database tables used by this module.
  * Remember that the easiest way to create the code for hook_schema is with
- * the @link http://drupal.org/project/schema schema module @endlink
+ * the @link http://drupal.org/project/schema schema module @endlink.
  *
  * @see hook_schema()
  * @ingroup dbtng_example
@@ -95,7 +84,7 @@ function dbtng_example_schema() {
         'default' => 0,
         'size' => 'tiny',
         'description' => 'The age of the person in years.',
-      )
+      ),
     ),
     'primary key' => array('pid'),
     'indexes' => array(
diff --git a/dbtng_example/dbtng_example.module b/dbtng_example/dbtng_example.module
index bc627f6..b3cb848 100644
--- a/dbtng_example/dbtng_example.module
+++ b/dbtng_example/dbtng_example.module
@@ -23,7 +23,9 @@
  * @link database.inc database abstraction layer documentation @endlink and
  * at @link http://drupal.org/node/310069 Database API @endlink.
  *
- * The several examples here demonstrate basic database usage.
+ * The several examples in DBTNGExampleController (see
+ * /lib/Drupal/dbtng_example/Controller/DBTNGExampleController.php) demonstrate
+ * basic database usage.
  *
  * db_insert() example:
  * @code
@@ -57,222 +59,6 @@
  * @see drupal_write_record()
  */
 
-/**
- * Save an entry in the database.
- *
- * The underlying DBTNG function is db_insert().
- *
- * Exception handling is shown in this example. It could be simplified
- * without the try/catch blocks, but since an insert will throw an exception
- * and terminate your application if the exception is not handled, it is best
- * to employ try/catch.
- *
- * @param $entry
- *   An array containing all the fields of the database record.
- *
- * @see db_insert()
- */
-function dbtng_example_entry_insert($entry) {
-  $return_value = NULL;
-  try {
-    $return_value = db_insert('dbtng_example')
-                    ->fields($entry)
-                    ->execute();
-  }
-  catch (Exception $e) {
-    drupal_set_message(t('db_insert failed. Message = %message, query= %query',
-      array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
-  }
-  return $return_value;
-}
-
-/**
- * Update an entry in the database.
- *
- * @param $entry
- *   An array containing all the fields of the item to be updated.
- *
- * @see db_update()
- */
-function dbtng_example_entry_update($entry) {
-  try {
-    // db_update()...->execute() returns the number of rows updated.
-    $count = db_update('dbtng_example')
-              ->fields($entry)
-              ->condition('pid', $entry['pid'])
-              ->execute();
-  }
-  catch (Exception $e) {
-    drupal_set_message(t('db_update failed. Message = %message, query= %query',
-      array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
-  }
-  return $count;
-}
-
-/**
- * Delete an entry from the database.
- *
- * @param $entry
- *   An array containing at least the person identifier 'pid' element of the
- *   entry to delete.
- *
- * @see db_delete()
- */
-function dbtng_example_entry_delete($entry) {
-  db_delete('dbtng_example')
-    ->condition('pid', $entry['pid'])
-    ->execute();
-}
-
-/**
- * Read from the database using a filter array.
- *
- * The standard function to perform reads was db_query(), and for static
- * queries, it still is.
- *
- * db_query() used an SQL query with placeholders and arguments as parameters.
- *
- * Drupal DBTNG provides an abstracted interface that will work with a wide
- * variety of database engines.
- *
- * db_query() is deprecated except when doing a static query. The following is
- * perfectly acceptable in Drupal 8. See
- * @link http://drupal.org/node/310072 the handbook page on static queries @endlink
- *
- * @code
- *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
- *   db_query(
- *     "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name",
- *     array(':uid' => 0, ':name' => 'John')
- *   )->execute();
- * @endcode
- *
- * But for more dynamic queries, Drupal provides the db_select()
- * API method, so there are several ways to perform the same SQL query.
- * See the @link http://drupal.org/node/310075 handbook page on dynamic queries. @endlink
- *
- * @code
- *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
- *   db_select('dbtng_example')
- *     ->fields('dbtng_example')
- *     ->condition('uid', 0)
- *     ->condition('name', 'John')
- *     ->execute();
- * @endcode
- *
- * Here is db_select with named placeholders:
- * @code
- *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
- *   $arguments = array(':name' => 'John', ':uid' => 0);
- *   db_select('dbtng_example')
- *     ->fields('dbtng_example')
- *     ->where('uid = :uid AND name = :name', $arguments)
- *     ->execute();
- * @endcode
- *
- * Conditions are stacked and evaluated as AND and OR depending on the type of
- * query. For more information, read the conditional queries handbook page at:
- * http://drupal.org/node/310086
- *
- * The condition argument is an 'equal' evaluation by default, but this can be
- * altered:
- * @code
- *   // SELECT * FROM {dbtng_example} WHERE age > 18
- *   db_select('dbtng_example')
- *     ->fields('dbtng_example')
- *     ->condition('age', 18, '>')
- *     ->execute();
- * @endcode
- *
- * @param $entry
- *   An array containing all the fields used to search the entries in the table.
- * @return
- *   An object containing the loaded entries if found.
- *
- * @see db_select()
- * @see db_query()
- * @see http://drupal.org/node/310072
- * @see http://drupal.org/node/310075
- *
- */
-function dbtng_example_entry_load($entry = array()) {
-  // Read all fields from the dbtng_example table.
-  $select = db_select('dbtng_example', 'example');
-  $select->fields('example');
-
-  // Add each field and value as a condition to this query.
-  foreach ($entry as $field => $value) {
-    $select->condition($field, $value);
-  }
-  // Return the result in object format.
-  return $select->execute()->fetchAll();
-}
-
-/**
- * Render a filtered list of entries in the database.
- *
- * DBTNG also helps processing queries that return several rows, providing the
- * found objects in the same query execution call.
- *
- * This function queries the database using a JOIN between users table and the
- * example entries, to provide the username that created the entry, and creates
- * a table with the results, processing each row.
- *
- * SELECT
- *  e.pid as pid, e.name as name, e.surname as surname, e.age as age
- *  u.name as username
- * FROM
- *  {dbtng_example} e
- * JOIN
- *  users u ON e.uid = u.uid
- * WHERE
- *  e.name = 'John' AND e.age > 18
- *
- * @see db_select()
- * @see http://drupal.org/node/310075
- */
-function dbtng_example_advanced_list() {
-  $output = '';
-
-  $select = db_select('dbtng_example', 'e');
-  // Join the users table, so we can get the entry creator's username.
-  $select->join('users', 'u', 'e.uid = u.uid');
-  // Select these specific fields for the output.
-  $select->addField('e', 'pid');
-  $select->addField('u', 'name', 'username');
-  $select->addField('e', 'name');
-  $select->addField('e', 'surname');
-  $select->addField('e', 'age');
-  // Filter only persons named "John".
-  $select->condition('e.name', 'John');
-  // Filter only persons older than 18 years.
-  $select->condition('e.age', 18, '>');
-  // Make sure we only get items 0-49, for scalability reasons.
-  $select->range(0, 50);
-
-  // Now, loop all these entries and show them in a table. Note that there is no
-  // db_fetch_* object or array function being called here. Also note that the
-  // following line could have been written as
-  // $entries = $select->execute()->fetchAll() which would return each selected
-  // record as an object instead of an array.
-  $entries = $select->execute()->fetchAll(PDO::FETCH_ASSOC);
-  if (!empty($entries)) {
-    $rows = array();
-    foreach ($entries as $entry) {
-      // Sanitize the data before handing it off to the theme layer.
-      $rows[] = array_map('check_plain', $entry);
-    }
-    // Make a table for them.
-    $header = array(t('Id'), t('Created by'), t('Name'), t('Surname'), t('Age'));
-    $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'dbtng-example-advanced-list')));
-  }
-  else {
-    drupal_set_message(t('No entries meet the filter criteria (Name = "John" and Age > 18).'));
-  }
-  return $output;
-}
-
-//// Helper functions ////
 
 /**
  * Implements hook_help().
@@ -285,13 +71,16 @@ function dbtng_example_help($path) {
     case 'examples/dbtng':
       $output = t('Generate a list of all entries in the database. There is no filter in the query.');
       break;
+
     case 'examples/dbtng/advanced':
       $output  = t('A more complex list of entries in the database.') . ' ';
       $output .= t('Only the entries with name = "John" and age older than 18 years are shown, the username of the person who created the entry is also shown.');
       break;
+
     case 'examples/dbtng/update':
       $output = t('Demonstrates a database update operation.');
       break;
+
     case 'examples/dbtng/add':
       $output = t('Add an entry to the dbtng_example table.');
       break;
@@ -309,8 +98,7 @@ function dbtng_example_menu() {
 
   $items['examples/dbtng'] = array(
     'title' => 'DBTNG Example',
-    'page callback' => 'dbtng_example_list',
-    'access callback' => TRUE,
+    'route_name' => 'dbtng_example_simple_list',
   );
   $items['examples/dbtng/list'] = array(
     'title' => 'List',
@@ -319,24 +107,19 @@ function dbtng_example_menu() {
   );
   $items['examples/dbtng/add'] = array(
     'title' => 'Add entry',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('dbtng_example_form_add'),
-    'access callback' => TRUE,
+    'route_name' => 'dbtng_example_add_form',
     'type' => MENU_LOCAL_TASK,
     'weight' => -9,
   );
   $items['examples/dbtng/update'] = array(
     'title' => 'Update entry',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('dbtng_example_form_update'),
+    'route_name' => 'dbtng_example_update_form',
     'type' => MENU_LOCAL_TASK,
-    'access callback' => TRUE,
     'weight' => -5,
   );
   $items['examples/dbtng/advanced'] = array(
     'title' => 'Advanced list',
-    'page callback' => 'dbtng_example_advanced_list',
-    'access callback' => TRUE,
+    'route_name' => 'dbtng_example_advanced_list',
     'type' => MENU_LOCAL_TASK,
   );
 
@@ -344,179 +127,5 @@ function dbtng_example_menu() {
 }
 
 /**
- * Render a list of entries in the database.
- */
-function dbtng_example_list() {
-  $output = '';
-
-  // Get all entries in the dbtng_example table.
-  if ($entries = dbtng_example_entry_load()) {
-    $rows = array();
-    foreach ($entries as $entry) {
-      // Sanitize the data before handing it off to the theme layer.
-      $rows[] = array_map('check_plain', (array) $entry);
-    }
-    // Make a table for them.
-    $header = array(t('Id'), t('uid'), t('Name'), t('Surname'), t('Age'));
-    $output .= theme('table', array('header' => $header, 'rows' => $rows));
-  }
-  else {
-    drupal_set_message(t('No entries have been added yet.'));
-  }
-  return $output;
-}
-
-/**
- * Prepare a simple form to add an entry, with all the interesting fields.
- */
-function dbtng_example_form_add($form, &$form_state) {
-  $form = array();
-
-  $form['add'] = array(
-    '#type'  => 'fieldset',
-    '#title' => t('Add a person entry'),
-  );
-  $form['add']['name'] = array(
-    '#type'  => 'textfield',
-    '#title' => t('Name'),
-    '#size'  => 15,
-  );
-  $form['add']['surname'] = array(
-    '#type'  => 'textfield',
-    '#title' => t('Surname'),
-    '#size'  => 15,
-  );
-  $form['add']['age'] = array(
-    '#type'  => 'textfield',
-    '#title' => t('Age'),
-    '#size'  => 5,
-    '#description' => t("Values greater than 127 will cause an exception. Try it - it's a great example why exception handling is needed with DTBNG."),
-  );
-  $form['add']['submit'] = array(
-    '#type'  => 'submit',
-    '#value' => t('Add'),
-  );
-
-  return $form;
-}
-
-/**
- * Submit handler for 'add entry' form.
- */
-function dbtng_example_form_add_submit($form, &$form_state) {
-  $account = \Drupal::request()->attributes->get('_account');
-
-  // Save the submitted entry.
-  $entry = array(
-    'name'    => $form_state['values']['name'],
-    'surname' => $form_state['values']['surname'],
-    'age'     => $form_state['values']['age'],
-    'uid'     => $account->id(),
-  );
-  $return = dbtng_example_entry_insert($entry);
-  if ($return) {
-    drupal_set_message(t('Created entry @entry', array('@entry' => print_r($entry, TRUE))));
-  }
-}
-
-/**
- * Sample UI to update a record.
- */
-function dbtng_example_form_update($form, &$form_state) {
-  $form = array(
-    '#prefix' => '<div id="updateform">',
-    '#suffix' => '</div>',
-  );
-
-  $entries = dbtng_example_entry_load();
-  $keyed_entries = array();
-  if (empty($entries)) {
-    $form['no_values'] = array(
-      '#value' => t('No entries exist in the table dbtng_example table.'),
-    );
-    return $form;
-  }
-
-  foreach ($entries as $entry) {
-    $options[$entry->pid] = t('@pid: @name @surname (@age)', array('@pid' => $entry->pid, '@name' => $entry->name, '@surname' => $entry->surname, '@age' => $entry->age));
-    $keyed_entries[$entry->pid] = $entry;
-  }
-  $default_entry = !empty($form_state['values']['pid']) ? $keyed_entries[$form_state['values']['pid']] : $entries[0];
-
-  $form_state['entries'] = $keyed_entries;
-
-  $form['pid'] = array(
-    '#type' => 'select',
-    '#options' => $options,
-    '#title' => t('Choose entry to update'),
-    '#default_value' => $default_entry->pid,
-    '#ajax' => array(
-      'wrapper' => 'updateform',
-      'callback' => 'dbtng_example_form_update_callback',
-    ),
-  );
-
-  $form['name'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Updated first name'),
-    '#size' => 15,
-    '#default_value' => $default_entry->name,
-  );
-
-  $form['surname'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Updated last name'),
-    '#size' => 15,
-    '#default_value' => $default_entry->surname,
-  );
-  $form['age'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Updated age'),
-    '#size' => 4,
-    '#default_value' => $default_entry->age,
-    '#description' => t('Values greater than 127 will cause an exception'),
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Update'),
-  );
-  return $form;
-}
-
-/**
- * AJAX callback handler for the pid select.
- *
- * When the pid changes, populates the defaults from the database in the form.
- */
-function dbtng_example_form_update_callback($form, $form_state) {
-  $entry = $form_state['entries'][$form_state['values']['pid']];
-  // Setting the #value of items is the only way I was able to figure out
-  // to get replaced defaults on these items. #default_value will not do it
-  // and shouldn't.
-  foreach (array('name', 'surname', 'age') as $item) {
-    $form[$item]['#value'] = $entry->$item;
-  }
-  return $form;
-}
-
-/**
- * Submit handler for 'update entry' form.
- */
-function dbtng_example_form_update_submit($form, &$form_state) {
-  $account = \Drupal::request()->attributes->get('_account');
-
-  // Save the submitted entry.
-  $entry = array(
-    'pid' => $form_state['values']['pid'],
-    'name' => $form_state['values']['name'],
-    'surname' => $form_state['values']['surname'],
-    'age' => $form_state['values']['age'],
-    'uid' => $account->id(),
-  );
-  $count = dbtng_example_entry_update($entry);
-  drupal_set_message(t('Updated entry @entry (@count row updated)', array('@count' => $count, '@entry' => print_r($entry, TRUE))));
-}
-/**
  * @} End of "defgroup dbtng_example".
  */
diff --git a/dbtng_example/dbtng_example.routing.yml b/dbtng_example/dbtng_example.routing.yml
new file mode 100644
index 0000000..cef88b9
--- /dev/null
+++ b/dbtng_example/dbtng_example.routing.yml
@@ -0,0 +1,24 @@
+dbtng_example_simple_list:
+  pattern: 'examples/dbtng'
+  defaults:
+    _content: '\Drupal\dbtng_example\Controller\DBTNGExampleController::simpleList'
+  requirements:
+    _access: 'TRUE'
+dbtng_example_add_form:
+  pattern: 'examples/dbtng/add'
+  defaults:
+    _form: '\Drupal\dbtng_example\Forms\DBTNGExampleAddForm'
+  requirements:
+    _access: 'TRUE'
+dbtng_example_update_form:
+  pattern: 'examples/dbtng/update'
+  defaults:
+    _form: '\Drupal\dbtng_example\Forms\DBTNGExampleUpdateForm'
+  requirements:
+    _access: 'TRUE'
+dbtng_example_advanced_list:
+  pattern: 'examples/dbtng/advanced'
+  defaults:
+    _content: '\Drupal\dbtng_example\Controller\DBTNGExampleController::advancedList'
+  requirements:
+    _access: 'TRUE'
diff --git a/dbtng_example/lib/Drupal/dbtng_example/Controller/DBTNGExampleController.php b/dbtng_example/lib/Drupal/dbtng_example/Controller/DBTNGExampleController.php
new file mode 100644
index 0000000..191eae7
--- /dev/null
+++ b/dbtng_example/lib/Drupal/dbtng_example/Controller/DBTNGExampleController.php
@@ -0,0 +1,290 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\dbtng_example\Controller\DBTNGExampleController.
+ */
+
+namespace Drupal\dbtng_example\Controller;
+
+/**
+ * Controller for DBTNG example
+ */
+class DBTNGExampleController {
+
+  /**
+   * Database table to use.
+   *
+   * This varaible stores the database table to use in the functions below.
+   *
+   * @var string.
+   */
+  protected $table = 'dbtng_example';
+
+  /**
+   * Save an entry in the database.
+   *
+   * The underlying DBTNG function is db_insert().
+   *
+   * Exception handling is shown in this example. It could be simplified
+   * without the try/catch blocks, but since an insert will throw an exception
+   * and terminate your application if the exception is not handled, it is best
+   * to employ try/catch.
+   *
+   * @param array $entry
+   *   An array containing all the fields of the database record.
+   *
+   * @see db_insert()
+   */
+  public function insert($entry) {
+    $return_value = NULL;
+    try {
+      $return_value = db_insert($this->table)
+        ->fields($entry)
+        ->execute();
+    }
+    catch (Exception $e) {
+      drupal_set_message(t('db_insert failed. Message = %message, query= %query',
+        array(
+          '%message' => $e->getMessage(),
+          '%query' => $e->query_string,
+        )), 'error');
+    }
+    return $return_value;
+  }
+
+  /**
+   * Update an entry in the database.
+   *
+   * @param array $entry
+   *   An array containing all the fields of the item to be updated.
+   *
+   * @see db_update()
+   */
+  public function update($entry) {
+    try {
+      // db_update()...->execute() returns the number of rows updated.
+      $count = db_update($this->table)
+        ->fields($entry)
+        ->condition('pid', $entry['pid'])
+        ->execute();
+    }
+    catch (Exception $e) {
+      drupal_set_message(t('db_update failed. Message = %message, query= %query',
+        array(
+          '%message' => $e->getMessage(),
+          '%query' => $e->query_string,
+        )), 'error');
+    }
+    return $count;
+  }
+
+  /**
+   * Delete an entry from the database.
+   *
+   * @param array $entry
+   *   An array containing at least the person identifier 'pid' element of the
+   *   entry to delete.
+   *
+   * @see db_delete()
+   */
+  public function delete($entry) {
+    db_delete($this->table)
+      ->condition('pid', $entry['pid'])
+      ->execute();
+  }
+
+  /**
+   * Read from the database using a filter array.
+   *
+   * The standard function to perform reads was db_query(), and for static
+   * queries, it still is.
+   *
+   * db_query() used an SQL query with placeholders and arguments as parameters.
+   *
+   * Drupal DBTNG provides an abstracted interface that will work with a wide
+   * variety of database engines.
+   *
+   * db_query() is deprecated except when doing a static query. The following is
+   * perfectly acceptable in Drupal 8. See
+   * @link http://drupal.org/node/310072 the handbook page on static queries
+   * @endlink
+   *
+   * @code
+   *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+   *   db_query(
+   *     "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name",
+   *     array(':uid' => 0, ':name' => 'John')
+   *   )->execute();
+   * @endcode
+   *
+   * But for more dynamic queries, Drupal provides the db_select()
+   * API method, so there are several ways to perform the same SQL query.
+   * See the @link http://drupal.org/node/310075 handbook page on dynamic
+   * queries. @endlink
+   *
+   * @code
+   *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+   *   db_select('dbtng_example')
+   *     ->fields('dbtng_example')
+   *     ->condition('uid', 0)
+   *     ->condition('name', 'John')
+   *     ->execute();
+   * @endcode
+   *
+   * Here is db_select with named placeholders:
+   * @code
+   *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+   *   $arguments = array(':name' => 'John', ':uid' => 0);
+   *   db_select('dbtng_example')
+   *     ->fields('dbtng_example')
+   *     ->where('uid = :uid AND name = :name', $arguments)
+   *     ->execute();
+   * @endcode
+   *
+   * Conditions are stacked and evaluated as AND and OR depending on the type of
+   * query. For more information, read the conditional queries handbook page at:
+   * http://drupal.org/node/310086
+   *
+   * The condition argument is an 'equal' evaluation by default, but this can be
+   * altered:
+   * @code
+   *   // SELECT * FROM {dbtng_example} WHERE age > 18
+   *   db_select('dbtng_example')
+   *     ->fields('dbtng_example')
+   *     ->condition('age', 18, '>')
+   *     ->execute();
+   * @endcode
+   *
+   * @param array $entry
+   *   An array containing all fields used to search the entries in the table.
+   *
+   * @return object
+   *   An object containing the loaded entries if found.
+   *
+   * @see db_select()
+   * @see db_query()
+   * @see http://drupal.org/node/310072
+   * @see http://drupal.org/node/310075
+   */
+  public function load($entry = array()) {
+    // Read all fields from the dbtng_example table.
+    $select = db_select($this->table, 'example');
+    $select->fields('example');
+
+    // Add each field and value as a condition to this query.
+    foreach ($entry as $field => $value) {
+      $select->condition($field, $value);
+    }
+    // Return the result in object format.
+    return $select->execute()->fetchAll();
+  }
+
+  /**
+   * Render a list of entries in the database.
+   *
+   * This page returns a list of entries in the database provided by the DBTNG
+   * example module.
+   */
+  public function simpleList() {
+    $build = array();
+    // Get all entries in the dbtng_example table.
+    if ($entries = $this->load()) {
+      // Make a table for them.
+      $build['table'] = array(
+        '#type' => 'table',
+        '#header' => array(
+          t('Id'),
+          t('uid'),
+          t('Name'),
+          t('Surname'),
+          t('Age'),
+        ),
+        '#rows' => array(),
+      );
+
+      foreach ($entries as $entry) {
+        // Sanitize the data before handing it off to the theme layer.
+        $build['table']['#rows'][] = array_map('check_plain', (array) $entry);
+      }
+
+    }
+    else {
+      drupal_set_message(t('No entries have been added yet.'));
+    }
+    return $build;
+  }
+
+  /**
+   * Render a filtered list of entries in the database.
+   *
+   * DBTNG also helps processing queries that return several rows, providing the
+   * found objects in the same query execution call.
+   *
+   * This function queries the database using a JOIN between users table and the
+   * example entries, to provide the username that created the entry, and
+   * creates a table with the results, processing each row.
+   *
+   * SELECT
+   *  e.pid as pid, e.name as name, e.surname as surname, e.age as age
+   *  u.name as username
+   * FROM
+   *  {dbtng_example} e
+   * JOIN
+   *  users u ON e.uid = u.uid
+   * WHERE
+   *  e.name = 'John' AND e.age > 18
+   *
+   * @see db_select()
+   * @see http://drupal.org/node/310075
+   */
+  public function advancedList() {
+    $build = array();
+
+    $select = db_select('dbtng_example', 'e');
+    // Join the users table, so we can get the entry creator's username.
+    $select->join('users', 'u', 'e.uid = u.uid');
+    // Select these specific fields for the output.
+    $select->addField('e', 'pid');
+    $select->addField('u', 'name', 'username');
+    $select->addField('e', 'name');
+    $select->addField('e', 'surname');
+    $select->addField('e', 'age');
+    // Filter only persons named "John".
+    $select->condition('e.name', 'John');
+    // Filter only persons older than 18 years.
+    $select->condition('e.age', 18, '>');
+    // Make sure we only get items 0-49, for scalability reasons.
+    $select->range(0, 50);
+
+    // Now, loop all these entries and show them in a table. Note that there is
+    // no db_fetch_* object or array function being called here. Also note that
+    // the following line could have been written as
+    // $entries = $select->execute()->fetchAll() which would return each
+    // selected record as an object instead of an array.
+    $entries = $select->execute()->fetchAll(\PDO::FETCH_ASSOC);
+    if (!empty($entries)) {
+      $build['table'] = array(
+        '#type' => 'table',
+        '#header' => array(
+          t('Id'),
+          t('Created by'),
+          t('Name'),
+          t('Surname'),
+          t('Age'),
+        ),
+        '#rows' => array(),
+        '#attributes' => array('id' => 'dbtng-example-advanced-list'),
+      );
+      foreach ($entries as $entry) {
+        // Sanitize the data before handing it off to the theme layer.
+        $build['table']['#rows'][] = array_map('check_plain', $entry);
+      }
+    }
+    else {
+      drupal_set_message(t('No entries meet the filter criteria (Name = "John" and Age > 18).'));
+    }
+    return $build;
+  }
+
+}
diff --git a/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleAddForm.php b/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleAddForm.php
new file mode 100644
index 0000000..b255868
--- /dev/null
+++ b/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleAddForm.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\cron_example\Form\DBTNGExampleAddForm
+ */
+
+namespace Drupal\dbtng_example\Forms;
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\dbtng_example\Controller\DBTNGExampleController;
+
+
+/**
+ * Simple form to add an entry, with all the interesting fields.
+ */
+class DBTNGExampleAddForm implements FormInterface {
+
+  /**
+   * The DBTNG Example Controller.
+   *
+   * @var \Drupal\dbtng_example\Controller\DBTNGExampleController
+   */
+  protected $controller;
+
+  /**
+   * Constructs a new DBTNGExampleAddForm.
+   */
+  public function __construct() {
+    $this->controller = new DBTNGExampleController();
+  }
+
+  /**
+   * From ID.
+   */
+  public function getFormID() {
+    return 'dbtng_example_add';
+  }
+
+  /**
+   * Build the add form.
+   */
+  public function buildForm(array $form, array &$form_state) {
+
+    $form['add'] = array(
+      '#type'  => 'fieldset',
+      '#title' => t('Add a person entry'),
+    );
+    $form['add']['name'] = array(
+      '#type'  => 'textfield',
+      '#title' => t('Name'),
+      '#size'  => 15,
+    );
+    $form['add']['surname'] = array(
+      '#type'  => 'textfield',
+      '#title' => t('Surname'),
+      '#size'  => 15,
+    );
+    $form['add']['age'] = array(
+      '#type'  => 'textfield',
+      '#title' => t('Age'),
+      '#size'  => 5,
+      '#description' => t("Values greater than 127 will cause an exception. Try it - it's a great example why exception handling is needed with DTBNG."),
+    );
+    $form['add']['submit'] = array(
+      '#type'  => 'submit',
+      '#value' => t('Add'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Validate the form.
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    // Confirm that age is numeric.
+    if (!intval($form_state['values']['age'])) {
+      form_set_error('age', t('Age needs to be a number'));
+    }
+  }
+
+  /**
+   * The actual insert.
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $account = \Drupal::request()->attributes->get('_account');
+
+    // Save the submitted entry.
+    $entry = array(
+      'name'    => $form_state['values']['name'],
+      'surname' => $form_state['values']['surname'],
+      'age'     => $form_state['values']['age'],
+      'uid'     => $account->id(),
+    );
+    $return = $this->controller->insert($entry);
+    if ($return) {
+      drupal_set_message(t('Created entry @entry', array('@entry' => print_r($entry, TRUE))));
+    }
+  }
+
+}
diff --git a/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleUpdateForm.php b/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleUpdateForm.php
new file mode 100644
index 0000000..987261e
--- /dev/null
+++ b/dbtng_example/lib/Drupal/dbtng_example/Forms/DBTNGExampleUpdateForm.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\cron_example\Form\DBTNGExampleUpdateForm
+ */
+
+namespace Drupal\dbtng_example\Forms;
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\dbtng_example\Controller\DBTNGExampleController;
+
+
+/**
+ * Sample UI to update a record.
+ */
+class DBTNGExampleUpdateForm implements FormInterface {
+
+  /**
+   * The DBTNG Example Controller.
+   *
+   * @var \Drupal\dbtng_example\Controller\DBTNGExampleController
+   */
+  protected $controller;
+
+  /**
+   * Constructs a new DBTNGExampleAddForm.
+   */
+  public function __construct() {
+    $this->controller = new DBTNGExampleController();
+  }
+
+  /**
+   * From ID.
+   */
+  public function getFormID() {
+    return 'dbtng_example_update';
+  }
+
+  /**
+   * Build the update form.
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $form = array(
+      '#prefix' => '<div id="updateform">',
+      '#suffix' => '</div>',
+    );
+
+    $entries = $this->controller->load();
+    $keyed_entries = array();
+    if (empty($entries)) {
+      $form['no_values'] = array(
+        '#value' => t('No entries exist in the table dbtng_example table.'),
+      );
+      return $form;
+    }
+
+    foreach ($entries as $entry) {
+      $options[$entry->pid] = t('@pid: @name @surname (@age)', array(
+        '@pid' => $entry->pid,
+        '@name' => $entry->name,
+        '@surname' => $entry->surname,
+        '@age' => $entry->age,
+      ));
+      $keyed_entries[$entry->pid] = $entry;
+    }
+    $default_entry = !empty($form_state['values']['pid']) ? $keyed_entries[$form_state['values']['pid']] : $entries[0];
+
+    $form_state['entries'] = $keyed_entries;
+
+    $form['pid'] = array(
+      '#type' => 'select',
+      '#options' => $options,
+      '#title' => t('Choose entry to update'),
+      '#default_value' => $default_entry->pid,
+      '#ajax' => array(
+        'wrapper' => 'updateform',
+        'callback' => array($this, 'updateCallback'),
+      ),
+    );
+
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Updated first name'),
+      '#size' => 15,
+      '#default_value' => $default_entry->name,
+    );
+
+    $form['surname'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Updated last name'),
+      '#size' => 15,
+      '#default_value' => $default_entry->surname,
+    );
+    $form['age'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Updated age'),
+      '#size' => 4,
+      '#default_value' => $default_entry->age,
+      '#description' => t('Values greater than 127 will cause an exception'),
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Update'),
+    );
+    return $form;
+  }
+
+  /**
+   * AJAX callback handler for the pid select.
+   *
+   * When the pid changes, populates the defaults from the database in the form.
+   */
+  public function updateCallback($form, $form_state) {
+    $entry = $form_state['entries'][$form_state['values']['pid']];
+    // Setting the #value of items is the only way I was able to figure out
+    // to get replaced defaults on these items. #default_value will not do it
+    // and shouldn't.
+    foreach (array('name', 'surname', 'age') as $item) {
+      $form[$item]['#value'] = $entry->$item;
+    }
+    return $form;
+  }
+
+  /**
+   * Validating the form.
+   */
+  public function validateForm(array &$form, array &$form_state) {
+    // Confirm that age is numeric.
+    if (!intval($form_state['values']['age'])) {
+      form_set_error('age', t('Age needs to be a number'));
+    }
+  }
+
+  /**
+   * The actual update.
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $account = \Drupal::request()->attributes->get('_account');
+
+    // Save the submitted entry.
+    $entry = array(
+      'pid' => $form_state['values']['pid'],
+      'name' => $form_state['values']['name'],
+      'surname' => $form_state['values']['surname'],
+      'age' => $form_state['values']['age'],
+      'uid' => $account->id(),
+    );
+    $count = $this->controller->update($entry);
+    drupal_set_message(t('Updated entry @entry (@count row updated)', array(
+      '@count' => $count,
+      '@entry' => print_r($entry, TRUE),
+    )));
+  }
+
+}
diff --git a/dbtng_example/lib/Drupal/dbtng_example/Tests/DBTNGExampleTest.php b/dbtng_example/lib/Drupal/dbtng_example/Tests/DBTNGExampleTest.php
index d642839..fe5a940 100644
--- a/dbtng_example/lib/Drupal/dbtng_example/Tests/DBTNGExampleTest.php
+++ b/dbtng_example/lib/Drupal/dbtng_example/Tests/DBTNGExampleTest.php
@@ -1,31 +1,45 @@
 <?php
 /**
  * @file
- * SimpleTests for dbtng_example module.
+ * Contains DBTNGExampleTest.
  */
 
 namespace Drupal\dbtng_example\Tests;
 use Drupal\simpletest\WebTestBase;
+use Drupal\dbtng_example\Controller\DBTNGExampleController;
 
 /**
- * Default test case for the dbtng_example module.
+ * Tests for the dbtng_example module.
  */
 class DBTNGExampleTest extends WebTestBase {
   public static $modules = array('dbtng_example');
 
+  /**
+   * Test info.
+   */
   public static function getInfo() {
     return array(
-      'name' => 'DBTNG example unit and UI tests',
-      'description' => 'Various unit tests on the dbtng example module.' ,
+      'name' => 'DBTNG example api and UI tests',
+      'description' => 'Various api tests on the dbtng example module.' ,
       'group' => 'Examples',
     );
   }
 
   /**
-   * Test default module installation, two entries in the database table.
+   * Sets up a Drupal site for running functional and integration tests.
+   *
+   * Adding the controller.
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->controller = new DBTNGExampleController();
+  }
+
+  /**
+   * Assert that two entries were inserted at install.
    */
-  function testInstall() {
-    $result = dbtng_example_entry_load();
+  public function testInstall() {
+    $result = $this->controller->load();
     $this->assertEqual(
       count($result),
       2,
@@ -33,27 +47,25 @@ class DBTNGExampleTest extends WebTestBase {
     );
   }
 
-
   /**
-   * Test the UI.
+   * Tests the UI.
    */
-  function testUI() {
+  public function testUI() {
     // Test the basic list.
     $this->drupalGet('examples/dbtng');
     $this->assertPattern("/John[td\/<>\w]+Doe/", "Text 'John Doe' found in table");
 
-    //Test the add tab.
+    // Test the add tab.
     // Add the new entry.
     $this->drupalPost('examples/dbtng/add',
-      array('name'  => 'Some', 'surname' => 'Anonymous', 'age' => 33), t('Add'));
+      array('name' => 'Some', 'surname' => 'Anonymous', 'age' => 33), t('Add'));
     // Now find the new entry.
     $this->drupalGet('examples/dbtng');
     $this->assertPattern("/Some[td\/<>\w]+Anonymous/", "Text 'Some Anonymous' found in table");
 
-
     // Try the update tab.
     // Find out the pid of our "anonymous" guy.
-    $result = dbtng_example_entry_load(array('surname' => 'Anonymous'));
+    $result = $this->controller->load(array('surname' => 'Anonymous'));
     $this->drupalGet("examples/dbtng");
     $this->assertEqual(
       count($result),
@@ -64,48 +76,48 @@ class DBTNGExampleTest extends WebTestBase {
     unset($entry->uid);
     $entry->name = 'NewFirstName';
     $this->drupalPost('examples/dbtng/update',
-      (array)$entry, t('Update'));
+      (array) $entry, t('Update'));
     // Now find the new entry.
     $this->drupalGet('examples/dbtng');
     $this->assertPattern("/NewFirstName[td\/<>\w]+Anonymous/", "Text 'NewFirstName Anonymous' found in table");
 
     // Try the advanced tab.
     $this->drupalGet('examples/dbtng/advanced');
-    $rows = $this->xpath("//*[@id='dbtng-example-advanced-list'][1]/tbody/tr");
+    $rows = $this->xpath("//table[@id='dbtng-example-advanced-list']/tbody/tr");
     $this->assertEqual(count($rows), 1, 'One row found in advanced view');
-    $this->assertFieldByXPath("//*[@id='dbtng-example-advanced-list'][1]/tbody/tr/td[4]", "Roe", "Name 'Roe' Exists in advanced list");
+    $this->assertFieldByXPath("//table[@id='dbtng-example-advanced-list']/tbody/tr[1]/td[4]", "Roe", "Name 'Roe' Exists in advanced list");
   }
 
   /**
-   * Test several combinations, adding entries, updating and deleting.
+   * Tests several combinations, adding entries, updating and deleting.
    */
-  function testAPIExamples() {
+  public function testAPIExamples() {
     // Create a new entry.
     $entry = array(
       'name' => 'James',
       'surname' => 'Doe',
       'age' => 23,
     );
-    dbtng_example_entry_insert($entry);
+    $this->controller->insert($entry);
 
-    // Save another entry
+    // Save another entry.
     $entry = array(
       'name' => 'Jane',
       'surname' => 'NotDoe',
       'age' => 19,
     );
-    dbtng_example_entry_insert($entry);
+    $this->controller->insert($entry);
 
-    // Verify that 4 records are found in the database
-    $result = dbtng_example_entry_load();
+    // Verify that 4 records are found in the database.
+    $result = $this->controller->load();
     $this->assertEqual(
       count($result),
       4,
       'Found a total of four entries in the table after creating two additional entries.'
     );
 
-    // Verify 2 of these records have 'Doe' as surname
-    $result = dbtng_example_entry_load(array('surname' => 'Doe'));
+    // Verify 2 of these records have 'Doe' as surname.
+    $result = $this->controller->load(array('surname' => 'Doe'));
     $this->assertEqual(
       count($result),
       2,
@@ -113,7 +125,7 @@ class DBTNGExampleTest extends WebTestBase {
     );
 
     // Now find our not-Doe entry.
-    $result = dbtng_example_entry_load(array('surname' => 'NotDoe'));
+    $result = $this->controller->load(array('surname' => 'NotDoe'));
     $this->assertEqual(
       count($result),
       1,
@@ -121,31 +133,31 @@ class DBTNGExampleTest extends WebTestBase {
     // Our NotDoe will be changed to "NowDoe".
     $entry = $result[0];
     $entry->surname = "NowDoe";
-    dbtng_example_entry_update((array)$entry);
+    $this->controller->update((array) $entry);
 
-    $result = dbtng_example_entry_load(array('surname' => 'NowDoe'));
+    $result = $this->controller->load(array('surname' => 'NowDoe'));
     $this->assertEqual(
       count($result),
       1,
       "Found renamed 'NowDoe' surname");
 
     // Read only John Doe entry.
-    $result = dbtng_example_entry_load(array('name' => 'John', 'surname' => 'Doe'));
+    $result = $this->controller->load(array('name' => 'John', 'surname' => 'Doe'));
     $this->assertEqual(
       count($result),
       1,
       'Found one entry for John Doe.'
     );
-    // Get the entry
+    // Get the entry.
     $entry = (array) end($result);
-    // Change age to 45
+    // Change age to 45.
     $entry['age'] = 45;
-    // Update entry in database
-    dbtng_example_entry_update((array)$entry);
+    // Update entry in database.
+    $this->controller->update((array) $entry);
 
     // Find entries with age = 45
     // Read only John Doe entry.
-    $result = dbtng_example_entry_load(array('surname' => 'NowDoe'));
+    $result = $this->controller->load(array('surname' => 'NowDoe'));
     $this->assertEqual(
       count($result),
       1,
@@ -166,10 +178,10 @@ class DBTNGExampleTest extends WebTestBase {
     );
 
     // Delete the entry.
-    dbtng_example_entry_delete($entry);
+    $this->controller->delete($entry);
 
-    // Verify that now there are only 3 records
-    $result = dbtng_example_entry_load();
+    // Verify that now there are only 3 records.
+    $result = $this->controller->load();
     $this->assertEqual(
       count($result),
       3,
diff --git a/examples.index.php b/examples.index.php
index 4557a94..49ca28f 100644
--- a/examples.index.php
+++ b/examples.index.php
@@ -6,7 +6,6 @@
  * Add links here as they are ported to 8.x-1.x
  *
  * - @link block_example.module Defining blocks @endlink
- * - @link cron_example.module Cron related examples @endlink
  * - @link dbtng_example.module Database examples (DBTNG) @endlink
  * - @link page_example.module Creating a custom page @endlink
  */
