diff --git a/src/php/ModelGenerator.php b/src/php/ModelGenerator.php
index c2543d9e73cda809da616040a693dbe95b7a0e6e..bc0f02ebfc645dd28d221062f0908bb3861e5a3a 100644
--- a/src/php/ModelGenerator.php
+++ b/src/php/ModelGenerator.php
@@ -30,17 +30,11 @@ use Irstea\NgModelGeneratorBundle\Metadata\ResourceMetadata;
 use Irstea\NgModelGeneratorBundle\Models\ClassName;
 use Irstea\NgModelGeneratorBundle\Models\Declaration;
 use Irstea\NgModelGeneratorBundle\Models\PHPClass;
-use Irstea\NgModelGeneratorBundle\Models\Types\Alias;
-use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
-use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Repository;
-use Irstea\NgModelGeneratorBundle\Models\Types\Resources\UUID;
-use Irstea\NgModelGeneratorBundle\Models\Types\StringConst;
 use Irstea\NgModelGeneratorBundle\Models\Types\Type;
-use Irstea\NgModelGeneratorBundle\Models\Types\Union;
 use Irstea\NgModelGeneratorBundle\Writers\MultiFileWriter;
-use Symfony\Component\PropertyInfo\Type as PHPType;
 use Twig\Environment;
 
 /**
@@ -57,24 +51,25 @@ final class ModelGenerator
     /** @var Documentation */
     private $documentation;
 
-    /** @var TypeFactoryInterface */
-    private $typeFactory;
-
     /** @var SerializationMapperFactoryInterface */
     private $serializationMapperFactory;
 
     /**
      * Serializer constructor.
      *
-     * @param MetadataFactoryInterface $metadataFactory
-     * @param Environment              $twigEnv
+     * @param MetadataFactoryInterface            $metadataFactory
+     * @param SerializationMapperFactoryInterface $serializationMapperFactory
+     * @param TypeFactoryInterface                $typeFactory
+     * @param Environment                         $twigEnv
      */
     public function __construct(
         MetadataFactoryInterface $metadataFactory,
+        SerializationMapperFactoryInterface $serializationMapperFactory,
         Environment $twigEnv
     ) {
         $this->metadataFactory = $metadataFactory;
         $this->twigEnv = $twigEnv;
+        $this->serializationMapperFactory = $serializationMapperFactory;
     }
 
     /**
@@ -83,10 +78,6 @@ final class ModelGenerator
      *
      * @param Documentation   $doc
      * @param MultiFileWriter $writer
-     *
-     * @throws \Twig_Error_Loader
-     * @throws \Twig_Error_Runtime
-     * @throws \Twig_Error_Syntax
      */
     public function generate(Documentation $doc, MultiFileWriter $writer): void
     {
@@ -107,17 +98,9 @@ final class ModelGenerator
 
     /**
      * @param MultiFileWriter $writer
-     *
-     * @throws \Twig_Error_Loader
-     * @throws \Twig_Error_Runtime
-     * @throws \Twig_Error_Syntax
      */
     private function doGenerate(MultiFileWriter $writer): void
     {
-        $this->typeFactory = $this->createTypeFactory();
-
-        $this->serializationMapperFactory = new SerializationMapperFactory($this->typeFactory);
-
         $context = [
             'title'        => $this->documentation->getTitle(),
             'version'      => $this->documentation->getVersion(),
@@ -167,10 +150,6 @@ final class ModelGenerator
 
     /**
      * @param MultiFileWriter $writer
-     *
-     * @throws \Twig_Error_Loader
-     * @throws \Twig_Error_Runtime
-     * @throws \Twig_Error_Syntax
      */
     private function generateFile(MultiFileWriter $writer, string $path, array $context): void
     {
@@ -185,72 +164,6 @@ final class ModelGenerator
         }
     }
 
-    /**
-     * Crée une usine à types contenant un certain nombre de types par défaut.
-     *
-     * @return TypeFactoryInterface
-     */
-    private function createTypeFactory(): TypeFactoryInterface
-    {
-        $factory = new TypeFactory();
-
-        foreach (['Array', 'Date', 'boolean', 'number', 'null', 'string', 'any', 'never'] as $builtin) {
-            $factory->add($builtin, BuiltinType::get($builtin));
-        }
-
-        $factory->add('UUID', UUID::get());
-        $factory->add('CommonFilters', $this->createCommonFilters('CommonFilters'));
-        $factory->add('Ordering', $this->createOrdering());
-
-        foreach ([
-            PHPType::BUILTIN_TYPE_ARRAY  => 'Array',
-            PHPType::BUILTIN_TYPE_BOOL   => 'boolean',
-            PHPType::BUILTIN_TYPE_FLOAT  => 'number',
-            PHPType::BUILTIN_TYPE_INT    => 'number',
-            PHPType::BUILTIN_TYPE_NULL   => 'null',
-            PHPType::BUILTIN_TYPE_STRING => 'string',
-            'Ramsey\Uuid\UuidInterface'  => 'UUID',
-            'DateTime'                   => 'string',
-            'DateTimeInterface'          => 'DateTime',
-            'DateTimeImmutable'          => 'DateTime',
-        ] as $alias => $target) {
-            if ($target === $alias || $factory->has($alias)) {
-                continue;
-            }
-            $factory->add($alias, $factory->get($target));
-        }
-
-        return $factory;
-    }
-
-    /**
-     * @param string $name
-     *
-     * @return Type
-     */
-    private function createCommonFilters(string $name): Type
-    {
-        $properties = [];
-
-        $meta = $this->metadataFactory->getPaginationMetadata();
-        if ($meta->isEnabled()) {
-            $properties[] = new Property($meta->getPageParameterName(), '', BuiltinType::get('number'), false, true);
-            if ($meta->isClientItemsPerPage()) {
-                $properties[] = new Property($meta->getItemsPerPageParameterName(), '', BuiltinType::get('number'), false, true);
-            }
-        }
-
-        return new InterfaceType($name, null, $properties);
-    }
-
-    /**
-     * @return Type
-     */
-    private function createOrdering(): Type
-    {
-        return new Alias('Ordering', Union::create([StringConst::get('asc'), StringConst::get('desc')]), 'Allowed values for ordering parameters');
-    }
-
     /**
      * Retourne un iterateur sur les métadonnées des ressources.
      *
@@ -274,13 +187,13 @@ final class ModelGenerator
     {
         $repositories = [];
 
-        // Premier passage pour prégénérer des référénces aux ressources
-        foreach ($this->getResourceMetadata() as $class => $resourceMeta) {
-            /* @var ClassName $class */
-            /* @var ResourceMetadata $resourceMeta */
-            $ref = $this->typeFactory->defer($class->getFullName());
-            $this->typeFactory->add($resourceMeta->getBaseName(), $ref);
-        }
+//        // Premier passage pour prégénérer des références aux ressources
+//        foreach ($this->getResourceMetadata() as $class => $resourceMeta) {
+//            /* @var ClassName $class */
+//            /* @var ResourceMetadata $resourceMeta */
+//            $ref = $this->typeFactory->defer($class->getFullName());
+//            $this->typeFactory->add($resourceMeta->getBaseName(), $ref);
+//        }
 
         // Maintenant on génére les repositories
         foreach ($this->getResourceMetadata() as $class => $resourceMeta) {
@@ -334,7 +247,7 @@ final class ModelGenerator
                 "No identifier found for %s (groups: [%s])\n",
                 $resourceMeta->getBaseName(),
                 implode(', ', $defaultNormalization->getGroups())
-                );
+            );
 
             return null;
         }
