diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index f1d0046..76164f4 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -71,7 +71,9 @@ public function build(ContainerBuilder $container) {
       ->addArgument(new Reference('database'));
     $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
       ->addArgument(new Reference('router.dumper'))
-      ->addArgument(new Reference('lock'));
+      ->addArgument(new Reference('lock'))
+      ->addArgument(new Reference('dispatcher'));
+
 
     $container->register('matcher', 'Drupal\Core\Routing\ChainMatcher');
     $container->register('legacy_url_matcher', 'Drupal\Core\LegacyUrlMatcher')
diff --git a/core/lib/Drupal/Core/Routing/RouteBuildEvent.php b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
new file mode 100644
index 0000000..9f16bcb
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteBuildEvent.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\Routing\RouteCollection;
+
+class RouteBuildEvent extends Event {
+
+  /**
+   * The route collection.
+   *
+   * @var Symfony\Component\Routing\RouteCollection
+   */
+  protected $routeCollection;
+
+  /**
+   * The module name.
+   *
+   * @var string
+   */
+  protected $module;
+
+  /**
+   * Constructor.
+   */
+  public function __construct(RouteCollection $route_collection, $module) {
+    $this->routeCollection = $route_collection;
+    $this->module = $module;
+  }
+
+  /**
+   * Get the route collection.
+   */
+  public function getRouteCollection() {
+    return $this->routeCollection;
+  }
+
+  /**
+   * Get the module.
+   */
+  public function getModule() {
+    return $this->module;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
index 3a27767..d69e5e3 100644
--- a/core/lib/Drupal/Core/Routing/RouteBuilder.php
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -7,8 +7,12 @@
 
 namespace Drupal\Core\Routing;
 
-use Drupal\Core\Lock\LockBackendInterface;
 use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Routing\RouteCollection;
+
+use Drupal\Core\Lock\LockBackendInterface;
 
 /**
  * Managing class for rebuilding the router table.
@@ -33,16 +37,26 @@ class RouteBuilder {
   protected $lock;
 
   /**
+   * The event dispatcher to notify of routes.
+   *
+   * @var Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
    * Construcs the RouteBuilder using the passed MatcherDumperInterface.
    *
    * @param \Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface $dumper
    *   The matcher dumper used to store the route information.
    * @param \Drupal\Core\Lock\LockBackendInterface $lock
    *   The lock backend.
+   * @param \Symfony\Component\EventDispatcherEventDispatcherInterface
+   *   The event dispatcher to notify of routes.
    */
-  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock) {
+  public function __construct(MatcherDumperInterface $dumper, LockBackendInterface $lock, EventDispatcherInterface $dispatcher) {
     $this->dumper = $dumper;
     $this->lock = $lock;
+    $this->dispatcher = $dispatcher;
   }
 
   /**
@@ -57,15 +71,38 @@ public function rebuild() {
       return;
     }
 
+    $parser = new Parser();
+
     // We need to manually call each module so that we can know which module
     // a given item came from.
-
-    foreach (module_implements('route_info') as $module) {
-      $routes = call_user_func($module . '_route_info');
-      drupal_alter('router_info', $routes, $module);
-      $this->dumper->addRoutes($routes);
+    // @todo Use an injected Extension service rather than module_list():
+    //   http://drupal.org/node/1331486.
+    foreach (module_list() as $module) {
+      $collection = new RouteCollection();
+      $routing_file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . '/' . $module . '.routing.yml';
+      if (file_exists($routing_file)) {
+        $routes = $parser->parse(file_get_contents($routing_file));
+        if (!empty($routes)) {
+          foreach ($routes as $name => $route_info) {
+            $defaults = isset($route_info['defaults']) ? $route_info['defaults'] : array();
+            $requirements = isset($route_info['requirements']) ? $route_info['requirements'] : array();
+            $route = new Route($route_info['pattern'], $defaults, $requirements);
+            $collection->add($name, $route);
+          }
+        }
+      }
+      $this->eventDispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, $module));
+      $this->dumper->addRoutes($collection);
       $this->dumper->dump(array('route_set' => $module));
     }
+
+    // Now allow modules to register additional, dynamic routes.
+    $collection = new RouteCollection();
+    $this->eventDispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection, 'dynamic_routes'));
+    $this->eventDispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection, 'dynamic_routes'));
+    $this->dumper->addRoutes($collection);
+    $this->dumper->dump(array('route_set' => $module));
+
     $this->lock->release('router_rebuild');
   }
 
diff --git a/core/lib/Drupal/Core/Routing/RoutingEvents.php b/core/lib/Drupal/Core/Routing/RoutingEvents.php
new file mode 100644
index 0000000..6a72ff0
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RoutingEvents.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\RoutingEvents.
+ */
+
+namespace Drupal\Core\Routing;
+
+/**
+ * Contains all events thrown in the core routing component.
+ */
+final class RoutingEvents {
+
+  /**
+   * The ALTER event is fired on a route collection to allow changes to routes.
+   *
+   * This event is used to process new routes before they get saved.
+   *
+   * @see Drupal\Core\Routing\RouteBuildEvent
+   *
+   * @var string
+   */
+  const ALTER = 'routing.route_alter';
+
+  /**
+   * The DYNAMIC event is fired to allow modules to register additional routes.
+   *
+   * Most routes are static, an should be defined as such. Dynamic routes are
+   * only those whose existence changes depending on the state of the system
+   * at runtime, depending on configuration.
+   *
+   * @see Drupal\Core\Routing\RouteBuildEvent
+   *
+   * @var string
+   */
+  const DYNAMIC = 'routing.route_dynamic';
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 2d09534..0965579 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -566,51 +566,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
 }
 
 /**
- * Defines routes in the new router system.
- *
- * A route is a Symfony Route object.  See the Symfony documentation for more
- * details on the available options.  Of specific note:
- *  - _controller: This is the PHP callable that will handle a request matching
- *              the route.
- *  - _content: This is the PHP callable that will handle the body of a request
- *              matching this route.  A default controller will provide the page
- *              rendering around it.
- *
- * Typically you will only specify one or the other of those properties.
- *
- * @deprecated
- *   This mechanism for registering routes is temporary. It will be replaced
- *   by a more robust mechanism in the near future.  It is documented here
- *   only for completeness.
- */
-function hook_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
-
-/**
  * Define menu items and page callbacks.
  *
  * This hook enables modules to register paths in order to define how URL
diff --git a/core/modules/system/tests/modules/router_test/router_test.module b/core/modules/system/tests/modules/router_test/router_test.module
index 4da939d..b3d9bbc 100644
--- a/core/modules/system/tests/modules/router_test/router_test.module
+++ b/core/modules/system/tests/modules/router_test/router_test.module
@@ -1,34 +1 @@
 <?php
-
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Routing\RouteCollection;
-
-/**
- * Implements hook_router_info().
- */
-function router_test_route_info() {
-  $collection = new RouteCollection();
-
-  $route = new Route('router_test/test1', array(
-    '_controller' => '\Drupal\router_test\TestControllers::test1'
-  ));
-  $collection->add('router_test_1', $route);
-
-  $route = new Route('router_test/test2', array(
-    '_content' => '\Drupal\router_test\TestControllers::test2'
-  ));
-  $collection->add('router_test_2', $route);
-
-  $route = new Route('router_test/test3/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test3'
-  ));
-  $collection->add('router_test_3', $route);
-
-  $route = new Route('router_test/test4/{value}', array(
-    '_content' => '\Drupal\router_test\TestControllers::test4',
-    'value' => 'narf',
-  ));
-  $collection->add('router_test_4', $route);
-
-  return $collection;
-}
diff --git a/core/modules/system/tests/modules/router_test/router_test.routing.yml b/core/modules/system/tests/modules/router_test/router_test.routing.yml
new file mode 100644
index 0000000..a49dd80
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/router_test.routing.yml
@@ -0,0 +1,20 @@
+router_test_1:
+  pattern: '/router_test/test1'
+  defaults:
+    _controller: '\Drupal\router_test\TestControllers::test1'
+
+router_test_2:
+  pattern: '/router_test/test2'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test2'
+
+router_test_3:
+  pattern: '/router_test/test3/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test3'
+
+router_test_4:
+  pattern: '/router_test/test4/{value}'
+  defaults:
+    _content: '\Drupal\router_test\TestControllers::test4'
+    value: 'narf'
