diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index a5d11fd..22ca9aa 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -8,12 +8,12 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheFactoryInterface;
 
 /**
  * Defines the cached storage.
  *
- * The class gets another storage and a cache backend injected. It reads from
+ * The class gets another storage and the cache factory injected. It reads from
  * the cache and delegates the read to the storage on a cache miss. It also
  * handles cache invalidation.
  */
@@ -27,6 +27,13 @@ class CachedStorage implements StorageInterface, StorageCacheInterface {
   protected $storage;
 
   /**
+   * The cache factory.
+   *
+   * @var \Drupal\Core\Cache\CacheFactoryInterface
+   */
+  protected $cacheFactory;
+
+  /**
    * The instantiated Cache backend.
    *
    * @var \Drupal\Core\Cache\CacheBackendInterface
@@ -45,12 +52,20 @@ class CachedStorage implements StorageInterface, StorageCacheInterface {
    *
    * @param \Drupal\Core\Config\StorageInterface $storage
    *   A configuration storage to be cached.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
-   *   A cache backend instance to use for caching.
+   * @param \Drupal\Core\Cache\CacheFactoryInterface $cache_factory
+   *   A cache factory used for getting cache backends.
    */
-  public function __construct(StorageInterface $storage, CacheBackendInterface $cache) {
+  public function __construct(StorageInterface $storage, CacheFactoryInterface $cache_factory) {
     $this->storage = $storage;
-    $this->cache = $cache;
+    $this->cacheFactory = $cache_factory;
+    $collection = $this->getCollectionName();
+    if (empty($collection)) {
+      $bin = 'config';
+    }
+    else {
+      $bin = 'config_' . str_replace('.', '_', $collection);
+    }
+    $this->cache = $this->cacheFactory->get($bin);
   }
 
   /**
@@ -238,4 +253,29 @@ public function deleteAll($prefix = '') {
   public function resetListCache() {
     $this->findByPrefixCache = array();
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createCollection($collection) {
+    return new static(
+      $this->storage->createCollection($collection),
+      $this->cacheFactory
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllCollectionNames() {
+    return $this->storage->getAllCollectionNames();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCollectionName() {
+    return $this->storage->getCollectionName();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php
index 6b36913..bc73819 100644
--- a/core/lib/Drupal/Core/Config/ConfigImporter.php
+++ b/core/lib/Drupal/Core/Config/ConfigImporter.php
@@ -185,7 +185,9 @@ public function __construct(StorageComparerInterface $storage_comparer, EventDis
     $this->moduleHandler = $module_handler;
     $this->themeHandler = $theme_handler;
     $this->stringTranslation = $string_translation;
-    $this->processedConfiguration = $this->storageComparer->getEmptyChangelist();
+    foreach ($this->storageComparer->getAllCollectionNames() as $collection) {
+      $this->processedConfiguration[$collection] = $this->storageComparer->getEmptyChangelist();
+    }
     $this->processedExtensions = $this->getEmptyExtensionsProcessedList();
   }
 
@@ -227,7 +229,9 @@ public function getStorageComparer() {
    */
   public function reset() {
     $this->storageComparer->reset();
-    $this->processedConfiguration = $this->storageComparer->getEmptyChangelist();
+    foreach ($this->storageComparer->getAllCollectionNames() as $collection) {
+      $this->processedConfiguration[$collection] = $this->storageComparer->getEmptyChangelist();
+    }
     $this->processedExtensions = $this->getEmptyExtensionsProcessedList();
     $this->createExtensionChangelist();
     $this->validated = FALSE;
@@ -257,16 +261,13 @@ protected function getEmptyExtensionsProcessedList() {
   /**
    * Checks if there are any unprocessed configuration changes.
    *
-   * @param array $ops
-   *   The operations to check for changes. Defaults to all operations, i.e.
-   *   array('delete', 'create', 'update', 'rename').
-   *
    * @return bool
    *   TRUE if there are changes to process and FALSE if not.
    */
-  public function hasUnprocessedConfigurationChanges($ops = array('delete', 'create', 'rename', 'update')) {
-    foreach ($ops as $op) {
-      if (count($this->getUnprocessedConfiguration($op))) {
+  public function hasUnprocessedConfigurationChanges() {
+    foreach ($this->storageComparer->getAllCollectionNames() as $collection)
+    foreach (array('delete', 'create', 'rename', 'update') as $op) {
+      if (count($this->getUnprocessedConfiguration($op, $collection))) {
         return TRUE;
       }
     }
@@ -276,23 +277,29 @@ public function hasUnprocessedConfigurationChanges($ops = array('delete', 'creat
   /**
    * Gets list of processed changes.
    *
+   * @param string $collection
+   *   (optional) The configuration collection to get processed changes for.
+   *   Defaults to the default collection.
+   *
    * @return array
    *   An array containing a list of processed changes.
    */
-  public function getProcessedConfiguration() {
-    return $this->processedConfiguration;
+  public function getProcessedConfiguration($collection = StorageInterface::DEFAULT_COLLECTION) {
+    return $this->processedConfiguration[$collection];
   }
 
   /**
    * Sets a change as processed.
    *
+   * @param string $collection
+   *   The configuration collection to set a change as processed for.
    * @param string $op
    *   The change operation performed, either delete, create, rename, or update.
    * @param string $name
    *   The name of the configuration processed.
    */
-  protected function setProcessedConfiguration($op, $name) {
-    $this->processedConfiguration[$op][] = $name;
+  protected function setProcessedConfiguration($collection, $op, $name) {
+    $this->processedConfiguration[$collection][$op][] = $name;
   }
 
   /**
@@ -301,12 +308,15 @@ protected function setProcessedConfiguration($op, $name) {
    * @param string $op
    *   The change operation to get the unprocessed list for, either delete,
    *   create, rename, or update.
+   * @param string $collection
+   *   (optional) The configuration collection to get unprocessed changes for.
+   *   Defaults to the default collection.
    *
    * @return array
    *   An array of configuration names.
    */
-  public function getUnprocessedConfiguration($op) {
-    return array_diff($this->storageComparer->getChangelist($op), $this->processedConfiguration[$op]);
+  public function getUnprocessedConfiguration($op, $collection = StorageInterface::DEFAULT_COLLECTION) {
+    return array_diff($this->storageComparer->getChangelist($op, $collection), $this->processedConfiguration[$collection][$op]);
   }
 
   /**
@@ -582,19 +592,29 @@ public function processConfigurations(array &$context) {
     // into account.
     if ($this->totalConfigurationToProcess == 0) {
       $this->storageComparer->reset();
-      foreach (array('delete', 'create', 'rename', 'update') as $op) {
-        foreach ($this->getUnprocessedConfiguration($op) as $name) {
-          $this->totalConfigurationToProcess += count($this->getUnprocessedConfiguration($op));
+      foreach ($this->storageComparer->getAllCollectionNames() as $collection) {
+        foreach (array('delete', 'create', 'rename', 'update') as $op) {
+          $this->totalConfigurationToProcess += count($this->getUnprocessedConfiguration($op, $collection));
         }
       }
     }
     $operation = $this->getNextConfigurationOperation();
     if (!empty($operation)) {
-      if ($this->checkOp($operation['op'], $operation['name'])) {
-        $this->processConfiguration($operation['op'], $operation['name']);
+      if ($this->checkOp($operation['collection'], $operation['op'], $operation['name'])) {
+        $this->processConfiguration($operation['collection'], $operation['op'], $operation['name']);
+      }
+      if ($operation['collection'] == StorageInterface::DEFAULT_COLLECTION) {
+        $context['message'] = $this->t('Synchronizing configuration: @op @name.', array('@op' => $operation['op'], '@name' => $operation['name']));
+      }
+      else {
+        $context['message'] = $this->t('Synchronizing configuration: @op @name in @collection.', array('@op' => $operation['op'], '@name' => $operation['name'], '@collection' => $operation['collection']));
+      }
+      $processed_count = 0;
+      foreach ($this->storageComparer->getAllCollectionNames() as $collection) {
+        foreach (array('delete', 'create', 'rename', 'update') as $op) {
+          $processed_count += count($this->processedConfiguration[$collection][$op]);
+        }
       }
-      $context['message'] = t('Synchronizing configuration: @op @name.', array('@op' => $operation['op'], '@name' => $operation['name']));
-      $processed_count = count($this->processedConfiguration['create']) + count($this->processedConfiguration['delete']) + count($this->processedConfiguration['update']);
       $context['finished'] = $processed_count / $this->totalConfigurationToProcess;
     }
     else {
@@ -658,13 +678,16 @@ protected function getNextExtensionOperation() {
   protected function getNextConfigurationOperation() {
     // The order configuration operations is processed is important. Deletes
     // have to come first so that recreates can work.
-    foreach (array('delete', 'create', 'rename', 'update') as $op) {
-      $config_names = $this->getUnprocessedConfiguration($op);
-      if (!empty($config_names)) {
-        return array(
-          'op' => $op,
-          'name' => array_shift($config_names),
-        );
+    foreach ($this->storageComparer->getAllCollectionNames() as $collection) {
+      foreach (array('delete', 'create', 'rename', 'update') as $op) {
+        $config_names = $this->getUnprocessedConfiguration($op, $collection);
+        if (!empty($config_names)) {
+          return array(
+            'op' => $op,
+            'name' => array_shift($config_names),
+            'collection' => $collection,
+          );
+        }
       }
     }
     return FALSE;
@@ -708,6 +731,8 @@ public function validate() {
   /**
    * Processes a configuration change.
    *
+   * @param string $collection
+   *   The configuration collection to process changes for.
    * @param string $op
    *   The change operation.
    * @param string $name
@@ -718,17 +743,21 @@ public function validate() {
    *   set, otherwise the exception message is logged and the configuration
    *   is skipped.
    */
-  protected function processConfiguration($op, $name) {
+  protected function processConfiguration($collection, $op, $name) {
     try {
-      if (!$this->importInvokeOwner($op, $name)) {
-        $this->importConfig($op, $name);
+      $processed = FALSE;
+      if ($this->storageComparer->supportsConfigurationEntities($collection)) {
+        $processed = $this->importInvokeOwner($collection, $op, $name);
+      }
+      if (!$processed) {
+        $this->importConfig($collection, $op, $name);
       }
     }
     catch (\Exception $e) {
       $this->logError($this->t('Unexpected error during import with operation @op for @name: @message', array('@op' => $op, '@name' => $name, '@message' => $e->getMessage())));
       // Error for that operation was logged, mark it as processed so that
       // the import can continue.
-      $this->setProcessedConfiguration($op, $name);
+      $this->setProcessedConfiguration($collection, $op, $name);
     }
   }
 
@@ -765,7 +794,7 @@ protected function processExtension($type, $op, $name) {
       // the default or admin theme is change this will be picked up whilst
       // processing configuration.
       if ($op == 'disable' && $this->processedSystemTheme === FALSE) {
-        $this->importConfig('update', 'system.theme');
+        $this->importConfig(StorageInterface::DEFAULT_COLLECTION, 'update', 'system.theme');
         $this->configManager->getConfigFactory()->reset('system.theme');
         $this->processedSystemTheme = TRUE;
       }
@@ -785,6 +814,8 @@ protected function processExtension($type, $op, $name) {
    * This method checks that the operation is still valid before processing a
    * configuration change.
    *
+   * @param string $collection
+   *   The configuration collection.
    * @param string $op
    *   The change operation.
    * @param string $name
@@ -795,10 +826,10 @@ protected function processExtension($type, $op, $name) {
    * @return bool
    *   TRUE is to continue processing, FALSE otherwise.
    */
-  protected function checkOp($op, $name) {
+  protected function checkOp($collection, $op, $name) {
     if ($op == 'rename') {
       $names = $this->storageComparer->extractRenameNames($name);
-      $target_exists = $this->storageComparer->getTargetStorage()->exists($names['new_name']);
+      $target_exists = $this->storageComparer->getTargetStorage($collection)->exists($names['new_name']);
       if ($target_exists) {
         // If the target exists, the rename has already occurred as the
         // result of a secondary configuration write. Change the operation
@@ -810,13 +841,13 @@ protected function checkOp($op, $name) {
       }
       return TRUE;
     }
-    $target_exists = $this->storageComparer->getTargetStorage()->exists($name);
+    $target_exists = $this->storageComparer->getTargetStorage($collection)->exists($name);
     switch ($op) {
       case 'delete':
         if (!$target_exists) {
           // The configuration has already been deleted. For example, a field
           // is automatically deleted if all the instances are.
-          $this->setProcessedConfiguration($op, $name);
+          $this->setProcessedConfiguration($collection, $op, $name);
           return FALSE;
         }
         break;
@@ -833,7 +864,7 @@ protected function checkOp($op, $name) {
             $this->logError($this->t('Deleted and replaced configuration entity "@name"', array('@name' => $name)));
           }
           else {
-            $this->storageComparer->getTargetStorage()->delete($name);
+            $this->storageComparer->getTargetStorage($collection)->delete($name);
             $this->logError($this->t('Deleted and replaced configuration "@name"', array('@name' => $name)));
           }
           return TRUE;
@@ -846,7 +877,7 @@ protected function checkOp($op, $name) {
           // Mark as processed so that the synchronisation continues. Once the
           // the current synchronisation is complete it will show up as a
           // create.
-          $this->setProcessedConfiguration($op, $name);
+          $this->setProcessedConfiguration($collection, $op, $name);
           return FALSE;
         }
         break;
@@ -857,22 +888,24 @@ protected function checkOp($op, $name) {
   /**
    * Writes a configuration change from the source to the target storage.
    *
+   * @param string $collection
+   *   The configuration collection.
    * @param string $op
    *   The change operation.
    * @param string $name
    *   The name of the configuration to process.
    */
-  protected function importConfig($op, $name) {
-    $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
+  protected function importConfig($collection, $op, $name) {
+    $config = new Config($name, $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager);
     if ($op == 'delete') {
       $config->delete();
     }
     else {
-      $data = $this->storageComparer->getSourceStorage()->read($name);
+      $data = $this->storageComparer->getSourceStorage($collection)->read($name);
       $config->setData($data ? $data : array());
       $config->save();
     }
-    $this->setProcessedConfiguration($op, $name);
+    $this->setProcessedConfiguration($collection, $op, $name);
   }
 
   /**
@@ -883,6 +916,8 @@ protected function importConfig($op, $name) {
    *
    * @todo Add support for other extension types; e.g., themes etc.
    *
+   * @param string $collection
+   *   The configuration collection.
    * @param string $op
    *   The change operation to get the unprocessed list for, either delete,
    *   create, rename, or update.
@@ -897,21 +932,21 @@ protected function importConfig($op, $name) {
    *   TRUE if the configuration was imported as a configuration entity. FALSE
    *   otherwise.
    */
-  protected function importInvokeOwner($op, $name) {
+  protected function importInvokeOwner($collection, $op, $name) {
     // Renames are handled separately.
     if ($op == 'rename') {
-      return $this->importInvokeRename($name);
+      return $this->importInvokeRename($collection, $name);
     }
     // Validate the configuration object name before importing it.
     // Config::validateName($name);
     if ($entity_type = $this->configManager->getEntityTypeIdByName($name)) {
-      $old_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
-      if ($old_data = $this->storageComparer->getTargetStorage()->read($name)) {
+      $old_config = new Config($name, $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager);
+      if ($old_data = $this->storageComparer->getTargetStorage($collection)->read($name)) {
         $old_config->initWithData($old_data);
       }
 
-      $data = $this->storageComparer->getSourceStorage()->read($name);
-      $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
+      $data = $this->storageComparer->getSourceStorage($collection)->read($name);
+      $new_config = new Config($name, $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager);
       if ($data !== FALSE) {
         $new_config->setData($data);
       }
@@ -924,7 +959,7 @@ protected function importInvokeOwner($op, $name) {
         throw new EntityStorageException(String::format('The entity storage "@storage" for the "@entity_type" entity type does not support imports', array('@storage' => get_class($entity_storage), '@entity_type' => $entity_type)));
       }
       $entity_storage->$method($name, $new_config, $old_config);
-      $this->setProcessedConfiguration($op, $name);
+      $this->setProcessedConfiguration($collection, $op, $name);
       return TRUE;
     }
     return FALSE;
@@ -933,6 +968,8 @@ protected function importInvokeOwner($op, $name) {
   /**
    * Imports a configuration entity rename.
    *
+   * @param string $collection
+   *   The configuration collection.
    * @param string $rename_name
    *   The rename configuration name, as provided by
    *   \Drupal\Core\Config\StorageComparer::createRenameName().
@@ -943,16 +980,16 @@ protected function importInvokeOwner($op, $name) {
    *
    * @see \Drupal\Core\Config\ConfigImporter::createRenameName()
    */
-  protected function importInvokeRename($rename_name) {
+  protected function importInvokeRename($collection, $rename_name) {
     $names = $this->storageComparer->extractRenameNames($rename_name);
     $entity_type_id = $this->configManager->getEntityTypeIdByName($names['old_name']);
-    $old_config = new Config($names['old_name'], $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
-    if ($old_data = $this->storageComparer->getTargetStorage()->read($names['old_name'])) {
+    $old_config = new Config($names['old_name'], $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager);
+    if ($old_data = $this->storageComparer->getTargetStorage($collection)->read($names['old_name'])) {
       $old_config->initWithData($old_data);
     }
 
-    $data = $this->storageComparer->getSourceStorage()->read($names['new_name']);
-    $new_config = new Config($names['new_name'], $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
+    $data = $this->storageComparer->getSourceStorage($collection)->read($names['new_name']);
+    $new_config = new Config($names['new_name'], $this->storageComparer->getTargetStorage($collection), $this->eventDispatcher, $this->typedConfigManager);
     if ($data !== FALSE) {
       $new_config->setData($data);
     }
@@ -964,7 +1001,7 @@ protected function importInvokeRename($rename_name) {
       throw new EntityStorageException(String::format('The entity storage "@storage" for the "@entity_type" entity type does not support imports', array('@storage' => get_class($entity_storage), '@entity_type' => $entity_type_id)));
     }
     $entity_storage->importRename($names['old_name'], $new_config, $old_config);
-    $this->setProcessedConfiguration('rename', $rename_name);
+    $this->setProcessedConfiguration($collection, 'rename', $rename_name);
     return TRUE;
   }
 
diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php
index 6faa23e..139aaf0 100644
--- a/core/lib/Drupal/Core/Config/ConfigManager.php
+++ b/core/lib/Drupal/Core/Config/ConfigManager.php
@@ -100,7 +100,11 @@ public function getConfigFactory() {
   /**
    * {@inheritdoc}
    */
-  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL) {
+  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) {
+    if ($collection != StorageInterface::DEFAULT_COLLECTION) {
+      $source_storage = $source_storage->createCollection($collection);
+      $target_storage = $target_storage->createCollection($collection);
+    }
     if (!isset($target_name)) {
       $target_name = $source_name;
     }
diff --git a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php
index 1d35637..a28a23d 100644
--- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php
+++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php
@@ -51,13 +51,16 @@ public function getConfigFactory();
    * @param string $target_name
    *   (optional) The name of the configuration object in the target storage.
    *   If omitted, the source name is used.
+   * @param string $collection
+   *   (optional) The configuration collection name. Defaults to the default
+   *   collection.
    *
    * @return core/lib/Drupal/Component/Diff
    *   A formatted string showing the difference between the two storages.
    *
    * @todo Make renderer injectable
    */
-  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL);
+  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL, $collection = StorageInterface::DEFAULT_COLLECTION);
 
   /**
    * Creates a configuration snapshot following a successful import.
diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php
index 11ab691..e973171 100644
--- a/core/lib/Drupal/Core/Config/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php
@@ -38,6 +38,13 @@ class DatabaseStorage implements StorageInterface {
   protected $options = array();
 
   /**
+   * The storage collection.
+   *
+   * @var string
+   */
+  protected $collection = '';
+
+  /**
    * Constructs a new DatabaseStorage.
    *
    * @param \Drupal\Core\Database\Connection $connection
@@ -46,11 +53,14 @@ class DatabaseStorage implements StorageInterface {
    *   A database table name to store configuration data in.
    * @param array $options
    *   (optional) Any additional database connection options to use in queries.
+   * @param string $collection
+   *   (optional) The collection to store configuration in.
    */
-  public function __construct(Connection $connection, $table, array $options = array()) {
+  public function __construct(Connection $connection, $table, array $options = array(), $collection = '') {
     $this->connection = $connection;
     $this->table = $table;
     $this->options = $options;
+    $this->collection = $collection;
   }
 
   /**
@@ -75,7 +85,7 @@ public function exists($name) {
   public function read($name) {
     $data = FALSE;
     try {
-      $raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', array(':name' => $name), $this->options)->fetchField();
+      $raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :name', array(':collection' => $this->collection, ':name' => $name), $this->options)->fetchField();
       if ($raw !== FALSE) {
         $data = $this->decode($raw);
       }
@@ -93,7 +103,7 @@ public function read($name) {
   public function readMultiple(array $names) {
     $list = array();
     try {
-      $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:names)', array(':names' => $names), $this->options)->fetchAllKeyed();
+      $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name IN (:names)', array(':collection' => $this->collection, ':names' => $names), $this->options)->fetchAllKeyed();
       foreach ($list as &$data) {
         $data = $this->decode($data);
       }
@@ -136,7 +146,7 @@ public function write($name, array $data) {
   protected function doWrite($name, $data) {
     $options = array('return' => Database::RETURN_AFFECTED) + $this->options;
     return (bool) $this->connection->merge($this->table, $options)
-      ->key('name', $name)
+      ->keys(array('collection', 'name'), array($this->collection, $name))
       ->fields(array('data' => $data))
       ->execute();
   }
@@ -176,8 +186,15 @@ protected static function schemaDefinition() {
     $schema = array(
       'description' => 'The base table for configuration data.',
       'fields' => array(
+        'collection' => array(
+          'description' => 'Primary Key: Config object collection.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
         'name' => array(
-          'description' => 'Primary Key: Unique config object name.',
+          'description' => 'Primary Key: Config object name.',
           'type' => 'varchar',
           'length' => 255,
           'not null' => TRUE,
@@ -190,7 +207,7 @@ protected static function schemaDefinition() {
           'size' => 'big',
         ),
       ),
-      'primary key' => array('name'),
+      'primary key' => array('collection', 'name'),
     );
     return $schema;
   }
@@ -205,6 +222,7 @@ protected static function schemaDefinition() {
   public function delete($name) {
     $options = array('return' => Database::RETURN_AFFECTED) + $this->options;
     return (bool) $this->connection->delete($this->table, $options)
+      ->condition('collection', $this->collection)
       ->condition('name', $name)
       ->execute();
   }
@@ -220,6 +238,7 @@ public function rename($name, $new_name) {
     return (bool) $this->connection->update($this->table, $options)
       ->fields(array('name' => $new_name))
       ->condition('name', $name)
+      ->condition('collection', $this->collection)
       ->execute();
   }
 
@@ -246,7 +265,8 @@ public function decode($raw) {
    */
   public function listAll($prefix = '') {
     try {
-      return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name LIKE :name', array(
+      return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name LIKE :name', array(
+        ':collection' => $this->collection,
         ':name' => $this->connection->escapeLike($prefix) . '%',
       ), $this->options)->fetchCol();
     }
@@ -263,10 +283,39 @@ public function deleteAll($prefix = '') {
       $options = array('return' => Database::RETURN_AFFECTED) + $this->options;
       return (bool) $this->connection->delete($this->table, $options)
         ->condition('name', $prefix . '%', 'LIKE')
+        ->condition('collection', $this->collection)
         ->execute();
     }
     catch (\Exception $e) {
       return FALSE;
     }
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createCollection($collection) {
+    return new static(
+      $this->connection,
+      $this->table,
+      $this->options,
+      $collection
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCollectionName() {
+    return $this->collection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllCollectionNames() {
+    return $this->connection->query('SELECT DISTINCT collection FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection <> \'\' ORDER by collection')->fetchCol();
+  }
+
+
 }
diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php
index 1dac0f4..2558959 100644
--- a/core/lib/Drupal/Core/Config/FileStorage.php
+++ b/core/lib/Drupal/Core/Config/FileStorage.php
@@ -17,6 +17,13 @@
 class FileStorage implements StorageInterface {
 
   /**
+   * The storage collection.
+   *
+   * @var string
+   */
+  protected $collection;
+
+  /**
    * The filesystem path for configuration objects.
    *
    * @var string
@@ -28,9 +35,12 @@ class FileStorage implements StorageInterface {
    *
    * @param string $directory
    *   A directory path to use for reading and writing of configuration files.
+   * @param string $collection
+   *   (optional) The collection to store configuration in.
    */
-  public function __construct($directory) {
+  public function __construct($directory, $collection = '') {
     $this->directory = $directory;
+    $this->collection = $collection;
   }
 
   /**
@@ -40,7 +50,7 @@ public function __construct($directory) {
    *   The path to the configuration file.
    */
   public function getFilePath($name) {
-    return $this->directory . '/' . $name . '.' . static::getFileExtension();
+    return $this->getCollectionDirectory() . '/' . $name . '.' . static::getFileExtension();
   }
 
   /**
@@ -57,10 +67,14 @@ public static function getFileExtension() {
    * Check if the directory exists and create it if not.
    */
   protected function ensureStorage() {
-    $success = file_prepare_directory($this->directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
-    $success = $success && file_save_htaccess($this->directory, TRUE, TRUE);
+    $dir = $this->getCollectionDirectory();
+    $success = file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    // Only create .htaccess file in root directory.
+    if ($dir == $this->directory) {
+      $success = $success && file_save_htaccess($this->directory, TRUE, TRUE);
+    }
     if (!$success) {
-      throw new StorageException("Failed to create config directory {$this->directory}");
+      throw new StorageException('Failed to create config directory ' . $dir);
     }
     return $this;
   }
@@ -142,12 +156,22 @@ public function write($name, array $data) {
    */
   public function delete($name) {
     if (!$this->exists($name)) {
-      if (!file_exists($this->directory)) {
-        throw new StorageException($this->directory . '/ not found.');
+      $dir = $this->getCollectionDirectory();
+      if (!file_exists($dir)) {
+        throw new StorageException($dir . '/ not found.');
       }
       return FALSE;
     }
-    return drupal_unlink($this->getFilePath($name));
+    $success = drupal_unlink($this->getFilePath($name));
+
+    // If a collection is now empty remove the directory.
+    if ($success && !empty($this->collection)) {
+      $names = $this->listAll();
+      if (empty($names)) {
+        drupal_rmdir($this->getCollectionDirectory());
+      }
+    }
+    return $success;
   }
 
   /**
@@ -186,12 +210,13 @@ public function decode($raw) {
   public function listAll($prefix = '') {
     // glob() silently ignores the error of a non-existing search directory,
     // even with the GLOB_ERR flag.
-    if (!file_exists($this->directory)) {
+    $dir = $this->getCollectionDirectory();
+    if (!file_exists($dir)) {
       return array();
     }
     $extension = '.' . static::getFileExtension();
     // \GlobIterator on Windows requires an absolute path.
-    $files = new \GlobIterator(realpath($this->directory) . '/' . $prefix . '*' . $extension);
+    $files = new \GlobIterator(realpath($dir) . '/' . $prefix . '*' . $extension);
 
     $names = array();
     foreach ($files as $file) {
@@ -215,4 +240,97 @@ public function deleteAll($prefix = '') {
 
     return $success;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createCollection($collection) {
+    return new static(
+      $this->directory,
+      $collection
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCollectionName() {
+    return $this->collection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllCollectionNames($directory = '') {
+    $collections = $this->getAllCollectionNamesHelper($this->directory);
+    sort($collections);
+    return $collections;
+  }
+
+  /**
+   * Helper function for getAllCollectionNames().
+   *
+   * If the file storage has the following subdirectory structure:
+   *   ./another_collection/one
+   *   ./another_collection/two
+   *   ./collection/sub/one
+   *   ./collection/sub/two
+   * this function will return:
+   * @code
+   *   array(
+   *     'another_collection.one',
+   *     'another_collection.two',
+   *     'collection.sub.one',
+   *     'collection.sub.two',
+   *   );
+   * @endcode
+   *
+   * @param string $directory
+   *   The directory to check for sub directories. This allows this
+   *   function to be used recursively to discover all the collections in the
+   *   storage.
+   *
+   * @return array
+   *   A list of collection names contained within the provided directory.
+   */
+  protected function getAllCollectionNamesHelper($directory) {
+    $collections = array();
+    foreach (new \DirectoryIterator($directory) as $fileinfo) {
+      if ($fileinfo->isDir() && !$fileinfo->isDot()) {
+        $collection = $fileinfo->getFilename();
+        // Discover if there are sub directories representing a dotted
+        // collection name.
+        $sub_collections = $this->getAllCollectionNamesHelper($directory . '/' . $collection);
+        if (!empty($sub_collections)) {
+          // Build up the collection name be concatenating the subdirectory
+          // names with the current directory name.
+          foreach ($sub_collections as $sub_collection) {
+            $collections[] = $collection . '.' . $sub_collection;
+          }
+        }
+        else {
+          //
+          $collections[] = $collection;
+        }
+      }
+    }
+    return $collections;
+  }
+
+  /**
+   * Gets the directory for the collection.
+   *
+   * @return string
+   *   The directory for the collection.
+   */
+  protected function getCollectionDirectory() {
+    if (empty($this->collection)) {
+      $dir = $this->directory;
+    }
+    else {
+      $dir = $this->directory . '/' . str_replace('.', '/', $this->collection);
+    }
+    return $dir;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/NullStorage.php b/core/lib/Drupal/Core/Config/NullStorage.php
index c66f718..5995da7 100644
--- a/core/lib/Drupal/Core/Config/NullStorage.php
+++ b/core/lib/Drupal/Core/Config/NullStorage.php
@@ -92,4 +92,26 @@ public function listAll($prefix = '') {
   public function deleteAll($prefix = '') {
     return FALSE;
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createCollection($collection) {
+    // No op.
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllCollectionNames() {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCollectionName() {
+    return '';
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/StorageComparer.php b/core/lib/Drupal/Core/Config/StorageComparer.php
index 8a6b9f1..0353206 100644
--- a/core/lib/Drupal/Core/Config/StorageComparer.php
+++ b/core/lib/Drupal/Core/Config/StorageComparer.php
@@ -23,6 +23,13 @@ class StorageComparer implements StorageComparerInterface {
   protected $sourceStorage;
 
   /**
+   * The source storages keyed by collection.
+   *
+   * @var \Drupal\Core\Config\StorageInterface[]
+   */
+  protected $sourceStorages;
+
+  /**
    * The target storage used to write configuration changes.
    *
    * @var \Drupal\Core\Config\StorageInterface
@@ -30,8 +37,17 @@ class StorageComparer implements StorageComparerInterface {
   protected $targetStorage;
 
   /**
+   * The target storages keyed by collection.
+   *
+   * @var \Drupal\Core\Config\StorageInterface[]
+   */
+  protected $targetStorages;
+
+  /**
    * List of changes to between the source storage and the target storage.
    *
+   * The list is keyed by storage collection name.
+   *
    * @var array
    */
   protected $changelist;
@@ -39,6 +55,8 @@ class StorageComparer implements StorageComparerInterface {
   /**
    * Sorted list of all the configuration object names in the source storage.
    *
+   * The list is keyed by storage collection name.
+   *
    * @var array
    */
   protected $sourceNames = array();
@@ -46,6 +64,8 @@ class StorageComparer implements StorageComparerInterface {
   /**
    * Sorted list of all the configuration object names in the target storage.
    *
+   * The list is keyed by storage collection name.
+   *
    * @var array
    */
   protected $targetNames = array();
@@ -53,6 +73,8 @@ class StorageComparer implements StorageComparerInterface {
   /**
    * The source configuration data keyed by name.
    *
+   * The data is keyed by storage collection name.
+   *
    * @var array
    */
   protected $sourceData = array();
@@ -60,6 +82,8 @@ class StorageComparer implements StorageComparerInterface {
   /**
    * The target configuration data keyed by name.
    *
+   * The data is keyed by storage collection name.
+   *
    * @var array
    */
   protected $targetData = array();
@@ -75,21 +99,37 @@ class StorageComparer implements StorageComparerInterface {
   public function __construct(StorageInterface $source_storage, StorageInterface $target_storage) {
     $this->sourceStorage = $source_storage;
     $this->targetStorage = $target_storage;
-    $this->changelist = $this->getEmptyChangelist();
+    $this->changelist[StorageInterface::DEFAULT_COLLECTION] = $this->getEmptyChangelist();
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getSourceStorage() {
-    return $this->sourceStorage;
+  public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
+    if (!isset($this->sourceStorages[$collection])) {
+      if ($collection == StorageInterface::DEFAULT_COLLECTION) {
+        $this->sourceStorages[$collection] = $this->sourceStorage;
+      }
+      else {
+        $this->sourceStorages[$collection] = $this->sourceStorage->createCollection($collection);
+      }
+    }
+    return $this->sourceStorages[$collection];
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getTargetStorage() {
-    return $this->targetStorage;
+  public function getTargetStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
+    if (!isset($this->targetStorages[$collection])) {
+      if ($collection == StorageInterface::DEFAULT_COLLECTION) {
+        $this->targetStorages[$collection] = $this->targetStorage;
+      }
+      else {
+        $this->targetStorages[$collection] = $this->targetStorage->createCollection($collection);
+      }
+    }
+    return $this->targetStorages[$collection];
   }
 
   /**
@@ -107,16 +147,18 @@ public function getEmptyChangelist() {
   /**
    * {@inheritdoc}
    */
-  public function getChangelist($op = NULL) {
+  public function getChangelist($op = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) {
     if ($op) {
-      return $this->changelist[$op];
+      return $this->changelist[$collection][$op];
     }
-    return $this->changelist;
+    return $this->changelist[$collection];
   }
 
   /**
    * Adds changes to the changelist.
    *
+   * @param string $collection
+   *   The storage collection to add changes for.
    * @param string $op
    *   The change operation performed. Either delete, create, rename, or update.
    * @param array $changes
@@ -125,16 +167,16 @@ public function getChangelist($op = NULL) {
    *   Array to sort that can be used to sort the changelist. This array must
    *   contain all the items that are in the change list.
    */
-  protected function addChangeList($op, array $changes, array $sort_order = NULL) {
+  protected function addChangeList($collection, $op, array $changes, array $sort_order = NULL) {
     // Only add changes that aren't already listed.
-    $changes = array_diff($changes, $this->changelist[$op]);
-    $this->changelist[$op] = array_merge($this->changelist[$op], $changes);
+    $changes = array_diff($changes, $this->changelist[$collection][$op]);
+    $this->changelist[$collection][$op] = array_merge($this->changelist[$collection][$op], $changes);
     if (isset($sort_order)) {
-      $count = count($this->changelist[$op]);
+      $count = count($this->changelist[$collection][$op]);
       // Sort the changlist in the same order as the $sort_order array and
       // ensure the array is keyed from 0.
-      $this->changelist[$op] = array_values(array_intersect($sort_order, $this->changelist[$op]));
-      if ($count != count($this->changelist[$op])) {
+      $this->changelist[$collection][$op] = array_values(array_intersect($sort_order, $this->changelist[$collection][$op]));
+      if ($count != count($this->changelist[$collection][$op])) {
         throw new \InvalidArgumentException(String::format('Sorting the @op changelist should not change its length.', array('@op' => $op)));
       }
     }
@@ -144,13 +186,20 @@ protected function addChangeList($op, array $changes, array $sort_order = NULL)
    * {@inheritdoc}
    */
   public function createChangelist() {
-    $this->getAndSortConfigData();
-    $this->addChangelistCreate();
-    $this->addChangelistUpdate();
-    $this->addChangelistDelete();
-    $this->addChangelistRename();
-    $this->sourceData = NULL;
-    $this->targetData = NULL;
+    foreach ($this->getAllCollectionNames() as $collection) {
+      $this->changelist[$collection] = $this->getEmptyChangelist();
+      $this->getAndSortConfigData($collection);
+      $this->addChangelistCreate($collection);
+      $this->addChangelistUpdate($collection);
+      $this->addChangelistDelete($collection);
+      // Only collections that support configuration entities can have renames.
+      if ($this->supportsConfigurationEntities($collection)) {
+        $this->addChangelistRename($collection);
+      }
+      // Only need data whilst calculating changelists. Free up the memory.
+      $this->sourceData = NULL;
+      $this->targetData = NULL;
+    }
     return $this;
   }
 
@@ -160,10 +209,13 @@ public function createChangelist() {
    * The list of deletes is sorted so that dependencies are deleted after
    * configuration entities that depend on them. For example, field instances
    * should be deleted after fields.
+   *
+   * @param string $collection
+   *   The storage collection to operate on.
    */
-  protected function addChangelistDelete() {
-    $deletes = array_diff(array_reverse($this->targetNames), $this->sourceNames);
-    $this->addChangeList('delete', $deletes);
+  protected function addChangelistDelete($collection) {
+    $deletes = array_diff(array_reverse($this->targetNames[$collection]), $this->sourceNames[$collection]);
+    $this->addChangeList($collection, 'delete', $deletes);
   }
 
   /**
@@ -172,10 +224,13 @@ protected function addChangelistDelete() {
    * The list of creates is sorted so that dependencies are created before
    * configuration entities that depend on them. For example, fields
    * should be created before field instances.
+   *
+   * @param string $collection
+   *   The storage collection to operate on.
    */
-  protected function addChangelistCreate() {
-    $creates = array_diff($this->sourceNames, $this->targetNames);
-    $this->addChangeList('create', $creates);
+  protected function addChangelistCreate($collection) {
+    $creates = array_diff($this->sourceNames[$collection], $this->targetNames[$collection]);
+    $this->addChangeList($collection, 'create', $creates);
   }
 
   /**
@@ -184,19 +239,22 @@ protected function addChangelistCreate() {
    * The list of updates is sorted so that dependencies are created before
    * configuration entities that depend on them. For example, fields
    * should be updated before field instances.
+   *
+   * @param string $collection
+   *   The storage collection to operate on.
    */
-  protected function addChangelistUpdate() {
+  protected function addChangelistUpdate($collection) {
     $recreates = array();
-    foreach (array_intersect($this->sourceNames, $this->targetNames) as $name) {
-      if ($this->sourceData[$name] !== $this->targetData[$name]) {
-        if (isset($this->sourceData[$name]['uuid']) && $this->sourceData[$name]['uuid'] != $this->targetData[$name]['uuid']) {
+    foreach (array_intersect($this->sourceNames[$collection], $this->targetNames[$collection]) as $name) {
+      if ($this->sourceData[$collection][$name] !== $this->targetData[$collection][$name]) {
+        if (isset($this->sourceData[$collection][$name]['uuid']) && $this->sourceData[$collection][$name]['uuid'] != $this->targetData[$collection][$name]['uuid']) {
           // The entity has the same file as an existing entity but the UUIDs do
           // not match. This means that the entity has been recreated so config
           // synchronisation should do the same.
           $recreates[] = $name;
         }
         else {
-          $this->addChangeList('update', array($name));
+          $this->addChangeList($collection, 'update', array($name));
         }
       }
     }
@@ -204,8 +262,8 @@ protected function addChangelistUpdate() {
     if (!empty($recreates)) {
       // Recreates should become deletes and creates. Deletes should be ordered
       // so that dependencies are deleted first.
-      $this->addChangeList('create', $recreates, $this->sourceNames);
-      $this->addChangeList('delete', $recreates, array_reverse($this->targetNames));
+      $this->addChangeList($collection, 'create', $recreates, $this->sourceNames[$collection]);
+      $this->addChangeList($collection, 'delete', $recreates, array_reverse($this->targetNames[$collection]));
 
     }
   }
@@ -216,17 +274,20 @@ protected function addChangelistUpdate() {
    * The list of renames is created from the different source and target names
    * with same UUID. These changes will be removed from the create and delete
    * lists.
+   *
+   * @param string $collection
+   *   The storage collection to operate on.
    */
-  protected function addChangelistRename() {
+  protected function addChangelistRename($collection) {
     // Renames will be present in both the create and delete lists.
-    $create_list = $this->getChangelist('create');
-    $delete_list = $this->getChangelist('delete');
+    $create_list = $this->getChangelist('create', $collection);
+    $delete_list = $this->getChangelist('delete', $collection);
     if (empty($create_list) || empty($delete_list)) {
       return;
     }
 
     $create_uuids = array();
-    foreach ($this->sourceData as $id => $data) {
+    foreach ($this->sourceData[$collection] as $id => $data) {
       if (isset($data['uuid']) && in_array($id, $create_list)) {
         $create_uuids[$data['uuid']] = $id;
       }
@@ -245,50 +306,52 @@ protected function addChangelistRename() {
     // Node type is a good example of a configuration entity that renames other
     // configuration when it is renamed.
     // @see \Drupal\node\Entity\NodeType::postSave()
-    foreach ($this->targetNames as $name) {
-      $data = $this->targetData[$name];
+    foreach ($this->targetNames[$collection] as $name) {
+      $data = $this->targetData[$collection][$name];
       if (isset($data['uuid']) && isset($create_uuids[$data['uuid']])) {
         // Remove the item from the create list.
-        $this->removeFromChangelist('create', $create_uuids[$data['uuid']]);
+        $this->removeFromChangelist($collection, 'create', $create_uuids[$data['uuid']]);
         // Remove the item from the delete list.
-        $this->removeFromChangelist('delete', $name);
+        $this->removeFromChangelist($collection, 'delete', $name);
         // Create the rename name.
         $renames[] = $this->createRenameName($name, $create_uuids[$data['uuid']]);
       }
     }
 
-    $this->addChangeList('rename', $renames);
+    $this->addChangeList($collection, 'rename', $renames);
   }
 
   /**
    * Removes the entry from the given operation changelist for the given name.
    *
+   * @param string $collection
+   *   The storage collection to operate on.
    * @param string $op
    *   The changelist to act on. Either delete, create, rename or update.
    * @param string $name
    *   The name of the configuration to remove.
    */
-  protected function removeFromChangelist($op, $name) {
-    $key = array_search($name, $this->changelist[$op]);
+  protected function removeFromChangelist($collection, $op, $name) {
+    $key = array_search($name, $this->changelist[$collection][$op]);
     if ($key !== FALSE) {
-      unset($this->changelist[$op][$key]);
+      unset($this->changelist[$collection][$op][$key]);
     }
   }
 
   /**
    * {@inheritdoc}
    */
-  public function moveRenameToUpdate($rename) {
+  public function moveRenameToUpdate($rename, $collection = StorageInterface::DEFAULT_COLLECTION) {
     $names = $this->extractRenameNames($rename);
-    $this->removeFromChangelist('rename', $rename);
-    $this->addChangeList('update', array($names['new_name']), $this->sourceNames);
+    $this->removeFromChangelist($collection, 'rename', $rename);
+    $this->addChangeList($collection, 'update', array($names['new_name']), $this->sourceNames[$collection]);
   }
 
   /**
    * {@inheritdoc}
    */
   public function reset() {
-    $this->changelist = $this->getEmptyChangelist();
+    $this->changelist = array(StorageInterface::DEFAULT_COLLECTION => $this->getEmptyChangelist());
     $this->sourceNames = $this->targetNames = array();
     return $this->createChangelist();
   }
@@ -296,10 +359,12 @@ public function reset() {
   /**
    * {@inheritdoc}
    */
-  public function hasChanges($ops = array('delete', 'create', 'update', 'rename')) {
-    foreach ($ops as $op) {
-      if (!empty($this->changelist[$op])) {
-        return TRUE;
+  public function hasChanges() {
+    foreach ($this->getAllCollectionNames() as $collection) {
+      foreach (array('delete', 'create', 'update', 'rename') as $op) {
+        if (!empty($this->changelist[$collection][$op])) {
+          return TRUE;
+        }
       }
     }
     return FALSE;
@@ -317,12 +382,22 @@ public function validateSiteUuid() {
   /**
    * Gets and sorts configuration data from the source and target storages.
    */
-  protected function getAndSortConfigData() {
-    $this->targetData = $this->targetStorage->readMultiple($this->targetStorage->listAll());
-    $this->sourceData = $this->sourceStorage->readMultiple($this->sourceStorage->listAll());
-    $dependency_manager = new ConfigDependencyManager();
-    $this->targetNames = $dependency_manager->setData($this->targetData)->sortAll();
-    $this->sourceNames = $dependency_manager->setData($this->sourceData)->sortAll();
+  protected function getAndSortConfigData($collection) {
+    $source_storage = $this->getSourceStorage($collection);
+    $target_storage = $this->getTargetStorage($collection);
+    $this->targetData[$collection] = $target_storage->readMultiple($target_storage->listAll());
+    $this->sourceData[$collection] = $source_storage->readMultiple($source_storage->listAll());
+    // Collections only support simple configuration therefore do not use
+    // configuration dependencies.
+    if ($this->supportsConfigurationEntities($collection)) {
+      $dependency_manager = new ConfigDependencyManager();
+      $this->targetNames[$collection] = $dependency_manager->setData($this->targetData[$collection])->sortAll();
+      $this->sourceNames[$collection] = $dependency_manager->setData($this->sourceData[$collection])->sortAll();
+    }
+    else {
+      $this->targetNames[$collection] = $target_storage->listAll();
+      $this->sourceNames[$collection] = $source_storage->listAll();
+    }
   }
 
   /**
@@ -352,4 +427,22 @@ public function extractRenameNames($name) {
       'new_name' => $names[1],
     );
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllCollectionNames($include_default = TRUE) {
+    $collections = array_unique(array_merge($this->sourceStorage->getAllCollectionNames(), $this->targetStorage->getAllCollectionNames()));
+    if ($include_default) {
+      array_unshift($collections, StorageInterface::DEFAULT_COLLECTION);
+    }
+    return $collections;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function supportsConfigurationEntities($collection) {
+    return $collection == StorageInterface::DEFAULT_COLLECTION;
+  }
 }
diff --git a/core/lib/Drupal/Core/Config/StorageComparerInterface.php b/core/lib/Drupal/Core/Config/StorageComparerInterface.php
index f85a8aa..f74d7a9 100644
--- a/core/lib/Drupal/Core/Config/StorageComparerInterface.php
+++ b/core/lib/Drupal/Core/Config/StorageComparerInterface.php
@@ -15,18 +15,26 @@
   /**
    * Gets the configuration source storage.
    *
+   * @param string $collection
+   *   (optional) The storage collection to use. Defaults to the
+   *   default collection.
+   *
    * @return \Drupal\Core\Config\StorageInterface
    *   Storage object used to read configuration.
    */
-  public function getSourceStorage();
+  public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION);
 
   /**
    * Gets the configuration target storage.
    *
+   * @param string $collection
+   *   (optional) The storage collection to use. Defaults to the
+   *   default collection.
+   *
    * @return \Drupal\Core\Config\StorageInterface
    *   Storage object used to write configuration.
    */
-  public function getTargetStorage();
+  public function getTargetStorage($collection = StorageInterface::DEFAULT_COLLECTION);
 
   /**
    * Gets an empty changelist.
@@ -42,11 +50,14 @@ public function getEmptyChangelist();
    * @param string $op
    *   (optional) A change operation. Either delete, create or update. If
    *   supplied the returned list will be limited to this operation.
+   * @param string $collection
+   *   (optional) The collection to get the changelist for. Defaults to the
+   *   default collection.
    *
    * @return array
    *   An array of config changes that are yet to be imported.
    */
-  public function getChangelist($op = NULL);
+  public function getChangelist($op = NULL, $collection = StorageInterface::DEFAULT_COLLECTION);
 
   /**
    * Recalculates the differences.
@@ -63,14 +74,10 @@ public function reset();
    *
    * @see \Drupal\Core\Config\StorageComparerInterface::createChangelist().
    *
-   * @param array $ops
-   *   The operations to check for changes. Defaults to all operations, i.e.
-   *   array('delete', 'create', 'update').
-   *
    * @return bool
    *   TRUE if there are changes to process and FALSE if not.
    */
-  public function hasChanges($ops = array('delete', 'create', 'update'));
+  public function hasChanges();
 
   /**
    * Validates that the system.site::uuid in the source and target match.
@@ -85,10 +92,13 @@ public function validateSiteUuid();
    *
    * @param string $rename
    *   The rename name, as provided by ConfigImporter::createRenameName().
+   * @param string $collection
+   *   (optional) The collection where the configuration is stored. Defaults to
+   *   the default collection.
    *
    * @see \Drupal\Core\Config\ConfigImporter::createRenameName()
    */
-  public function moveRenameToUpdate($rename);
+  public function moveRenameToUpdate($rename, $collection = StorageInterface::DEFAULT_COLLECTION);
 
   /**
    * Extracts old and new configuration names from a configuration change name.
@@ -106,4 +116,26 @@ public function moveRenameToUpdate($rename);
    */
   public function extractRenameNames($name);
 
+  /**
+   * Gets the existing collections from both the target and source storage.
+   *
+   * @param bool $include_default
+   *   (optional) Include the default unnamed collection. Defaults to TRUE.
+   *
+   * @return array
+   *   An array of existing collection names.
+   */
+  public function getAllCollectionNames($include_default = TRUE);
+
+  /**
+   * Determines if the provided collection supports configuration entities.
+   *
+   * @param string $collection
+   *   The collection to check.
+   *
+   * @return bool
+   *   TRUE if the collection support configuration entities, FALSE if not.
+   */
+  public function supportsConfigurationEntities($collection);
+
 }
diff --git a/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php
index 8b99784..a5ffb02 100644
--- a/core/lib/Drupal/Core/Config/StorageInterface.php
+++ b/core/lib/Drupal/Core/Config/StorageInterface.php
@@ -16,6 +16,11 @@
 interface StorageInterface {
 
   /**
+   * The default collection name.
+   */
+  const DEFAULT_COLLECTION = '';
+
+  /**
    * Returns whether a configuration object exists.
    *
    * @param string $name
@@ -156,4 +161,52 @@ public function listAll($prefix = '');
    */
   public function deleteAll($prefix = '');
 
+  /**
+   * Creates a collection on the storage.
+   *
+   * A configuration storage can contain multiple sets of configuration objects
+   * in partitioned collections. The collection name identifies the current
+   * collection used.
+   *
+   * Implementations of this method must provide a new instance to avoid side
+   * effects caused by the fact that Config objects have their storage injected.
+   *
+   * @param string $collection
+   *   The collection name. Valid collection names conform to the following
+   *   regex [a-zA-Z_.]. A storage does not need to have a collection set.
+   *   However, if a collection is set, then storage should use it to store
+   *   configuration in a way that allows retrieval of configuration for a
+   *   particular collection. Collections can be nested, for example
+   *   'language.de'. If this is a file system then we could create a language
+   *   folder with a subfolder named de. If this is a database table then there
+   *   could be a collection column which will store the value 'language.de'.
+   *   Storage nested levels should be consistent. If a collection with the name
+   *   'language.de' exists then a collections with the names 'language' or
+   *   'language.en.gb' should not be used.
+   *
+   * @return \Drupal\Core\Config\StorageInterface
+   *   An new instance of the storage backend with the collection set.
+   */
+  public function createCollection($collection);
+
+  /**
+   * Gets the existing collections.
+   *
+   * A configuration storage can contain multiple sets of configuration objects
+   * in partitioned collections. The collection key name identifies the current
+   * collection used.
+   *
+   * @return array
+   *   An array of existing collection names.
+   */
+  public function getAllCollectionNames();
+
+  /**
+   * Gets the name of the current collection the storage is using.
+   *
+   * @return string
+   *   The current collection name.
+   */
+  public function getCollectionName();
+
 }
diff --git a/core/modules/config/config.routing.yml b/core/modules/config/config.routing.yml
index f2a944a..0fdb30b 100644
--- a/core/modules/config/config.routing.yml
+++ b/core/modules/config/config.routing.yml
@@ -14,6 +14,14 @@ config.diff:
   requirements:
     _permission: 'synchronize configuration'
 
+config.diff_collection:
+  path: '/admin/config/development/configuration/sync/diff_collection/{collection}/{source_name}/{target_name}'
+  defaults:
+    _content: '\Drupal\config\Controller\ConfigController::diff'
+    target_name: NULL
+  requirements:
+    _permission: 'synchronize configuration'
+
 config.export_download:
   path: '/admin/config/development/configuration/full/export-download'
   defaults:
diff --git a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php
index 88cdeab..ff91637 100644
--- a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php
+++ b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php
@@ -85,9 +85,15 @@ public function downloadExport() {
     file_unmanaged_delete(file_directory_temp() . '/config.tar.gz');
 
     $archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz');
-    foreach (\Drupal::service('config.storage')->listAll() as $name) {
+    foreach ($this->targetStorage->listAll() as $name) {
       $archiver->addString("$name.yml", Yaml::encode(\Drupal::config($name)->get()));
     }
+    foreach ($this->targetStorage->getAllCollectionNames() as $collection) {
+      $collection_storage = $this->targetStorage->createCollection($collection);
+      foreach ($collection_storage->listAll() as $name) {
+        $archiver->addString(str_replace('.', '/', $collection) . "/$name.yml", Yaml::encode($collection_storage->read($name)));
+      }
+    }
 
     $request = new Request(array('file' => 'config.tar.gz'));
     return $this->fileDownloadController->download($request, 'temporary');
@@ -96,15 +102,23 @@ public function downloadExport() {
   /**
    * Shows diff of specificed configuration file.
    *
-   * @param string $config_file
+   * @param string $source_name
    *   The name of the configuration file.
+   * @param string $target_name
+   *   (optional) The name of the target configuration file if different from
+   *   the $source_name.
+   * @param string $collection
+   *   (optional) The configuration collection name. Defaults to the default
+   *   collection.
    *
    * @return string
    *   Table showing a two-way diff between the active and staged configuration.
    */
-  public function diff($source_name, $target_name = NULL) {
-
-    $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name);
+  public function diff($source_name, $target_name = NULL, $collection = NULL) {
+    if (!isset($collection)) {
+      $collection = StorageInterface::DEFAULT_COLLECTION;
+    }
+    $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name, $collection);
     $formatter = new \DrupalDiffFormatter();
     $formatter->show_header = FALSE;
 
diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
index a9eaac7..3a19912 100644
--- a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
+++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
@@ -184,71 +184,86 @@ public function buildForm(array $form, array &$form_state) {
     // Add the AJAX library to the form for dialog support.
     $form['#attached']['library'][] = 'core/drupal.ajax';
 
-    foreach ($storage_comparer->getChangelist() as $config_change_type => $config_names) {
-      if (empty($config_names)) {
-        continue;
-      }
-
-      // @todo A table caption would be more appropriate, but does not have the
-      //   visual importance of a heading.
-      $form[$config_change_type]['heading'] = array(
-        '#type' => 'html_tag',
-        '#tag' => 'h3',
-      );
-      switch ($config_change_type) {
-        case 'create':
-          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count new', '@count new');
-          break;
-
-        case 'update':
-          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count changed', '@count changed');
-          break;
-
-        case 'delete':
-          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count removed', '@count removed');
-          break;
-
-        case 'rename':
-          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count renamed', '@count renamed');
-          break;
+    foreach ($storage_comparer->getAllCollectionNames() as $collection) {
+      if ($collection != StorageInterface::DEFAULT_COLLECTION) {
+        $form[$collection]['collection_heading'] = array(
+          '#type' => 'html_tag',
+          '#tag' => 'h2',
+          '#value' => $this->t('!collection configuration collection', array('!collection' => $collection)),
+        );
       }
-      $form[$config_change_type]['list'] = array(
-        '#type' => 'table',
-        '#header' => array('Name', 'Operations'),
-      );
-
-      foreach ($config_names as $config_name) {
-        if ($config_change_type == 'rename') {
-          $names = $storage_comparer->extractRenameNames($config_name);
-          $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name']));
-          $config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name']));
+      foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) {
+        if (empty($config_names)) {
+          continue;
         }
-        else {
-          $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $config_name));
+
+        // @todo A table caption would be more appropriate, but does not have the
+        //   visual importance of a heading.
+        $form[$collection][$config_change_type]['heading'] = array(
+          '#type' => 'html_tag',
+          '#tag' => 'h3',
+        );
+        switch ($config_change_type) {
+          case 'create':
+            $form[$collection][$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count new', '@count new');
+            break;
+
+          case 'update':
+            $form[$collection][$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count changed', '@count changed');
+            break;
+
+          case 'delete':
+            $form[$collection][$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count removed', '@count removed');
+            break;
+
+          case 'rename':
+            $form[$collection][$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count renamed', '@count renamed');
+            break;
         }
-        $links['view_diff'] = array(
-          'title' => $this->t('View differences'),
-          'href' => $href,
-          'attributes' => array(
-            'class' => array('use-ajax'),
-            'data-accepts' => 'application/vnd.drupal-modal',
-            'data-dialog-options' => json_encode(array(
-              'width' => 700
-            )),
-          ),
+        $form[$collection][$config_change_type]['list'] = array(
+          '#type' => 'table',
+          '#header' => array('Name', 'Operations'),
         );
-        $form[$config_change_type]['list']['#rows'][] = array(
-          'name' => $config_name,
-          'operations' => array(
-            'data' => array(
-              '#type' => 'operations',
-              '#links' => $links,
+
+        foreach ($config_names as $config_name) {
+          if ($config_change_type == 'rename') {
+            $names = $storage_comparer->extractRenameNames($config_name);
+            $route_options = array('source_name' => $names['old_name'], 'target_name' => $names['new_name']);
+            $config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name']));
+          }
+          else {
+            $route_options = array('source_name' => $config_name);
+          }
+          if ($collection != StorageInterface::DEFAULT_COLLECTION) {
+            $route_options['collection'] = $collection;
+            $href = $this->urlGenerator->getPathFromRoute('config.diff_collection', $route_options);
+          }
+          else {
+            $href = $this->urlGenerator->getPathFromRoute('config.diff', $route_options);
+          }
+          $links['view_diff'] = array(
+            'title' => $this->t('View differences'),
+            'href' => $href,
+            'attributes' => array(
+              'class' => array('use-ajax'),
+              'data-accepts' => 'application/vnd.drupal-modal',
+              'data-dialog-options' => json_encode(array(
+                'width' => 700
+              )),
             ),
-          ),
-        );
+          );
+          $form[$collection][$config_change_type]['list']['#rows'][] = array(
+            'name' => $config_name,
+            'operations' => array(
+              'data' => array(
+                '#type' => 'operations',
+                '#links' => $links,
+              ),
+            ),
+          );
+        }
       }
     }
-
     return $form;
   }
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php
index 6f3b3de..138b861 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php
@@ -113,4 +113,36 @@ function testDiff() {
     $this->assertEqual(count($diff->edits), 2, 'There are two items in the diff.');
   }
 
+  /**
+   * Tests calculating the difference between two sets of config collections.
+   */
+  function testCollectionDiff() {
+    /** @var \Drupal\Core\Config\StorageInterface $active */
+    $active = $this->container->get('config.storage');
+    /** @var \Drupal\Core\Config\StorageInterface $staging */
+    $staging = $this->container->get('config.storage.staging');
+    $active_test_collection = $active->createCollection('test');
+    $staging_test_collection = $staging->createCollection('test');
+
+    $config_name = 'config_test.test';
+    $data = array('foo' => 'bar');
+
+    $active->write($config_name, $data);
+    $staging->write($config_name, $data);
+    $active_test_collection->write($config_name, $data);
+    $staging_test_collection->write($config_name, array('foo' => 'baz'));
+
+    // Test the fields match in the default collection diff.
+    $diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name);
+    $this->assertEqual($diff->edits[0]->type, 'copy',  'The first item in the diff is a copy.');
+    $this->assertEqual(count($diff->edits), 1, 'There is one item in the diff');
+
+    // Test that the differences are detected when diffing the collection.
+    $diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name, NULL, 'test');
+    $this->assertEqual($diff->edits[0]->type, 'change',  'The second item in the diff is a copy.');
+    $this->assertEqual($diff->edits[0]->orig, array('foo: bar'));
+    $this->assertEqual($diff->edits[0]->closing, array('foo: baz'));
+    $this->assertEqual($diff->edits[1]->type, 'copy',  'The second item in the diff is a copy.');
+  }
+
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php
index 0a01bd1..d9bc321 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\config\Tests;
 
+use Drupal\Core\Archiver\ArchiveTar;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -130,4 +131,84 @@ public function testExportImport() {
     $this->drupalGet('node/add');
     $this->assertFieldByName("{$this->field->name}[0][value]", '', 'Widget is displayed');
   }
+
+  /**
+   * Tests an export and import of collections.
+   */
+  public function testExportImportCollections() {
+
+    /** @var \Drupal\Core\Config\StorageInterface $active_storage */
+    $active_storage = \Drupal::service('config.storage');
+    $test1_storage = $active_storage->createCollection('collection.test1');
+    $test1_storage->write('config_test.create', array('foo' => 'bar'));
+    $test1_storage->write('config_test.update', array('foo' => 'bar'));
+    $test2_storage = $active_storage->createCollection('collection.test2');
+    $test2_storage->write('config_test.another_create', array('foo' => 'bar'));
+    $test2_storage->write('config_test.another_update', array('foo' => 'bar'));
+
+    // Export the configuration.
+    $this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export');
+    $this->tarball = $this->drupalGetContent();
+    $filename = file_directory_temp() .'/' . $this->randomName();
+    file_put_contents($filename, $this->tarball);
+
+    // Set up the active storage collections to test import.
+    $test1_storage->delete('config_test.create');
+    $test1_storage->write('config_test.update', array('foo' => 'baz'));
+    $test1_storage->write('config_test.delete', array('foo' => 'bar'));
+    $test2_storage->delete('config_test.another_create');
+    $test2_storage->write('config_test.another_update', array('foo' => 'baz'));
+    $test2_storage->write('config_test.another_delete', array('foo' => 'bar'));
+
+    // Create the tar contains the expected contect for the collections.
+    $tar = new ArchiveTar($filename, 'gz');
+    $content_list = $tar->listContent();
+    // Convert the list of files into something easy to search.
+    $files = array();
+    foreach ($content_list as $file) {
+      $files[] = $file['filename'];
+    }
+    $this->assertTrue(in_array('collection/test1/config_test.create.yml', $files), 'Config export contains collection/test1/config_test.create.yml.');
+    $this->assertTrue(in_array('collection/test2/config_test.another_create.yml', $files), 'Config export contains collection/test2/config_test.another_create.yml.');
+    $this->assertTrue(in_array('collection/test1/config_test.update.yml', $files), 'Config export contains collection/test1/config_test.update.yml.');
+    $this->assertTrue(in_array('collection/test2/config_test.another_update.yml', $files), 'Config export contains collection/test2/config_test.another_update.yml.');
+    $this->assertFalse(in_array('collection/test1/config_test.delete.yml', $files), 'Config export does not contain collection/test1/config_test.delete.yml.');
+    $this->assertFalse(in_array('collection/test2/config_test.another_delete.yml', $files), 'Config export does not contain collection/test2/config_test.another_delete.yml.');
+
+    $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload');
+    // Verify that there are configuration differences to import.
+    $this->drupalGet('admin/config/development/configuration');
+    $this->assertNoText(t('There are no configuration changes.'));
+    $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test1')));
+    $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test2')));
+    $this->assertText('config_test.create');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.create');
+    $this->assertText('config_test.update');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.update');
+    $this->assertText('config_test.delete');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.delete');
+    $this->assertText('config_test.another_create');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_create');
+    $this->assertText('config_test.another_update');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_update');
+    $this->assertText('config_test.another_delete');
+    $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_delete');
+
+    $this->drupalPostForm(NULL, array(), 'Import all');
+    $this->assertText(t('There are no configuration changes.'));
+
+    // Test data in collections.
+    $data = $test1_storage->read('config_test.create');
+    $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.create in collection.test1 has been created.');
+    $data = $test1_storage->read('config_test.update');
+    $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.update in collection.test1 has been updated.');
+    $this->assertFalse($test1_storage->read('config_test.delete'), 'The config_test.delete in collection.test1 has been updated.');
+
+    $data = $test2_storage->read('config_test.another_create');
+    $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_create in collection.test2 has been created.');
+    $data = $test2_storage->read('config_test.another_update');
+    $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_update in collection.test2 has been updated.');
+    $this->assertFalse($test2_storage->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 has been updated.');
+  }
+
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/CachedStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/CachedStorageTest.php
index cb00a1e..766d013 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/CachedStorageTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/CachedStorageTest.php
@@ -42,8 +42,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
     $this->filestorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
+    $this->storage = new CachedStorage($this->filestorage, \Drupal::service('cache_factory'));
     $this->cache = \Drupal::service('cache_factory')->get('config');
-    $this->storage = new CachedStorage($this->filestorage, $this->cache);
     // ::listAll() verifications require other configuration data to exist.
     $this->storage->write('system.performance', array());
   }
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
index c112ccd..420e406 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
@@ -181,6 +181,61 @@ function testDataTypes() {
     $this->assertIdentical($read_data, $data);
   }
 
+  /**
+   * Tests that the storage supports collections.
+   */
+  public function testCollection() {
+    $name = 'config_test.storage';
+    $data = array('foo' => 'bar');
+    $result = $this->storage->write($name, $data);
+    $this->assertIdentical($result, TRUE);
+    $this->assertIdentical($data, $this->storage->read($name));
+
+    // Create configuration in a new collection.
+    $new_storage = $this->storage->createCollection('collection.sub.new');
+    $this->assertEqual(array(), $new_storage->listAll());
+    $new_storage->write($name, $data);
+    $this->assertIdentical($result, TRUE);
+    $this->assertIdentical($data, $new_storage->read($name));
+    $this->assertEqual(array($name), $new_storage->listAll());
+    $new_data = array('foo' => 'baz');
+    $new_storage->write($name, $new_data);
+    $this->assertIdentical($result, TRUE);
+    $this->assertIdentical($new_data, $new_storage->read($name));
+
+    // Create configuration in another collection.
+    $another_storage = $this->storage->createCollection('collection.sub.another');
+    $this->assertEqual(array(), $another_storage->listAll());
+    $another_storage->write($name, $new_data);
+    $this->assertIdentical($result, TRUE);
+    $this->assertIdentical($new_data, $another_storage->read($name));
+    $this->assertEqual(array($name), $another_storage->listAll());
+
+    // Create configuration in yet another collection.
+    $alt_storage = $this->storage->createCollection('alternate');
+    $alt_storage->write($name, $new_data);
+    $this->assertIdentical($result, TRUE);
+    $this->assertIdentical($new_data, $alt_storage->read($name));
+
+    // Switch back to the collection-less mode and check the data still exists
+    // add has not been touched.
+    $this->assertIdentical($data, $this->storage->read($name));
+
+    // Check that the getAllCollectionNames() method works.
+    $this->assertIdentical(array('alternate', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
+
+    // Check that the collections are removed when they are empty.
+    $alt_storage->delete($name);
+    $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
+
+    // Check that the having an empty collection-less storage does not break
+    // anything. Before deleting check that the previous delete did not affect
+    // data in another collection.
+    $this->assertIdentical($data, $this->storage->read($name));
+    $this->storage->delete($name);
+    $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
+  }
+
   abstract protected function read($name);
 
   abstract protected function insert($name, $data);
@@ -188,4 +243,5 @@ function testDataTypes() {
   abstract protected function update($name, $data);
 
   abstract protected function delete($name);
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
index 6c60af4..a66d55b 100644
--- a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
@@ -6,7 +6,6 @@
 use Drupal\Core\Config\CachedStorage;
 use Drupal\Core\Cache\MemoryBackend;
 use Drupal\Core\Cache\NullBackend;
-use Drupal\Core\Cache\CacheBackendInterface;
 
 /**
  * Tests the interaction of cache and file storage in CachedStorage.
@@ -15,6 +14,11 @@
  */
 class CachedStorageTest extends UnitTestCase {
 
+  /**
+   * @var \Drupal\Core\Cache\CacheFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheFactory;
+
   public static function getInfo() {
     return array(
       'name' => 'Config cached storage test',
@@ -23,6 +27,10 @@ public static function getInfo() {
     );
   }
 
+  public function setUp() {
+    $this->cacheFactory = $this->getMock('Drupal\Core\Cache\CacheFactoryInterface');
+  }
+
   /**
    * Test listAll static cache.
    */
@@ -37,7 +45,11 @@ public function testListAllStaticCache() {
       ->will($this->returnValue($response));
 
     $cache = new NullBackend(__FUNCTION__);
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $this->assertEquals($response, $cachedStorage->listAll($prefix));
     $this->assertEquals($response, $cachedStorage->listAll($prefix));
   }
@@ -53,7 +65,11 @@ public function testListAllPrimedPersistentCache() {
     $response = array("$prefix." . $this->randomName(), "$prefix." . $this->randomName());
     $cache = new MemoryBackend(__FUNCTION__);
     $cache->set('find:' . $prefix, $response);
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $this->assertEquals($response, $cachedStorage->listAll($prefix));
   }
 
@@ -79,7 +95,11 @@ public function testGetMultipleOnPrimedCache() {
     foreach ($configCacheValues as $key => $value) {
       $cache->set($key, $value);
     }
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $this->assertEquals($configCacheValues, $cachedStorage->readMultiple($configNames));
   }
 
@@ -119,7 +139,11 @@ public function testGetMultipleOnPartiallyPrimedCache() {
       ->with(array(2 => $configNames[2], 4 => $configNames[4]))
       ->will($this->returnValue($response));
 
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $expected_data = $configCacheValues + array($configNames[2] => $config_exists_not_cached_data);
     $this->assertEquals($expected_data, $cachedStorage->readMultiple($configNames));
 
@@ -143,7 +167,11 @@ public function testReadNonExistentFileCacheMiss() {
             ->method('read')
             ->with($name)
             ->will($this->returnValue(FALSE));
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
 
     $this->assertFalse($cachedStorage->read($name));
 
@@ -163,7 +191,11 @@ public function testReadNonExistentFileCached() {
     $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
     $storage->expects($this->never())
             ->method('read');
-    $cachedStorage = new CachedStorage($storage, $cache);
+    $this->cacheFactory->expects($this->once())
+      ->method('get')
+      ->with('config')
+      ->will($this->returnValue($cache));
+    $cachedStorage = new CachedStorage($storage, $this->cacheFactory);
     $this->assertFalse($cachedStorage->read($name));
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Config/StorageComparerTest.php b/core/tests/Drupal/Tests/Core/Config/StorageComparerTest.php
index 500c06f..893a56e 100644
--- a/core/tests/Drupal/Tests/Core/Config/StorageComparerTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/StorageComparerTest.php
@@ -123,6 +123,12 @@ public function testCreateChangelistNoChange() {
     $this->targetStorage->expects($this->once())
       ->method('readMultiple')
       ->will($this->returnValue($config_data));
+    $this->sourceStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
+    $this->targetStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
 
     $this->storageComparer->createChangelist();
     $this->assertEmpty($this->storageComparer->getChangelist('create'));
@@ -151,6 +157,12 @@ public function testCreateChangelistCreate() {
     $this->targetStorage->expects($this->once())
       ->method('readMultiple')
       ->will($this->returnValue($target_data));
+    $this->sourceStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
+    $this->targetStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
 
     $this->storageComparer->createChangelist();
     $expected = array(
@@ -184,6 +196,12 @@ public function testCreateChangelistDelete() {
     $this->targetStorage->expects($this->once())
       ->method('readMultiple')
       ->will($this->returnValue($target_data));
+    $this->sourceStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
+    $this->targetStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
 
     $this->storageComparer->createChangelist();
     $expected = array(
@@ -217,6 +235,12 @@ public function testCreateChangelistUpdate() {
     $this->targetStorage->expects($this->once())
       ->method('readMultiple')
       ->will($this->returnValue($target_data));
+    $this->sourceStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
+    $this->targetStorage->expects($this->once())
+      ->method('getAllCollectionNames')
+      ->will($this->returnValue(array()));
 
     $this->storageComparer->createChangelist();
     $expected = array(