diff --git a/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php b/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php
new file mode 100644
index 0000000000000000000000000000000000000000..6bd6c84db710fa675774ac395630730c3f570755
--- /dev/null
+++ b/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php
@@ -0,0 +1,62 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class AbstractTypeFactoryDecorator.
+ */
+abstract class AbstractTypeFactoryDecorator implements TypeFactoryInterface
+{
+    /**
+     * @var TypeFactoryInterface
+     */
+    private $decorated;
+
+    /**
+     * AbstractTypeFactoryDecorator constructor.
+     *
+     * @param TypeFactoryInterface $decorated
+     */
+    public function __construct(TypeFactoryInterface $decorated)
+    {
+        $this->decorated = $decorated;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return $this->decorated->supportsType($type, $context);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        return $this->decorated->createType($type, $context);
+    }
+}
diff --git a/src/php/Models/Types/Factory/BuiltinTypeFactory.php b/src/php/Models/Types/Factory/BuiltinTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa27f52fe20539c09ba3254c4f235de18d998391
--- /dev/null
+++ b/src/php/Models/Types/Factory/BuiltinTypeFactory.php
@@ -0,0 +1,94 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Irstea\NgModelGeneratorBundle\Models\Types\Alias;
+use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Resources\UUID;
+use Irstea\NgModelGeneratorBundle\Models\Types\StringConst;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Irstea\NgModelGeneratorBundle\Models\Types\Union;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class BuiltinTypeFactory.
+ */
+class BuiltinTypeFactory implements TypeFactoryInterface
+{
+    /**
+     * @var array<string, Type>
+     */
+    private $types = [];
+
+    /**
+     * BuiltinTypeFactory constructor.
+     *
+     * @param array<string, Type> $types
+     */
+    public function __construct(array $types)
+    {
+        Assertion::allIsInstanceOf($types, Type::class);
+
+        $this->types = $types;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return \array_key_exists(TypeHelper::getTypeKey($type), $this->types);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        Assertion::true($this->supportsType($type, $context));
+
+        return $this->types[TypeHelper::getTypeKey($type)];
+    }
+
+    /**
+     * @return BuiltinTypeFactory
+     */
+    public static function createDefault(): BuiltinTypeFactory
+    {
+        $types = [
+            PropertyType::BUILTIN_TYPE_ARRAY  => BuiltinType::get('Array'),
+            PropertyType::BUILTIN_TYPE_BOOL   => BuiltinType::get('boolean'),
+            PropertyType::BUILTIN_TYPE_FLOAT  => BuiltinType::get('number'),
+            PropertyType::BUILTIN_TYPE_INT    => BuiltinType::get('number'),
+            PropertyType::BUILTIN_TYPE_NULL   => BuiltinType::get('null'),
+            PropertyType::BUILTIN_TYPE_STRING => BuiltinType::get('string'),
+            'Ramsey\Uuid\UuidInterface'       => UUID::get(),
+            'DateTime'                        => BuiltinType::get('string'),
+            'DateTimeInterface'               => BuiltinType::get('string'),
+            'DateTimeImmutable'               => BuiltinType::get('string'),
+            'Ordering'                        => new Alias('Ordering', Union::create([StringConst::get('asc'), StringConst::get('desc')]), 'Allowed values for ordering parameters'),
+        ];
+
+        return new BuiltinTypeFactory($types);
+    }
+}
diff --git a/src/php/Models/Types/Factory/CollectionTypeFactory.php b/src/php/Models/Types/Factory/CollectionTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e3ef073c7e8d6b514bc846ba504994c87b2fc39
--- /dev/null
+++ b/src/php/Models/Types/Factory/CollectionTypeFactory.php
@@ -0,0 +1,51 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType;
+use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class CollectionTypeFactory.
+ */
+final class CollectionTypeFactory
+{
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return $type->isCollection();
+    }
+
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        Assertion::true($this->supportsType($type, $context));
+
+        $valueType = $type->getCollectionValueType();
+        if (!$valueType) {
+            return BuiltinType::get('Array');
+        }
+
+        return new ArrayType($context->createType($valueType));
+    }
+}
diff --git a/src/php/Models/Types/Factory/CompositeTypeFactory.php b/src/php/Models/Types/Factory/CompositeTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..681940d1f8bc8cefdb9ccfdfcbff4fde5d331f99
--- /dev/null
+++ b/src/php/Models/Types/Factory/CompositeTypeFactory.php
@@ -0,0 +1,77 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Irstea\NgModelGeneratorBundle\Exceptions\TypeNotFoundException;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class CompositeTypeFactory.
+ */
+final class CompositeTypeFactory implements TypeFactoryInterface
+{
+    /**
+     * @var TypeFactoryInterface[]
+     */
+    private $typeFactories;
+
+    /**
+     * CompositeTypeFactory constructor.
+     *
+     * @param array $typeFactories
+     */
+    public function __construct(array $typeFactories)
+    {
+        Assertion::allIsInstanceOf($typeFactories, TypeFactoryInterface::class);
+        $this->typeFactories = $typeFactories;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        foreach ($this->typeFactories as $typeFactory) {
+            if ($typeFactory->supportsType($type, $context)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        foreach ($this->typeFactories as $typeFactory) {
+            if ($typeFactory->supportsType($type, $context)) {
+                return $typeFactory->createType($type, $context);
+            }
+        }
+
+        throw new TypeNotFoundException(TypeHelper::getTypeKey($type));
+    }
+}
diff --git a/src/php/Models/Types/Factory/Context.php b/src/php/Models/Types/Factory/Context.php
new file mode 100644
index 0000000000000000000000000000000000000000..2bcdb0015b09abfed1c1b60e144a7aafeb747e8e
--- /dev/null
+++ b/src/php/Models/Types/Factory/Context.php
@@ -0,0 +1,62 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class Context.
+ */
+final class Context implements ContextInterface
+{
+    /**
+     * @var TypeFactoryInterface
+     */
+    private $typeFactory;
+
+    /**
+     * Context constructor.
+     *
+     * @param TypeFactoryInterface $typeFactory
+     */
+    public function __construct(TypeFactoryInterface $typeFactory)
+    {
+        $this->typeFactory = $typeFactory;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type): bool
+    {
+        return $this->typeFactory->supportsType($type, $this);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type): Type
+    {
+        return $this->typeFactory->createType($type, $this);
+    }
+}
diff --git a/src/php/Models/Types/Factory/ContextInterface.php b/src/php/Models/Types/Factory/ContextInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..164a4ea96bddfd2a0986918108a4f482a9a5e78c
--- /dev/null
+++ b/src/php/Models/Types/Factory/ContextInterface.php
@@ -0,0 +1,45 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class ContextInterface.
+ */
+interface ContextInterface
+{
+    /**
+     * @param PropertyType $type
+     *
+     * @return bool
+     */
+    public function supportsType(PropertyType $type): bool;
+
+    /**
+     * @param PropertyType $type
+     *
+     * @return Type
+     */
+    public function createType(PropertyType $type): Type;
+}
diff --git a/src/php/Models/Types/Factory/DeferrableTypeFactory.php b/src/php/Models/Types/Factory/DeferrableTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd47ecc6290a3d48e4f68a456e21d225443d6295
--- /dev/null
+++ b/src/php/Models/Types/Factory/DeferrableTypeFactory.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Irstea\NgModelGeneratorBundle\Models\Types\Reference;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class DeferrableTypeFactory.
+ */
+final class DeferrableTypeFactory extends AbstractTypeFactoryDecorator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        $type = new Reference(TypeHelper::getTypeKey($type));
+
+        return $type;
+    }
+}
diff --git a/src/php/Models/Types/Factory/NullableTypeFactory.php b/src/php/Models/Types/Factory/NullableTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..7b986abba973b92c05789c764ea14bd4f3f576dc
--- /dev/null
+++ b/src/php/Models/Types/Factory/NullableTypeFactory.php
@@ -0,0 +1,52 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Irstea\NgModelGeneratorBundle\Models\Types\Union;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class NullableTypeFactory.
+ */
+final class NullableTypeFactory implements TypeFactoryInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return $type->isNullable();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        Assertion::true($this->supportsType($type, $context));
+
+        return Union::create([BuiltinType::get('null'), $context->createType(TypeHelper::notNullable($type))]);
+    }
+}
diff --git a/src/php/Models/Types/Factory/ReflectionTypeFactory.php b/src/php/Models/Types/Factory/ReflectionTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..76d75e0aed5f2082b7f13e9adf274a5d61898231
--- /dev/null
+++ b/src/php/Models/Types/Factory/ReflectionTypeFactory.php
@@ -0,0 +1,95 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Irstea\NgModelGeneratorBundle\Models\PHPClass;
+use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property;
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class ReflectionTypeFactory.
+ */
+final class ReflectionTypeFactory implements TypeFactoryInterface
+{
+    /**
+     * @var PropertyInfoExtractor
+     */
+    private $propertyInfoExtractor;
+
+    /**
+     * ReflectionTypeFactory constructor.
+     *
+     * @param PropertyInfoExtractor $propertyInfoExtractor
+     */
+    public function __construct(PropertyInfoExtractor $propertyInfoExtractor)
+    {
+        $this->propertyInfoExtractor = $propertyInfoExtractor;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return $type->getClassName() && \class_exists($type->getClassName());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        Assertion::true($this->supportsType($type, $context));
+
+        $fqcn = $type->getClassName();
+        $propertyNames = $this->propertyInfoExtractor->getProperties($fqcn);
+
+        $properties = [];
+
+        foreach ($propertyNames as $propertyName) {
+            $propertyTypes = $this->propertyInfoExtractor->getTypes($fqcn, $propertyName);
+            if (!isset($propertyTypes[0])) {
+                continue;
+            }
+
+            $propertyType = $propertyTypes[0];
+
+            $type = $context->createType($propertyType);
+
+            $properties[] = new Property(
+                $propertyName,
+                $this->propertyInfoExtractor->getShortDescription($fqcn, $propertyName) ?: '',
+                $type,
+                false,
+                $propertyType->isNullable(),
+                !$this->propertyInfoExtractor->isWritable($fqcn, $propertyName)
+                && !$this->propertyInfoExtractor->isInitializable($fqcn, $propertyName)
+            );
+        }
+
+        return new InterfaceType(PHPClass::get($fqcn)->getBaseName(), null, $properties);
+    }
+}
diff --git a/src/php/Models/Types/Factory/TypeCache.php b/src/php/Models/Types/Factory/TypeCache.php
new file mode 100644
index 0000000000000000000000000000000000000000..6455d4b8cd3a55d7eb8be2c27fb652cab8f692b8
--- /dev/null
+++ b/src/php/Models/Types/Factory/TypeCache.php
@@ -0,0 +1,57 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
+
+/**
+ * Class TypeCache.
+ */
+final class TypeCache extends AbstractTypeFactoryDecorator
+{
+    /**
+     * @var array
+     */
+    private $cache = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
+    {
+        return \array_key_exists(TypeHelper::getTypeKey($type), $this->cache) || parent::supportsType($type, $context);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createType(PropertyType $type, ContextInterface $context): Type
+    {
+        $key = TypeHelper::getTypeKey($type);
+        if (\array_key_exists($key, $this->cache)) {
+            return $this->cache[$key];
+        }
+
+        return $this->cache[$key] = parent::createType($type, $context);
+    }
+}
diff --git a/src/php/TypeFactoryInterface.php b/src/php/Models/Types/Factory/TypeFactoryInterface.php
similarity index 70%
rename from src/php/TypeFactoryInterface.php
rename to src/php/Models/Types/Factory/TypeFactoryInterface.php
index 4b2ae3f9e80eb61b8c3f3260c76ba3afd57dbeb1..085e6c4560e6b280bd65c05be9f108f56208b906 100644
--- a/src/php/TypeFactoryInterface.php
+++ b/src/php/Models/Types/Factory/TypeFactoryInterface.php
@@ -19,10 +19,10 @@
  * <https://www.gnu.org/licenses/>.
  */
 
-namespace Irstea\NgModelGeneratorBundle;
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
 
-use Irstea\NgModelGeneratorBundle\Models\Types\Deferred;
 use Irstea\NgModelGeneratorBundle\Models\Types\Type;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
 
 /**
  * Interface TypeFactoryInterface.
@@ -30,29 +30,18 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Type;
 interface TypeFactoryInterface
 {
     /**
-     * @param string $name
+     * @param PropertyType     $type
+     * @param ContextInterface $context
      *
      * @return bool
      */
-    public function has(string $name): bool;
+    public function supportsType(PropertyType $type, ContextInterface $context): bool;
 
     /**
-     * @param string $name
+     * @param PropertyType     $type
+     * @param ContextInterface $context
      *
      * @return Type
      */
-    public function get(string $name): Type;
-
-    /**
-     * @param string $name
-     *
-     * @return Deferred
-     */
-    public function defer(string $name): Deferred;
-
-    /**
-     * @param string $name
-     * @param Type   $type
-     */
-    public function add(string $name, Type $type): void;
+    public function createType(PropertyType $type, ContextInterface $context): Type;
 }
