diff --git a/.styleci.yml b/.styleci.yml
index 6232d56..c41d891 100644
--- a/.styleci.yml
+++ b/.styleci.yml
@@ -7,3 +7,5 @@ enabled:
 
 disabled:
   - unalign_equals
+  - braces
+  - property_separation
diff --git a/ApiDocGenerator.php b/ApiDocGenerator.php
index 822e8c1..4b7b796 100644
--- a/ApiDocGenerator.php
+++ b/ApiDocGenerator.php
@@ -32,11 +32,12 @@ final class ApiDocGenerator
      * @param DescriberInterface[]|iterable      $describers
      * @param ModelDescriberInterface[]|iterable $modelDescribers
      */
-    public function __construct($describers, $modelDescribers, CacheItemPoolInterface $cacheItemPool = null)
+    public function __construct($describers, $modelDescribers, CacheItemPoolInterface $cacheItemPool = null, string $cacheItemId = null)
     {
         $this->describers = $describers;
         $this->modelDescribers = $modelDescribers;
         $this->cacheItemPool = $cacheItemPool;
+        $this->cacheItemId = $cacheItemId;
     }
 
     public function generate(): Swagger
@@ -46,7 +47,7 @@ final class ApiDocGenerator
         }
 
         if ($this->cacheItemPool) {
-            $item = $this->cacheItemPool->getItem('swagger_doc');
+            $item = $this->cacheItemPool->getItem($this->cacheItemId ?? 'swagger_doc');
             if ($item->isHit()) {
                 return $this->swagger = $item->get();
             }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ffca06..d0497d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,21 @@ JMS Serializer
 SwaggerPHP
 * Handle `enum` and `default` properties from SwaggerPHP annotation
 
+Config
+* `nelmio_api_doc.routes` has been replaced by `nelmio_api_doc.areas`. Please update your config accordingly.
+
+  Before:
+  ```yml
+  nelmio_api_doc:
+      routes: [ path_patterns: [ /api ] ]
+  ```
+
+  After:
+  ```yml
+  nelmio_api_doc:
+      areas: [ path_patterns: [ /api ] ]
+  ```
+
 3.0.0 (2017-12-10)
 ------------------
 
diff --git a/Controller/DocumentationController.php b/Controller/DocumentationController.php
index 5129d04..2bc0915 100644
--- a/Controller/DocumentationController.php
+++ b/Controller/DocumentationController.php
@@ -12,21 +12,42 @@
 namespace Nelmio\ApiDocBundle\Controller;
 
 use Nelmio\ApiDocBundle\ApiDocGenerator;
+use Psr\Container\ContainerInterface;
+use Symfony\Component\DependencyInjection\ServiceLocator;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 
 final class DocumentationController
 {
-    private $apiDocGenerator;
+    private $generatorLocator;
 
-    public function __construct(ApiDocGenerator $apiDocGenerator)
+    /**
+     * @param ContainerInterface $generatorLocator
+     */
+    public function __construct($generatorLocator)
     {
-        $this->apiDocGenerator = $apiDocGenerator;
+        if (!$generatorLocator instanceof ContainerInterface) {
+            if (!$generatorLocator instanceof ApiDocGenerator) {
+                throw new \InvalidArgumentException(sprintf('Providing an instance of "%s" to "%s" is not supported.', get_class($generatorLocator), __METHOD__));
+            }
+
+            @trigger_error(sprintf('Providing an instance of "%s" to "%s()" is deprecated since version 3.1. Provide it an instance of "%s" instead.', ApiDocGenerator::class, __METHOD__, ContainerInterface::class), E_USER_DEPRECATED);
+            $generatorLocator = new ServiceLocator(['default' => function () use ($generatorLocator): ApiDocGenerator {
+                return $generatorLocator;
+            }]);
+        }
+
+        $this->generatorLocator = $generatorLocator;
     }
 
-    public function __invoke(Request $request)
+    public function __invoke(Request $request, $area = 'default')
     {
-        $spec = $this->apiDocGenerator->generate()->toArray();
+        if (!$this->generatorLocator->has($area)) {
+            throw new BadRequestHttpException(sprintf('Area "%s" is not supported.', $area));
+        }
+
+        $spec = $this->generatorLocator->get($area)->generate()->toArray();
         if ('' !== $request->getBaseUrl()) {
             $spec['basePath'] = $request->getBaseUrl();
         }
diff --git a/Controller/SwaggerUiController.php b/Controller/SwaggerUiController.php
index b435bb1..a8ccca3 100644
--- a/Controller/SwaggerUiController.php
+++ b/Controller/SwaggerUiController.php
@@ -12,24 +12,45 @@
 namespace Nelmio\ApiDocBundle\Controller;
 
 use Nelmio\ApiDocBundle\ApiDocGenerator;
+use Psr\Container\ContainerInterface;
+use Symfony\Component\DependencyInjection\ServiceLocator;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 
 final class SwaggerUiController
 {
-    private $apiDocGenerator;
+    private $generatorLocator;
 
     private $twig;
 
-    public function __construct(ApiDocGenerator $apiDocGenerator, \Twig_Environment $twig)
+    /**
+     * @param ContainerInterface $generatorLocator
+     */
+    public function __construct($generatorLocator, \Twig_Environment $twig)
     {
-        $this->apiDocGenerator = $apiDocGenerator;
+        if (!$generatorLocator instanceof ContainerInterface) {
+            if (!$generatorLocator instanceof ApiDocGenerator) {
+                throw new \InvalidArgumentException(sprintf('Providing an instance of "%s" to "%s" is not supported.', get_class($generatorLocator), __METHOD__));
+            }
+
+            @trigger_error(sprintf('Providing an instance of "%s" to "%s()" is deprecated since version 3.1. Provide it an instance of "%s" instead.', ApiDocGenerator::class, __METHOD__, ContainerInterface::class), E_USER_DEPRECATED);
+            $generatorLocator = new ServiceLocator(['default' => function () use ($generatorLocator): ApiDocGenerator {
+                return $generatorLocator;
+            }]);
+        }
+
+        $this->generatorLocator = $generatorLocator;
         $this->twig = $twig;
     }
 
-    public function __invoke(Request $request)
+    public function __invoke(Request $request, $area = 'default')
     {
-        $spec = $this->apiDocGenerator->generate()->toArray();
+        if (!$this->generatorLocator->has($area)) {
+            throw new BadRequestHttpException(sprintf('Area "%s" is not supported.', $area));
+        }
+
+        $spec = $this->generatorLocator->get($area)->generate()->toArray();
         if ('' !== $request->getBaseUrl()) {
             $spec['basePath'] = $request->getBaseUrl();
         }
diff --git a/DependencyInjection/Compiler/AddModelDescribersPass.php b/DependencyInjection/Compiler/AddModelDescribersPass.php
deleted file mode 100644
index 18094d4..0000000
--- a/DependencyInjection/Compiler/AddModelDescribersPass.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/*
- * This file is part of the NelmioApiDocBundle package.
- *
- * (c) Nelmio
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
-
-use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-/**
- * @internal
- */
-final class AddModelDescribersPass implements CompilerPassInterface
-{
-    use PriorityTaggedServiceTrait;
-
-    public function process(ContainerBuilder $container)
-    {
-        $modelDescribers = $this->findAndSortTaggedServices('nelmio_api_doc.model_describer', $container);
-
-        $container->getDefinition('nelmio_api_doc.generator')->replaceArgument(1, $modelDescribers);
-    }
-}
diff --git a/DependencyInjection/Compiler/AddRouteDescribersPass.php b/DependencyInjection/Compiler/AddRouteDescribersPass.php
deleted file mode 100644
index f29c59a..0000000
--- a/DependencyInjection/Compiler/AddRouteDescribersPass.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/*
- * This file is part of the NelmioApiDocBundle package.
- *
- * (c) Nelmio
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
-
-use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-
-/**
- * @internal
- */
-final class AddRouteDescribersPass implements CompilerPassInterface
-{
-    use PriorityTaggedServiceTrait;
-
-    public function process(ContainerBuilder $container)
-    {
-        $routeDescribers = $this->findAndSortTaggedServices('nelmio_api_doc.route_describer', $container);
-
-        $container->getDefinition('nelmio_api_doc.describers.route')->replaceArgument(2, $routeDescribers);
-    }
-}
diff --git a/DependencyInjection/Compiler/AddDescribersPass.php b/DependencyInjection/Compiler/TagDescribersPass.php
similarity index 51%
rename from DependencyInjection/Compiler/AddDescribersPass.php
rename to DependencyInjection/Compiler/TagDescribersPass.php
index ad2203b..cad60e8 100644
--- a/DependencyInjection/Compiler/AddDescribersPass.php
+++ b/DependencyInjection/Compiler/TagDescribersPass.php
@@ -12,20 +12,22 @@
 namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
 
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 /**
  * @internal
  */
-final class AddDescribersPass implements CompilerPassInterface
+final class TagDescribersPass implements CompilerPassInterface
 {
-    use PriorityTaggedServiceTrait;
-
     public function process(ContainerBuilder $container)
     {
-        $describers = $this->findAndSortTaggedServices('nelmio_api_doc.describer', $container);
-
-        $container->getDefinition('nelmio_api_doc.generator')->replaceArgument(0, $describers);
+        foreach ($container->findTaggedServiceIds('nelmio_api_doc.describer') as $id => $tags) {
+            $describer = $container->getDefinition($id);
+            foreach ($container->getParameter('nelmio_api_doc.areas') as $area) {
+                foreach ($tags as $tag) {
+                    $describer->addTag(sprintf('nelmio_api_doc.describer.%s', $area), $tag);
+                }
+            }
+        }
     }
 }
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 744b48f..8e1ee63 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -21,6 +21,24 @@ final class Configuration implements ConfigurationInterface
         $treeBuilder = new TreeBuilder();
         $treeBuilder
             ->root('nelmio_api_doc')
+            ->beforeNormalization()
+                ->ifTrue(function ($v) {
+                    return !isset($v['areas']) && isset($v['routes']);
+                })
+                ->then(function ($v) {
+                    $v['areas'] = $v['routes'];
+                    unset($v['routes']);
+                    @trigger_error('The `nelmio_api_doc.routes` config option is deprecated. Please use `nelmio_api_doc.areas` instead (just replace `routes` by `areas` in your config).', E_USER_DEPRECATED);
+
+                    return $v;
+                })
+            ->end()
+            ->beforeNormalization()
+                ->ifTrue(function ($v) {
+                    return isset($v['routes']);
+                })
+                ->thenInvalid('You must not use both `nelmio_api_doc.areas` and `nelmio_api_doc.routes` config options. Please update your config to only use `nelmio_api_doc.areas`.')
+            ->end()
             ->children()
                 ->arrayNode('documentation')
                     ->useAttributeAsKey('key')
@@ -28,13 +46,30 @@ final class Configuration implements ConfigurationInterface
                     ->example(['info' => ['title' => 'My App']])
                     ->prototype('variable')->end()
                 ->end()
-                ->arrayNode('routes')
+                ->arrayNode('areas')
                     ->info('Filter the routes that are documented')
-                    ->addDefaultsIfNotSet()
-                    ->children()
-                        ->arrayNode('path_patterns')
-                            ->example(['^/api', '^/api(?!/admin)'])
-                            ->prototype('scalar')->end()
+                    ->beforeNormalization()
+                        ->ifTrue(function ($v) {
+                            return empty($v) or isset($v['path_patterns']);
+                        })
+                        ->then(function ($v) {
+                            return ['default' => $v];
+                        })
+                    ->end()
+                    ->validate()
+                        ->ifTrue(function ($v) {
+                            return !isset($v['default']);
+                        })
+                        ->thenInvalid('You must specify a `default` area under `nelmio_api_doc.areas`.')
+                    ->end()
+                    ->useAttributeAsKey('name')
+                    ->prototype('array')
+                        ->addDefaultsIfNotSet()
+                        ->children()
+                            ->arrayNode('path_patterns')
+                                ->example(['^/api', '^/api(?!/admin)'])
+                                ->prototype('scalar')->end()
+                            ->end()
                         ->end()
                     ->end()
                 ->end()
diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php
index f5add0f..b966fb1 100644
--- a/DependencyInjection/NelmioApiDocExtension.php
+++ b/DependencyInjection/NelmioApiDocExtension.php
@@ -12,10 +12,14 @@
 namespace Nelmio\ApiDocBundle\DependencyInjection;
 
 use FOS\RestBundle\Controller\Annotations\ParamInterface;
+use Nelmio\ApiDocBundle\ApiDocGenerator;
+use Nelmio\ApiDocBundle\Describer\RouteDescriber;
+use Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber;
 use Nelmio\ApiDocBundle\ModelDescriber\JMSModelDescriber;
 use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
 use phpDocumentor\Reflection\DocBlockFactory;
 use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
@@ -54,22 +58,57 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
         $routesDefinition = (new Definition(RouteCollection::class))
             ->setFactory([new Reference('router'), 'getRouteCollection']);
 
-        if (0 === count($config['routes']['path_patterns'])) {
-            $container->setDefinition('nelmio_api_doc.routes', $routesDefinition)
-                ->setPublic(false);
-        } else {
-            $container->register('nelmio_api_doc.routes', RouteCollection::class)
+        $container->setParameter('nelmio_api_doc.areas', array_keys($config['areas']));
+        foreach ($config['areas'] as $area => $areaConfig) {
+            $container->register(sprintf('nelmio_api_doc.generator.%s', $area), ApiDocGenerator::class)
                 ->setPublic(false)
-                ->setFactory([
-                    (new Definition(FilteredRouteCollectionBuilder::class))
-                        ->addArgument($config['routes']['path_patterns']),
-                    'filter',
+                ->setArguments([
+                    new TaggedIteratorArgument(sprintf('nelmio_api_doc.describer.%s', $area)),
+                    new TaggedIteratorArgument('nelmio_api_doc.model_describer'),
+                ]);
+
+            if (0 === count($areaConfig['path_patterns'])) {
+                $container->setDefinition(sprintf('nelmio_api_doc.routes.%s', $area), $routesDefinition)
+                    ->setPublic(false);
+            } else {
+                $container->register(sprintf('nelmio_api_doc.routes.%s', $area), RouteCollection::class)
+                    ->setPublic(false)
+                    ->setFactory([
+                        (new Definition(FilteredRouteCollectionBuilder::class))
+                            ->addArgument($areaConfig['path_patterns']),
+                        'filter',
+                    ])
+                    ->addArgument($routesDefinition);
+            }
+
+            $container->register(sprintf('nelmio_api_doc.describers.route.%s', $area), RouteDescriber::class)
+                ->setPublic(false)
+                ->setArguments([
+                    new Reference(sprintf('nelmio_api_doc.routes.%s', $area)),
+                    new Reference('nelmio_api_doc.controller_reflector'),
+                    new TaggedIteratorArgument('nelmio_api_doc.route_describer'),
                 ])
-                ->addArgument($routesDefinition);
+                ->addTag(sprintf('nelmio_api_doc.describer.%s', $area), ['priority' => -400]);
+
+            $container->register(sprintf('nelmio_api_doc.describers.swagger_php.%s', $area), SwaggerPhpDescriber::class)
+                ->setPublic(false)
+                ->setArguments([
+                    new Reference(sprintf('nelmio_api_doc.routes.%s', $area)),
+                    new Reference('nelmio_api_doc.controller_reflector'),
+                    new Reference('annotation_reader'),
+                ])
+                ->addTag(sprintf('nelmio_api_doc.describer.%s', $area), ['priority' => -200]);
         }
 
+        $container->register('nelmio_api_doc.generator_locator')
+            ->setPublic(false)
+            ->addTag('container.service_locator')
+            ->addArgument(array_combine(
+                array_keys($config['areas']),
+                array_map(function ($area) { return new Reference(sprintf('nelmio_api_doc.generator.%s', $area)); }, array_keys($config['areas']))
+            ));
+
         // Import services needed for each library
-        $loader->load('swagger_php.xml');
         if (class_exists(DocBlockFactory::class)) {
             $loader->load('php_doc.xml');
         }
diff --git a/NelmioApiDocBundle.php b/NelmioApiDocBundle.php
index df48166..1d1791f 100644
--- a/NelmioApiDocBundle.php
+++ b/NelmioApiDocBundle.php
@@ -11,10 +11,8 @@
 
 namespace Nelmio\ApiDocBundle;
 
-use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddDescribersPass;
-use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddModelDescribersPass;
-use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddRouteDescribersPass;
 use Nelmio\ApiDocBundle\DependencyInjection\Compiler\ConfigurationPass;
+use Nelmio\ApiDocBundle\DependencyInjection\Compiler\TagDescribersPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 
@@ -26,8 +24,6 @@ final class NelmioApiDocBundle extends Bundle
     public function build(ContainerBuilder $container)
     {
         $container->addCompilerPass(new ConfigurationPass());
-        $container->addCompilerPass(new AddDescribersPass());
-        $container->addCompilerPass(new AddModelDescribersPass());
-        $container->addCompilerPass(new AddRouteDescribersPass());
+        $container->addCompilerPass(new TagDescribersPass());
     }
 }
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 401b78d..107ada3 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -6,19 +6,16 @@
     <services>
         <!-- Controllers -->
         <service id="nelmio_api_doc.controller.swagger_ui" class="Nelmio\ApiDocBundle\Controller\SwaggerUiController" public="true">
-            <argument type="service" id="nelmio_api_doc.generator" />
+            <argument type="service" id="nelmio_api_doc.generator_locator" />
             <argument type="service" id="twig" />
         </service>
 
         <service id="nelmio_api_doc.controller.swagger" class="Nelmio\ApiDocBundle\Controller\DocumentationController" public="true">
-            <argument type="service" id="nelmio_api_doc.generator" />
+            <argument type="service" id="nelmio_api_doc.generator_locator" />
         </service>
 
         <!-- Swagger Spec Generator -->
-        <service id="nelmio_api_doc.generator" class="Nelmio\ApiDocBundle\ApiDocGenerator" public="true">
-            <argument type="collection" /> <!-- Describers -->
-            <argument type="collection" /> <!-- Model Describers -->
-        </service>
+        <service id="nelmio_api_doc.generator" alias="nelmio_api_doc.generator.default" />
 
         <service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
             <argument type="service" id="service_container" />
@@ -32,14 +29,6 @@
             <tag name="nelmio_api_doc.describer" priority="1000" />
         </service>
 
-        <service id="nelmio_api_doc.describers.route" class="Nelmio\ApiDocBundle\Describer\RouteDescriber" public="false">
-            <argument type="service" id="nelmio_api_doc.routes" />
-            <argument type="service" id="nelmio_api_doc.controller_reflector" />
-            <argument type="collection" />
-
-            <tag name="nelmio_api_doc.describer" priority="-400" />
-        </service>
-
         <service id="nelmio_api_doc.describers.default" class="Nelmio\ApiDocBundle\Describer\DefaultDescriber" public="false">
             <tag name="nelmio_api_doc.describer" priority="-1000" />
         </service>
diff --git a/Resources/config/swagger_php.xml b/Resources/config/swagger_php.xml
deleted file mode 100644
index b2e249c..0000000
--- a/Resources/config/swagger_php.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" ?>
-<container xmlns="http://symfony.com/schema/dic/services"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
-    <services>
-        <service id="nelmio_api_doc.describers.swagger_php" class="Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber" public="false">
-            <argument type="service" id="nelmio_api_doc.routes" />
-            <argument type="service" id="nelmio_api_doc.controller_reflector" />
-            <argument type="service" id="annotation_reader" />
-
-            <tag name="nelmio_api_doc.describer" priority="-200" />
-        </service>
-    </services>
-
-</container>
diff --git a/Resources/doc/areas.rst b/Resources/doc/areas.rst
new file mode 100644
index 0000000..f996ca7
--- /dev/null
+++ b/Resources/doc/areas.rst
@@ -0,0 +1,48 @@
+Areas
+=====
+
+We've already seen that you can configure which routes are documented using ``nelmio_api_doc.areas``:
+
+.. code-block:: yaml
+
+    nelmio_api_doc:
+        areas:
+            path_patterns: [ ^/api ]
+
+But in fact, this config option is way more powerful and allows you to split your documentation in several parts.
+
+Configuration
+-------------
+
+You can define areas which will each generates a different documentation:
+
+.. code-block:: yaml
+
+    nelmio_api_doc:
+        areas:
+            default:
+                path_patterns: [ ^/api ]
+            internal:
+                path_patterns: [ ^/internal ]
+            commercial:
+                path_patterns: [ ^/commercial ]
+
+Your main documentation is under the ``default`` area. It's the one shown when accessing ``/api/doc``.
+
+Then update your routing to be able to access your different documentations:
+
+.. code-block:: yaml
+
+    # app/config/routing.yml
+    app.swagger_ui:
+        path: /api/doc/{area}
+        methods: GET
+        defaults: { _controller: nelmio_api_doc.controller.swagger_ui, area: default }
+
+    # To expose them as JSON
+    #app.swagger.areas:
+    #    path: /api/doc/{area}.json
+    #    methods: GET
+    #    defaults: { _controller: nelmio_api_doc.controller.swagger }
+
+That's all! You can now access ``/api/doc/internal`` and ``/api/doc/commercial``.
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
index a6c5290..106110d 100644
--- a/Resources/doc/index.rst
+++ b/Resources/doc/index.rst
@@ -1,6 +1,6 @@
 NelmioApiDocBundle
 ==================
-
+
 The **NelmioApiDocBundle** bundle allows you to generate documentation in the
 OpenAPI (Swagger) format and provides a sandbox to interactively browse the API documentation.
 
@@ -12,7 +12,7 @@ This bundle supports _Symfony_ route requirements, PHP annotations,
 [_FOSRestBundle_](https://github.com/FriendsOfSymfony/FOSRestBundle) annotations
 and apps using [_Api-Platform_](https://github.com/api-platform/api-platform).
 
-For models, it supports the Symfony serializer and the JMS serializer.
+For models, it supports the Symfony serializer and the JMS serializer.
 
 Migrate from 2.x to 3.0
 -----------------------
@@ -21,8 +21,8 @@ Migrate from 2.x to 3.0
 
 Installation
 ------------
-
-Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:
+
+Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:
 
 .. code-block:: bash
 
@@ -65,7 +65,7 @@ Open a command console, enter your project directory and execute the following c
             path: /api/doc.json
             methods: GET
             defaults: { _controller: nelmio_api_doc.controller.swagger }
-
+
     As you just installed the bundle, you'll likely see routes you don't want in
     your documentation such as `/_profiler/`. To fix this, you can filter the
     routes that are documented by configuring the bundle:
@@ -79,7 +79,7 @@ Open a command console, enter your project directory and execute the following c
                     - ^/api(?!/doc$)
 
 How does this bundle work?
---------------------------
+--------------------------
 
 It generates you a swagger documentation from your symfony app thanks to
 _Describers_. Each of these _Describers_ extract infos from various sources.
@@ -88,11 +88,11 @@ routes, etc.
 
 If you configured the ``app.swagger_ui`` route above, you can browse your
 documentation at `http://example.org/api/doc`.
-
+
 Using the bundle
 ----------------
 
-You can configure global information in the bundle configuration ``documentation.info`` section (take a look at
+You can configure global information in the bundle configuration ``documentation.info`` section (take a look at
 [the Swagger specification](http://swagger.io/specification/) to know the fields
 available):
 
@@ -151,7 +151,7 @@ Use models
 
 As shown in the example above, the bundle provides the ``@Model`` annotation.
 When you use it, the bundle will deduce your model properties.
-
+
 It has two options:
 
 * ``type`` to specify your model's type::
@@ -165,12 +165,12 @@ It has two options:
     /**
      * @Model(type=User::class, groups={"non_sensitive_data"})
      */
-
+
 .. warning::
 
     The ``@Model`` annotation acts like a ``@Schema`` annotation. If you nest it with a ``@Schema`` annotation, the bundle will consider that
     you're documenting an array of models.
-
+
     For instance, the following example::
 
         /**
@@ -215,7 +215,7 @@ It has two options:
         public function myAction()
         {
         }
-
+
 If you're not using the JMS Serializer
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -240,3 +240,13 @@ support in your config:
 
     nelmio_api_doc:
         models: { use_jms: false }
+
+Learn more
+----------
+
+If you need more complex features, take a look at:
+
+.. toctree::
+    :maxdepth: 1
+
+    areas
diff --git a/Tests/ApiDocGeneratorTest.php b/Tests/ApiDocGeneratorTest.php
new file mode 100644
index 0000000..8eac565
--- /dev/null
+++ b/Tests/ApiDocGeneratorTest.php
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle package.
+ *
+ * (c) Nelmio
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests;
+
+use Nelmio\ApiDocBundle\ApiDocGenerator;
+use Nelmio\ApiDocBundle\Describer\DefaultDescriber;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Cache\Adapter\ArrayAdapter;
+
+class ApiDocGeneratorTest extends TestCase
+{
+    public function testCache()
+    {
+        $adapter = new ArrayAdapter();
+        $generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter);
+
+        $this->assertEquals($generator->generate(), $adapter->getItem('swagger_doc')->get());
+    }
+
+    public function testCacheWithCustomId()
+    {
+        $adapter = new ArrayAdapter();
+        $generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter, 'custom_id');
+
+        $this->assertEquals($generator->generate(), $adapter->getItem('custom_id')->get());
+    }
+}
diff --git a/Tests/Controller/ControllersTest.php b/Tests/Controller/ControllersTest.php
new file mode 100644
index 0000000..d94f57d
--- /dev/null
+++ b/Tests/Controller/ControllersTest.php
@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle package.
+ *
+ * (c) Nelmio
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests\Controller;
+
+use Nelmio\ApiDocBundle\ApiDocGenerator;
+use Nelmio\ApiDocBundle\Controller\DocumentationController;
+use Nelmio\ApiDocBundle\Controller\SwaggerUiController;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+class ControllersTest extends TestCase
+{
+    /**
+     * @group legacy
+     * @expectedDeprecation Providing an instance of "Nelmio\ApiDocBundle\ApiDocGenerator" to "Nelmio\ApiDocBundle\Controller\SwaggerUiController::__construct()" is deprecated since version 3.1. Provide it an instance of "Psr\Container\ContainerInterface" instead.
+     */
+    public function testSwaggerUiControllerInstanciation()
+    {
+        $controller = new SwaggerUiController(new ApiDocGenerator([], []), $this->createMock('Twig_Environment'));
+        $controller(new Request());
+    }
+
+    /**
+     * @group legacy
+     * @expectedDeprecation Providing an instance of "Nelmio\ApiDocBundle\ApiDocGenerator" to "Nelmio\ApiDocBundle\Controller\DocumentationController::__construct()" is deprecated since version 3.1. Provide it an instance of "Psr\Container\ContainerInterface" instead.
+     */
+    public function testDocumentationControllerInstanciation()
+    {
+        $controller = new DocumentationController(new ApiDocGenerator([], []));
+        $controller(new Request());
+    }
+}
diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php
new file mode 100644
index 0000000..c506d90
--- /dev/null
+++ b/Tests/DependencyInjection/ConfigurationTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle package.
+ *
+ * (c) Nelmio
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests\DependencyInjection;
+
+use Nelmio\ApiDocBundle\DependencyInjection\Configuration;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Config\Definition\Processor;
+
+class ConfigurationTest extends TestCase
+{
+    public function testDefaultArea()
+    {
+        $processor = new Processor();
+        $config = $processor->processConfiguration(new Configuration(), [['areas' => ['path_patterns' => ['/foo']]]]);
+
+        $this->assertEquals(['default' => ['path_patterns' => ['/foo']]], $config['areas']);
+    }
+
+    public function testAreas()
+    {
+        $processor = new Processor();
+        $config = $processor->processConfiguration(new Configuration(), [['areas' => $areas = [
+            'default' => ['path_patterns' => ['/foo']],
+            'internal' => ['path_patterns' => ['/internal']],
+            'commercial' => ['path_patterns' => ['/internal']],
+        ]]]);
+
+        $this->assertEquals($areas, $config['areas']);
+    }
+
+    /**
+     * @group legacy
+     * @expectedException \InvalidArgumentException
+     * @expectedExceptionMessage You must not use both `nelmio_api_doc.areas` and `nelmio_api_doc.routes` config options. Please update your config to only use `nelmio_api_doc.areas`.
+     */
+    public function testBothAreasAndRoutes()
+    {
+        $processor = new Processor();
+        $config = $processor->processConfiguration(new Configuration(), [['areas' => [], 'routes' => []]]);
+    }
+
+    /**
+     * @group legacy
+     * @expectedDeprecation The `nelmio_api_doc.routes` config option is deprecated. Please use `nelmio_api_doc.areas` instead (just replace `routes` by `areas` in your config).
+     */
+    public function testDefaultConfig()
+    {
+        $processor = new Processor();
+        $config = $processor->processConfiguration(new Configuration(), [['routes' => ['path_patterns' => ['/foo']]]]);
+
+        $this->assertEquals(['default' => ['path_patterns' => ['/foo']]], $config['areas']);
+    }
+}
diff --git a/Tests/Functional/Controller/TestController.php b/Tests/Functional/Controller/TestController.php
new file mode 100644
index 0000000..ef3c80a
--- /dev/null
+++ b/Tests/Functional/Controller/TestController.php
@@ -0,0 +1,32 @@
+<?php
+
+/*
+ * This file is part of the NelmioApiDocBundle package.
+ *
+ * (c) Nelmio
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
+
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Swagger\Annotations as SWG;
+
+/**
+ * @Route("/test")
+ */
+class TestController
+{
+    /**
+     * @SWG\Response(
+     *     response="200",
+     *     description="Test"
+     * )
+     * @Route("/test/", methods={"GET"})
+     */
+    public function testAction()
+    {
+    }
+}
diff --git a/Tests/Functional/SwaggerUiTest.php b/Tests/Functional/SwaggerUiTest.php
index c8761ac..be004f5 100644
--- a/Tests/Functional/SwaggerUiTest.php
+++ b/Tests/Functional/SwaggerUiTest.php
@@ -18,19 +18,40 @@ class SwaggerUiTest extends WebTestCase
         return parent::createClient([], ['PHP_SELF' => '/app_dev.php/docs', 'SCRIPT_FILENAME' => '/var/www/app/web/app_dev.php']);
     }
 
-    public function testSwaggerUi()
+    /**
+     * @dataProvider areaProvider
+     */
+    public function testSwaggerUi($url, $area, $expected)
     {
         $client = self::createClient();
-        $crawler = $client->request('GET', '/app_dev.php/docs/');
+        $crawler = $client->request('GET', '/app_dev.php'.$url);
 
         $response = $client->getResponse();
         $this->assertEquals(200, $response->getStatusCode());
         $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('Content-Type'));
 
+        $this->assertEquals($expected, json_decode($crawler->filterXPath('//script[@id="swagger-data"]')->text(), true)['spec']);
+    }
+
+    public function areaProvider()
+    {
         $expected = $this->getSwaggerDefinition()->toArray();
         $expected['basePath'] = '/app_dev.php';
 
-        $this->assertEquals($expected, json_decode($crawler->filterXPath('//script[@id="swagger-data"]')->text(), true)['spec']);
+        yield ['/docs', 'default', $expected];
+
+        // Api-platform documentation
+        $expected['paths'] = [
+            '/api/dummies' => $expected['paths']['/api/dummies'],
+            '/api/foo' => $expected['paths']['/api/foo'],
+            '/api/dummies/{id}' => $expected['paths']['/api/dummies/{id}'],
+            '/test/test/' => ['get' => [
+                'responses' => ['200' => ['description' => 'Test']],
+            ]],
+        ];
+        $expected['definitions'] = ['Dummy' => $expected['definitions']['Dummy']];
+
+        yield ['/docs/test', 'test', $expected];
     }
 
     public function testJsonDocs()
diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php
index cfce559..22bd4b8 100644
--- a/Tests/Functional/TestKernel.php
+++ b/Tests/Functional/TestKernel.php
@@ -64,11 +64,11 @@ class TestKernel extends Kernel
      */
     protected function configureRoutes(RouteCollectionBuilder $routes)
     {
+        $routes->import(__DIR__.'/Controller/TestController.php', '/', 'annotation');
         $routes->import(__DIR__.'/Controller/ApiController.php', '/', 'annotation');
         $routes->import(__DIR__.'/Controller/UndocumentedController.php', '/', 'annotation');
         $routes->import('', '/api', 'api_platform');
-        $routes->import('@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml', '/docs');
-
+        $routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default');
         $routes->add('/docs.json', 'nelmio_api_doc.controller.swagger');
 
         if ($this->useJMS) {
@@ -110,8 +110,9 @@ class TestKernel extends Kernel
                     'title' => 'My Test App',
                 ],
             ],
-           'routes' => [
-                'path_patterns' => ['^/api(?!/admin)'],
+           'areas' => [
+               'default' => ['path_patterns' => ['^/api(?!/admin)']],
+               'test' => ['path_patterns' => ['^/test']],
             ],
         ]);
     }