diff --git a/src/php/Models/Types/Factory/TypeHelper.php b/src/php/Models/Types/Factory/TypeHelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..9f7a3ad0744030c053c349eb31d1f06829aff8e8
--- /dev/null
+++ b/src/php/Models/Types/Factory/TypeHelper.php
@@ -0,0 +1,92 @@
+<?php declare(strict_types=1);
+/*
+ * This file is part of "irstea/ng-model-generator-bundle".
+ *
+ * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018-2019 IRSTEA
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License and the GNU
+ * Lesser General Public License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory;
+
+use Assert\Assertion;
+use Symfony\Component\PropertyInfo\Type;
+
+/**
+ * Class TypeHelper.
+ */
+final class TypeHelper
+{
+    /**
+     * Retourne une chaîne permettant d'identifier un type.
+     *
+     * @param Type $type
+     *
+     * @return string
+     */
+    public static function getTypeKey(Type $type): string
+    {
+        if ($type->isNullable()) {
+            return '?' . self::getTypeKey(self::notNullable($type));
+        }
+
+        if ($type->isCollection()) {
+            if ($type->getCollectionValueType()) {
+                return self::getTypeKey($type->getCollectionValueType()) . '[]';
+            }
+
+            return 'array';
+        }
+
+        if ($type->getClassName()) {
+            return trim($type->getClassName(), '\\');
+        }
+
+        return $type->getBuiltinType();
+    }
+
+    /**
+     * @param string $className
+     *
+     * @return Type
+     */
+    public static function fromClassName(string $className): Type
+    {
+        Assertion::classExists($className);
+
+        return new Type('object', false, $className);
+    }
+
+    /**
+     * @param Type $type
+     *
+     * @return Type
+     */
+    public static function notNullable(Type $type): Type
+    {
+        if (!$type->isNullable()) {
+            return $type;
+        }
+
+        return new Type(
+            $type->getBuiltinType(),
+            false,
+            $type->getClassName(),
+            $type->isCollection(),
+            $type->getCollectionKeyType(),
+            $type->getCollectionValueType()
+        );
+    }
+}
diff --git a/src/php/OperationMapper.php b/src/php/OperationMapper.php
index 20f5c91068239322b2d9f5f4ccc76810b02a6222..30c35ff095b3801ad50027fdf26c1a52f137f2d9 100644
--- a/src/php/OperationMapper.php
+++ b/src/php/OperationMapper.php
@@ -30,6 +30,7 @@ use Irstea\NgModelGeneratorBundle\Models\PHPClass;
 use Irstea\NgModelGeneratorBundle\Models\Types\AbstractCollection;
 use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType;
 use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
+use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\AnonymousObject;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property;
@@ -39,7 +40,6 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Path;
 use Irstea\NgModelGeneratorBundle\Models\Types\Placeholder;
 use Irstea\NgModelGeneratorBundle\Models\Types\Reference;
 use Irstea\NgModelGeneratorBundle\Models\Types\Resources\Collection;
-use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI;
 use Irstea\NgModelGeneratorBundle\Models\Types\Type;
 use Irstea\NgModelGeneratorBundle\Models\Types\Union;
 
@@ -182,7 +182,7 @@ final class OperationMapper
             !$this->operation->getOpDef()->isCreateItem()
         );
 
-        return $mapper->get($serializationMetadata->getRoot()->getFullName());
+        return $mapper->create($serializationMetadata->getRoot()->getFullName());
     }
 
     /**
@@ -250,7 +250,7 @@ final class OperationMapper
             $filterType = PHPClass::get(get_class($filter))->getBaseName();
 
             foreach ($filter->getDescription($this->operation->getResource()->getFullName()) as $name => $filterDesc) {
-                $type = $this->typeFactory->get($filterDesc['type']);
+                $type = $this->typeFactory->create($filterDesc['type']);
 
                 if (isset($filterDesc['property']) && is_string($filterDesc['property'])) {
                     $propType = $this->resolvePropertyType($rootClass, $filterDesc['property']);
@@ -304,7 +304,7 @@ final class OperationMapper
     private function resolvePropertyType(string $class, string $property): ?Type
     {
         /** @var AnonymousObject|null $type */
-        $type = $this->typeFactory->get($class)->findType(AnonymousObject::class);
+        $type = $this->typeFactory->create($class)->findType(AnonymousObject::class);
         if (!$type || !$type->hasProperty($property)) {
             return null;
         }
@@ -329,7 +329,7 @@ final class OperationMapper
                 break;
 
             case 'OrderFilter':
-               return $this->typeFactory->get('Ordering');
+               return $this->typeFactory->create('Ordering');
 
             case 'BooleanFilter':
             case 'ExistFilter':
diff --git a/src/php/Resources/config/config.xml b/src/php/Resources/config/config.xml
index 20f06f5db757685dc3c43df080208dfafae961f4..ea112ca83f74c454d444215de4427ded1a4223a3 100644
--- a/src/php/Resources/config/config.xml
+++ b/src/php/Resources/config/config.xml
@@ -6,8 +6,7 @@
 
     <services>
 
-        <service id="ng_model_generator.metadata.factory" lazy="true"
-                 class="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory">
+        <service id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" lazy="true">
             <argument type="service" id="api_platform.resource_class_resolver"/>
             <argument type="service" id="api_platform.metadata.resource.metadata_factory"/>
             <argument type="service" id="api_platform.metadata.property.name_collection_factory"/>
@@ -16,65 +15,69 @@
             <argument type="service" id="api_platform.operation_method_resolver"/>
             <argument type="service" id="router"/>
             <argument type="service" id="api_platform.filter_locator"/>
-            <argument type="service" id="ng_model_generator.metadata.pagination"/>
-            <argument type="service" id="irstea_ng_model_generator.metadata.resource_class_hierarchy"/>
+            <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata"/>
+            <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy"/>
         </service>
 
-        <service id="ng_model_generator.metadata.caching_factory" lazy="true" public="false"
-                 decorates="ng_model_generator.metadata.factory"
-                 class="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory">
-            <argument type="service" id="ng_model_generator.metadata.caching_factory.inner"/>
+        <service id="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory" lazy="true" public="false"
+                 decorates="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory">
+            <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory.inner"/>
             <argument type="service" id="doctrine_cache.providers.ng_model_generator_metadata_cache" on-invalid="null"/>
         </service>
 
-        <service id="irstea_ng_model_generator.metadata.resource_class_hierarchy"
-                 class="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy">
-            <argument id="irstea_ng_model_generator.resource_name_collection" type="service"/>
+        <service id="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy">
+            <argument id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection" type="service"/>
         </service>
 
-        <service id="ng_model_generator.metadata.pagination" lazy="true"
-                 class="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata">
+        <service id="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata" lazy="true">
             <argument>%api_platform.collection.pagination.enabled%</argument>
             <argument>%api_platform.collection.pagination.page_parameter_name%</argument>
             <argument>%api_platform.collection.pagination.client_items_per_page%</argument>
             <argument>%api_platform.collection.pagination.items_per_page_parameter_name%</argument>
         </service>
 
-        <service id="ng_model_generator.command.ng_model_generate_command" lazy="true"
-                 class="Irstea\NgModelGeneratorBundle\Command\NgModelGenerateCommand">
-            <argument type="service" id="irstea_ng_model_generator.model_generator"/>
-            <argument type="service" id="irstea_ng_model_generator.documentation"/>
+        <service id="Irstea\NgModelGeneratorBundle\Command\NgModelGenerateCommand" lazy="true">
+            <argument type="service" id="Irstea\NgModelGeneratorBundle\ModelGenerator"/>
+            <argument type="service" id="ApiPlatform\Core\Documentation\Documentation"/>
 
             <tag name="console.command"/>
         </service>
 
-        <service id="ng_model_generator.command.ng_model_metadata_command" lazy="true"
-                 class="Irstea\NgModelGeneratorBundle\Command\NgModelMetadataCommand">
-            <argument type="service" id="ng_model_generator.metadata.factory"/>
-            <argument type="service" id="irstea_ng_model_generator.documentation"/>
+        <service
+            id="Irstea\NgModelGeneratorBundle\Command\NgModelMetadataCommand"
+            lazy="true"
+        >
+            <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory"/>
+            <argument type="service" id="ApiPlatform\Core\Documentation\Documentation"/>
 
             <tag name="console.command"/>
         </service>
 
-        <service id="irstea_ng_model_generator.model_generator" lazy="true" class="Irstea\NgModelGeneratorBundle\ModelGenerator">
-            <argument id="ng_model_generator.metadata.factory" type="service"/>
+        <service id="Irstea\NgModelGeneratorBundle\ModelGenerator"
+                 lazy="true"
+        >
+            <argument id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" type="service"/>
+            <argument id="Irstea\NgModelGeneratorBundle\SerializationMapperFactory" type="service"/>
             <argument id="twig" type="service"/>
         </service>
 
-        <service id="irstea_ng_model_generator.documentation" lazy="true" class="ApiPlatform\Core\Documentation\Documentation">
-            <argument type="service" id="irstea_ng_model_generator.resource_name_collection"/>
+        <service id="ApiPlatform\Core\Documentation\Documentation" lazy="true">
+            <argument type="service" id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection"/>
             <argument>%api_platform.title%</argument>
             <argument>%api_platform.description%</argument>
             <argument>%api_platform.version%</argument>
             <argument>%api_platform.formats%</argument>
         </service>
 
-        <service id="irstea_ng_model_generator.resource_name_collection" lazy="true" public="false"
-                 class="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection">
+        <service id="Irstea\NgModelGeneratorBundle\Models\Types\Factory\BuiltinTypeProvider" lazy="true">
+            <argument id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" type="service"/>
+        </service>
+
+        <service id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection" lazy="true" public="false">
             <factory service="api_platform.metadata.resource.name_collection_factory" method="create"/>
         </service>
 
-        <service class="Irstea\NgModelGeneratorBundle\Twig\GeneratorExtension" id="irstea_ng_model_generator.twig.generator_extension">
+        <service id="Irstea\NgModelGeneratorBundle\Twig\GeneratorExtension">
             <tag name="twig.extension"/>
         </service>
 
diff --git a/src/php/SerializationMapper.php b/src/php/SerializationMapper.php
index a310632cac02d6b9611ce862c0d3e685cb5c4c44..67a92a3c6604d4eca08230b669e1e50a15085e49 100644
--- a/src/php/SerializationMapper.php
+++ b/src/php/SerializationMapper.php
@@ -30,9 +30,9 @@ use Irstea\NgModelGeneratorBundle\Models\ClassInfo;
 use Irstea\NgModelGeneratorBundle\Models\ClassName;
 use Irstea\NgModelGeneratorBundle\Models\PHPClass;
 use Irstea\NgModelGeneratorBundle\Models\Types\Alias;
-use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType;
-use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
-use Irstea\NgModelGeneratorBundle\Models\Types\Deferred;
+use Irstea\NgModelGeneratorBundle\Models\Types\Factory\ContextInterface;
+use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface;
+use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeHelper;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property;
 use Irstea\NgModelGeneratorBundle\Models\Types\Reference;
 use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI;
@@ -40,16 +40,13 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Resources\Representation;
 use Irstea\NgModelGeneratorBundle\Models\Types\StringConst;
 use Irstea\NgModelGeneratorBundle\Models\Types\Type;
 use Irstea\NgModelGeneratorBundle\Models\Types\Union;
-use Symfony\Component\PropertyInfo\Type as APIType;
+use Symfony\Component\PropertyInfo\Type as PropertyType;
 
 /**
  * Class SerializationMapper.
  */
 final class SerializationMapper implements TypeFactoryInterface
 {
-    /** @var TypeFactoryInterface */
-    private $typeFactory;
-
     /** @var SerializationMetadata */
     private $serialization;
 
@@ -62,16 +59,13 @@ final class SerializationMapper implements TypeFactoryInterface
     /**
      * SerializationMapper constructor.
      *
-     * @param TypeFactoryInterface  $typeFactory
      * @param SerializationMetadata $serialization
      * @param bool                  $withAtFields
      */
     public function __construct(
-        TypeFactoryInterface $typeFactory,
         SerializationMetadata $serialization,
         bool $withAtFields
     ) {
-        $this->typeFactory = $typeFactory;
         $this->serialization = $serialization;
         $this->withAtFields = $withAtFields;
     }
@@ -79,53 +73,32 @@ final class SerializationMapper implements TypeFactoryInterface
     /**
      * {@inheritdoc}
      */
-    public function has(string $name): bool
+    public function supportsType(PropertyType $type, ContextInterface $context): bool
     {
-        return $this->typeFactory->has($name);
-    }
+        $className = $type->getClassName();
 
-    /**
-     * {@inheritdoc}
-     */
-    public function add(string $name, Type $type): void
-    {
-        $this->typeFactory->add($name, $type);
+        return $className && $this->serialization->hasRepresentationOf(PHPClass::get($className));
     }
 
     /**
      * {@inheritdoc}
      */
-    public function defer(string $name): Deferred
+    public function createType(PropertyType $type, ContextInterface $context): Type
     {
-        return $this->typeFactory->defer($name);
-    }
+        Assertion::true($this->createType($type, $context));
 
-    /**
-     * {@inheritdoc}
-     */
-    public function get(string $name): Type
-    {
-        if (class_exists($name)) {
-            $class = PHPClass::get($name);
-            if ($this->serialization->hasRepresentationOf($class)) {
-                $repr = $this->serialization->getRepresentationOf($class);
-
-                return $this->defer($repr->getName())
-                    ->resolveWith(
-                        function () use ($repr) {
-                            return $this->mapRepresentation($repr);
-                        }
-                    );
-            }
-        }
+        $class = PHPClass::get($type->getClassName());
+        $repr = $this->serialization->getRepresentationOf($class);
 
-        return $this->typeFactory->get($name);
+        return $this->mapRepresentation($repr, $context);
     }
 
     /**
+     * @param ContextInterface $context
+     *
      * @return array
      */
-    public function getResourceData(): array
+    public function getResourceData(ContextInterface $context): array
     {
         $resource = $this->serialization->getRoot();
         $resourceName = $resource->getFullName();
@@ -142,15 +115,16 @@ final class SerializationMapper implements TypeFactoryInterface
 
         $identifier = $identifiers ? array_shift($identifiers) : null;
 
-        return [$this->get($resourceName), $identifier, $properties];
+        return [$context->createType(TypeHelper::fromClassName($resourceName)), $identifier, $properties];
     }
 
     /**
      * @param RepresentationMetadata $repr
+     * @param ContextInterface       $context
      *
      * @return Type
      */
-    private function mapRepresentation(RepresentationMetadata $repr): Type
+    private function mapRepresentation(RepresentationMetadata $repr, ContextInterface $context): Type
     {
         $classInfo = $this->getClassInfo($repr);
 
@@ -168,7 +142,7 @@ final class SerializationMapper implements TypeFactoryInterface
             /** @var Type[] $types */
             $types = [];
             foreach ($classInfo->iterateInterfaceDescendants() as $child) {
-                $types[] = $this->get($child->getFullName());
+                $types[] = $context->createType(TypeHelper::fromClassName($child->getFullName()));
             }
 
             switch (\count($types)) {
@@ -185,22 +159,22 @@ final class SerializationMapper implements TypeFactoryInterface
         }
 
         if ($classInfo->isIRI()) {
-            return $this->createIRI([$classInfo]);
+            return $this->createIRI([$classInfo], $context);
         }
 
         $parent = null;
         $parentInfo = $classInfo->getParent();
         if ($parentInfo !== null && $parentInfo->isInterface()) {
-            $parent = $this->get($parentInfo->getFullName());
+            $parent = $context->createType(TypeHelper::fromClassName($parentInfo->getFullName()));
         }
 
-        $properties = $this->mapProperties($classInfo);
+        $properties = $this->mapProperties($classInfo, $context);
 
         $children = [];
         /* @var ClassName $class */
         foreach ($classInfo->getChildren() as $child) {
             if ($child->isInterface()) {
-                $children[] = $this->get($child->getFullName());
+                $children[] = $context->createType(TypeHelper::fromClassName($child->getFullName()));
             }
         }
 
@@ -208,16 +182,17 @@ final class SerializationMapper implements TypeFactoryInterface
     }
 
     /**
-     * @param array $resources
+     * @param array            $resources
+     * @param ContextInterface $context
      *
      * @return IRI
      */
-    private function createIRI(array $resources): IRI
+    private function createIRI(array $resources, ContextInterface $context): IRI
     {
         return new IRI(
             array_map(
-                function (ClassName $class) {
-                    return $this->typeFactory->get($class->getFullName());
+                function (ClassName $class) use ($context) {
+                    return $context->createType(TypeHelper::fromClassName($class->getFullName()));
                 },
                 $resources
             )
@@ -225,11 +200,12 @@ final class SerializationMapper implements TypeFactoryInterface
     }
 
     /**
-     * @param ClassInfo $classInfo
+     * @param ClassInfo        $classInfo
+     * @param ContextInterface $context
      *
      * @return array
      */
-    private function mapProperties(ClassInfo $classInfo): array
+    private function mapProperties(ClassInfo $classInfo, ContextInterface $context): array
     {
         $properties = [];
         $identifierCount = 0;
@@ -240,7 +216,7 @@ final class SerializationMapper implements TypeFactoryInterface
                 ++$identifierCount;
             }
 
-            $property = $this->mapProperty($propertyMeta);
+            $property = $this->mapProperty($propertyMeta, $context);
             $properties[$property->getName()] = $property;
         }
 
@@ -258,7 +234,7 @@ final class SerializationMapper implements TypeFactoryInterface
             $properties['@id'] = new Property(
                 '@id',
                 '',
-                $this->createIRI(\iterator_to_array($classInfo->iterateConcreteDescendants())),
+                $this->createIRI(\iterator_to_array($classInfo->iterateConcreteDescendants()), $context),
                 true,
                 !$this->serialization->isNormalization(),
                 true
@@ -294,62 +270,22 @@ final class SerializationMapper implements TypeFactoryInterface
 
     /**
      * @param PropertyMetadata $propertyMeta
+     * @param ContextInterface $context
      *
      * @return Property
      */
-    private function mapProperty(PropertyMetadata $propertyMeta): Property
+    private function mapProperty(PropertyMetadata $propertyMeta, ContextInterface $context): Property
     {
         return new Property(
             $propertyMeta->getName(),
             $propertyMeta->getDescription() ?: '',
-            $this->mapType($propertyMeta->getType(), $propertyMeta->isLink() && !$propertyMeta->isEmbedded()),
+            $context->createType($propertyMeta->getType()),
             $propertyMeta->isIdentifier(),
             $propertyMeta->isNullable(),
             !$propertyMeta->isWritable()
         );
     }
 
-    /**
-     * @param APIType $type
-     * @param bool    $isLink
-     *
-     * @return Type
-     */
-    private function mapType(?APIType $type, bool $isLink = false): Type
-    {
-        if ($type === null) {
-            return BuiltinType::get('unknown');
-        }
-
-        if (!$type->isCollection()) {
-            $className = $type->getClassName();
-            if ($className === null) {
-                return $this->get($type->getBuiltinType());
-            }
-            if ($isLink) {
-                return $this->createIRI([PHPClass::get($className)]);
-            }
-
-            return $this->get($className);
-        }
-
-        $indexType = $type->getCollectionKeyType();
-        if ($indexType === null && $type->getCollectionValueType() === null && $type->getBuiltinType() === 'array') {
-            return new ArrayType(BuiltinType::get('unknown'));
-        }
-
-        Assertion::notNull($indexType, 'Cannot handle collection with undefined index type');
-
-        $builtinType = $indexType->getBuiltinType();
-        if ($builtinType !== 'int') {
-            throw new \InvalidArgumentException("Cannot handle collection with index type $builtinType");
-        }
-
-        $valueType = $this->mapType($type->getCollectionValueType(), $isLink);
-
-        return new ArrayType($valueType);
-    }
-
     /**
      * @param ClassName $class
      *
diff --git a/src/php/SerializationMapperFactory.php b/src/php/SerializationMapperFactory.php
index 58e0be36cd7de7fffd99052104528761a4ac19c2..1e78889e84c844630c5d8401ad3a87ac866899a2 100644
--- a/src/php/SerializationMapperFactory.php
+++ b/src/php/SerializationMapperFactory.php
@@ -28,22 +28,9 @@ use Irstea\NgModelGeneratorBundle\Metadata\SerializationMetadata;
  */
 final class SerializationMapperFactory implements SerializationMapperFactoryInterface
 {
-    /** @var TypeFactoryInterface */
-    private $typeFactory;
-
     /** @var SerializationMapper[] */
     private $cache = [];
 
-    /**
-     * SerializationMapperFactory constructor.
-     *
-     * @param TypeFactoryInterface $typeFactory
-     */
-    public function __construct(TypeFactoryInterface $typeFactory)
-    {
-        $this->typeFactory = $typeFactory;
-    }
-
     /**
      * @param SerializationMetadata $serialization
      * @param bool                  $withAtFields
@@ -61,7 +48,7 @@ final class SerializationMapperFactory implements SerializationMapperFactoryInte
         );
 
         if (!isset($this->cache[$key])) {
-            $this->cache[$key] = new SerializationMapper($this->typeFactory, $serialization, $withAtFields);
+            $this->cache[$key] = new SerializationMapper($serialization, $withAtFields);
         }
 
         return $this->cache[$key];
diff --git a/src/php/TypeFactory.php b/src/php/TypeFactory.php
deleted file mode 100644
index a9422d89402bb72001c72c7c0e17b1369560015d..0000000000000000000000000000000000000000
--- a/src/php/TypeFactory.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php declare(strict_types=1);
-/*
- * This file is part of "irstea/ng-model-generator-bundle".
- *
- * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata.
- * Copyright (C) 2018-2019 IRSTEA
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation, either version 3 of the License, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License and the GNU
- * Lesser General Public License along with this program. If not, see
- * <https://www.gnu.org/licenses/>.
- */
-
-namespace Irstea\NgModelGeneratorBundle;
-
-use Irstea\NgModelGeneratorBundle\Exceptions\TypeAlreadyExistsException;
-use Irstea\NgModelGeneratorBundle\Exceptions\TypeNotFoundException;
-use Irstea\NgModelGeneratorBundle\Models\Types\Deferred;
-use Irstea\NgModelGeneratorBundle\Models\Types\Reference;
-use Irstea\NgModelGeneratorBundle\Models\Types\Type;
-
-/**
- * Class TypeFactory.
- */
-final class TypeFactory implements TypeFactoryInterface, \IteratorAggregate
-{
-    /** @var Type[] */
-    private $types = [];
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getIterator()
-    {
-        yield from $this->types;
-    }
-
-    /**
-     * @param string $name
-     *
-     * @return bool
-     */
-    public function has(string $name): bool
-    {
-        return isset($this->types[$name]);
-    }
-
-    /**
-     * @param string $name
-     *
-     * @return Type
-     */
-    public function get(string $name): Type
-    {
-        // API-platform semble avoir un problème avec les types utilisés dans un Trait.
-        if (strpos($name, '\UuidInterface') !== false) {
-            $name = 'Ramsey\Uuid\UuidInterface';
-        }
-
-        if (!isset($this->types[$name])) {
-            throw new TypeNotFoundException("unknown type ${name}");
-        }
-
-        return $this->types[$name];
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function add(string $name, Type $type): void
-    {
-        if (isset($this->types[$name])) {
-            throw new TypeAlreadyExistsException("type already exists: ${name}");
-        }
-
-        $this->types[$name] = $type;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function defer(string $name): Deferred
-    {
-        if (isset($this->types[$name]) && $this->types[$name] instanceof Deferred) {
-            /* @noinspection PhpIncompatibleReturnTypeInspection */
-            return $this->types[$name];
-        }
-        $ref = new Reference($name);
-        $this->add($name, $ref);
-
-        return $ref;
-    }
-}