diff --git a/src/Models/Types/Operations/PlainPath.php b/src/Models/Types/Operations/FixedPathPart.php
similarity index 61%
rename from src/Models/Types/Operations/PlainPath.php
rename to src/Models/Types/Operations/FixedPathPart.php
index ff4c6b00bf91dba82bb529ff9f60173222cf0e51..1146dfeb4ed13bf9357fdfb629b2f9f5e6ad0ef1 100644
--- a/src/Models/Types/Operations/PlainPath.php
+++ b/src/Models/Types/Operations/FixedPathPart.php
@@ -19,63 +19,69 @@
 
 namespace Irstea\NgModelGeneratorBundle\Models\Types\Operations;
 
-use Irstea\NgModelGeneratorBundle\TypescriptHelper;
-
 /**
- * Class PlainPath.
+ * Class FixedPathPart.
  */
-class PlainPath implements Path, \JsonSerializable
+final class FixedPathPart implements PathPart
 {
-    /** @var string */
-    private $path;
+    /**
+     * @var string
+     */
+    private $part;
 
     /**
-     * PlainPath constructor.
-     *
-     * @param string $path
+     * FixedPathPart constructor.
      */
-    public function __construct(string $path)
+    public function __construct(string $part)
     {
-        $this->path = $path;
+        $this->part = $part;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getUsage(): string
+    public function asTemplate(): string
     {
-        return TypescriptHelper::quoteString($this->path);
+        return $this->part;
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getParameters(): array
+    public function asTestPattern(): string
     {
-        return [];
+        return \preg_quote($this->part, '/');
     }
 
     /**
      * {@inheritdoc}
      */
-    public function getPattern(): string
+    public function asCapturePattern(): string
     {
-        return preg_quote($this->path, '/');
+        return $this->asTestPattern();
     }
 
     /**
-     * @return Parameter|null
+     * {@inheritdoc}
      */
-    public function getIdentifier(): ?Parameter
+    public function getParameter(): ?Parameter
     {
         return null;
     }
 
     /**
-     * @return mixed|string
+     * {@inheritdoc}
+     */
+    public function isEqual(PathPart $other): bool
+    {
+        return $other instanceof self && $other->part === $this->part;
+    }
+
+    /**
+     * @return array
      */
     public function jsonSerialize()
     {
-        return $this->path;
+        return \get_object_vars($this);
     }
 }
diff --git a/src/Models/Types/Operations/Parameter.php b/src/Models/Types/Operations/Parameter.php
index 146e397f185129ffc4f8144fc6d5b43407df23e2..bd40905b1362d516b6bc95f6c5fa9ddb1b0e24e5 100644
--- a/src/Models/Types/Operations/Parameter.php
+++ b/src/Models/Types/Operations/Parameter.php
@@ -89,15 +89,29 @@ class Parameter implements \IteratorAggregate, \JsonSerializable
         yield $this->type;
     }
 
+    /**
+     * @param self $other
+     *
+     * @return bool
+     */
+    public function isEqual(self $other): bool
+    {
+        /* @noinspection TypeUnsafeComparisonInspection */
+        return $other instanceof self &&
+            $other->name === $this->name &&
+            $other->optional === $this->optional &&
+            $other->type == $this->type;
+    }
+
     /**
      * @return array|mixed
      */
     public function jsonSerialize()
     {
         return [
-            'name'        => $this->name,
-            'type'        => $this->type,
-            'required'    => !$this->optional,
+            'name'     => $this->name,
+            'type'     => $this->type,
+            'required' => !$this->optional,
         ];
     }
 }
diff --git a/src/Models/Types/Operations/ParameterPathPart.php b/src/Models/Types/Operations/ParameterPathPart.php
new file mode 100644
index 0000000000000000000000000000000000000000..bcb47c97e3e389b59bb5b4bb5bd6bf741cb2b460
--- /dev/null
+++ b/src/Models/Types/Operations/ParameterPathPart.php
@@ -0,0 +1,104 @@
+<?php declare(strict_types=1);
+/*
+ * irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018 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\Operations;
+
+/**
+ * Class ParameterPathPart.
+ */
+final class ParameterPathPart implements PathPart
+{
+    /**
+     * @var Parameter
+     */
+    private $parameter;
+
+    /**
+     * @var string
+     */
+    private $requirement;
+
+    /**
+     * ParameterPathPart constructor.
+     *
+     * @param Parameter $parameter
+     * @param string    $requirement
+     */
+    public function __construct(Parameter $parameter, string $requirement = '[^/]+')
+    {
+        $this->parameter = $parameter;
+        $this->requirement = $requirement;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function asTemplate(): string
+    {
+        return sprintf('${%s}', $this->parameter->getName());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function asTestPattern(): string
+    {
+        return $this->requirement;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function asCapturePattern(): string
+    {
+        return sprintf('(%s)', $this->requirement);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getParameter(): ?Parameter
+    {
+        return $this->parameter;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isEqual(PathPart $other): bool
+    {
+        return $other instanceof self && $other->parameter->isEqual($this->parameter);
+    }
+
+    /**
+     * @return \Generator
+     */
+    public function getIterator()
+    {
+        yield $this->parameter;
+    }
+
+    /**
+     * @return array
+     */
+    public function jsonSerialize()
+    {
+        return \get_object_vars($this);
+    }
+}
diff --git a/src/Models/Types/Operations/Path.php b/src/Models/Types/Operations/Path.php
index 61ef73a85157af5d8d06d6b15a1245c7511a29bc..b14e9d9a96a707c78eccea7b2475a2ac83f684f0 100644
--- a/src/Models/Types/Operations/Path.php
+++ b/src/Models/Types/Operations/Path.php
@@ -19,28 +19,157 @@
 
 namespace Irstea\NgModelGeneratorBundle\Models\Types\Operations;
 
+use Irstea\NgModelGeneratorBundle\TypescriptHelper;
+
 /**
- * Interface Path.
+ * Class Path.
  */
-interface Path
+final class Path
 {
+    /**
+     * @var PathPart[]
+     */
+    private $parts;
+
+    /**
+     * Path constructor.
+     *
+     * @param PathPart[] $parts
+     */
+    public function __construct(array $parts)
+    {
+        $this->parts = $parts;
+    }
+
+    /**
+     * @return string
+     */
+    public function getUsage(): string
+    {
+        return TypescriptHelper::quoteString(
+            $this->getTemplate(),
+            $this->hasParameters() ? '`' : "'"
+        );
+    }
+
+    /**
+     * @return string
+     */
+    public function getTemplate(): string
+    {
+        return implode(
+            '',
+            array_map(
+                function (PathPart $part): string {
+                    return $part->asTemplate();
+                },
+                $this->parts
+            )
+        );
+    }
+
     /**
      * @return string
      */
-    public function getUsage(): string;
+    public function getTestPattern(): string
+    {
+        return implode(
+            '',
+            array_map(
+                function (PathPart $part): string {
+                    return $part->asTestPattern();
+                },
+                $this->parts
+            )
+        );
+    }
 
     /**
      * @return string
      */
-    public function getPattern(): string;
+    public function getCapturePattern(): string
+    {
+        return implode(
+            '',
+            array_map(
+                function (PathPart $part): string {
+                    return $part->asCapturePattern();
+                },
+                $this->parts
+            )
+        );
+    }
+
+    /**
+     * @return bool
+     */
+    public function hasParameters(): bool
+    {
+        foreach ($this->parts as $part) {
+            if ($part->getParameter() !== null) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 
     /**
      * @return Parameter[]
      */
-    public function getParameters(): array;
+    public function getParameters(): array
+    {
+        return \array_filter(
+            array_map(
+                function (PathPart $part): ?Parameter {
+                    return $part->getParameter();
+                },
+                $this->parts
+            )
+        );
+    }
+
+    /**
+     * @param Path      $other
+     * @param Parameter $otherParameter
+     *
+     * @return Path
+     */
+    public function buildUsing(Path $other, Parameter $otherParameter): Path
+    {
+        if (\count($other->parts) > \count($this->parts)) {
+            return $this;
+        }
+        foreach ($other->parts as $index => $part) {
+            if (!$this->parts[$index]->isEqual($part)) {
+                return $this;
+            }
+        }
+        if (\count($other->parts) === \count($this->parts)) {
+            return $other;
+        }
+
+        return new Path(
+            \array_merge(
+                [new ParameterPathPart($otherParameter, $other->getTestPattern())],
+                \array_slice($this->parts, \count($other->parts))
+            )
+        );
+    }
+
+    /**
+     * @return \Generator
+     */
+    public function getIterator()
+    {
+        yield from $this->parts;
+    }
 
     /**
-     * @return Parameter|null
+     * @return array
      */
-    public function getIdentifier(): ?Parameter;
+    public function jsonSerialize()
+    {
+        return \get_object_vars($this);
+    }
 }
diff --git a/src/Models/Types/Operations/PathPart.php b/src/Models/Types/Operations/PathPart.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c745d4d55de00804e21e7f088ca66ccf519eb2b
--- /dev/null
+++ b/src/Models/Types/Operations/PathPart.php
@@ -0,0 +1,53 @@
+<?php declare(strict_types=1);
+/*
+ * irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
+ * Copyright (C) 2018 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\Operations;
+
+/**
+ * interface PathPart.
+ */
+interface PathPart
+{
+    /**
+     * @return string
+     */
+    public function asTemplate(): string;
+
+    /**
+     * @return string
+     */
+    public function asTestPattern(): string;
+
+    /**
+     * @return string
+     */
+    public function asCapturePattern(): string;
+
+    /**
+     * @return Parameter|null
+     */
+    public function getParameter(): ?Parameter;
+
+    /**
+     * @param PathPart $other
+     *
+     * @return bool
+     */
+    public function isEqual(PathPart $other): bool;
+}
diff --git a/src/Models/Types/Operations/PathTemplate.php b/src/Models/Types/Operations/PathTemplate.php
deleted file mode 100644
index 192f07e60419ca10e2d1410cbd4822c65b08c014..0000000000000000000000000000000000000000
--- a/src/Models/Types/Operations/PathTemplate.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php declare(strict_types=1);
-/*
- * irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
- * Copyright (C) 2018 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\Operations;
-
-use Irstea\NgModelGeneratorBundle\Exceptions\DomainException;
-use Irstea\NgModelGeneratorBundle\TypescriptHelper;
-
-/**
- * Class PathTemplate.
- */
-class PathTemplate implements Path, \JsonSerializable
-{
-    /** @var string */
-    private $template;
-
-    /** @var string */
-    private $pattern;
-
-    /** @var Parameter[] */
-    private $parameters = [];
-
-    /** @var string|null */
-    private $identifierName;
-
-    /**
-     * PathTemplate constructor.
-     *
-     * @param string      $template
-     * @param string      $pattern
-     * @param Parameter[] $parameters
-     * @param string|null $identifierName
-     */
-    public function __construct(string $template, string $pattern, array $parameters, ?string $identifierName)
-    {
-        $this->template = $template;
-        $this->pattern = $pattern;
-        $this->identifierName = $identifierName;
-
-        foreach ($parameters as $parameter) {
-            $this->parameters[$parameter->getName()] = $parameter;
-        }
-
-        if ($identifierName && !isset($this->parameters[$identifierName])) {
-            throw new DomainException("L'identifiant $identifierName n'est pas dans la liste des paramètres");
-        }
-    }
-
-    /**
-     * Get template.
-     *
-     * @return string
-     */
-    public function getUsage(): string
-    {
-        return TypescriptHelper::quoteString($this->template, '`');
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getPattern(): string
-    {
-        return $this->pattern;
-    }
-
-    /**
-     * Get parameters.
-     *
-     * @return Parameter[]
-     */
-    public function getParameters(): array
-    {
-        return $this->parameters;
-    }
-
-    /**
-     * @return Parameter|null
-     */
-    public function getIdentifier(): ?Parameter
-    {
-        return $this->identifierName ? $this->parameters[$this->identifierName] : null;
-    }
-
-    /**
-     * @return mixed|array
-     */
-    public function jsonSerialize()
-    {
-        return \get_object_vars($this);
-    }
-}
diff --git a/src/PathParser.php b/src/PathParser.php
index 184c4d44aea5d46463ddce567c2b877d7ea9aaf0..1488d5f9cabe9f495fa0a488945d63b66d68a382 100644
--- a/src/PathParser.php
+++ b/src/PathParser.php
@@ -19,15 +19,12 @@
 
 namespace Irstea\NgModelGeneratorBundle;
 
-use Irstea\NgModelGeneratorBundle\Models\ClassName;
 use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType;
 use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property;
+use Irstea\NgModelGeneratorBundle\Models\Types\Operations\FixedPathPart;
 use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Parameter;
+use Irstea\NgModelGeneratorBundle\Models\Types\Operations\ParameterPathPart;
 use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Path;
-use Irstea\NgModelGeneratorBundle\Models\Types\Operations\PathTemplate;
-use Irstea\NgModelGeneratorBundle\Models\Types\Operations\PlainPath;
-use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI;
-use Irstea\NgModelGeneratorBundle\Models\Types\Union;
 
 /**
  * Class PathParser.
@@ -37,19 +34,14 @@ final class PathParser implements PathParserInterface
     /** @var Property[] */
     private $properties;
 
-    /** @var ClassName */
-    private $resource;
-
     /**
      * PathParser constructor.
      *
-     * @param ClassName  $resource
      * @param Property[] $properties
      */
-    public function __construct(ClassName $resource, array $properties)
+    public function __construct(array $properties)
     {
         $this->properties = $properties;
-        $this->resource = $resource;
     }
 
     /**
@@ -57,65 +49,36 @@ final class PathParser implements PathParserInterface
      */
     public function parse(string $path, array $requirements): Path
     {
-        $parts = preg_split('/\{(\w+)\}/', $path, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
-
-        if (\count($parts) === 1) {
-            return new PlainPath($parts[0]);
-        }
+        $stringParts = preg_split('/\{(\w+)\}/', $path, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
 
-        return $this->parsePathTemplate($parts, $requirements);
-    }
+        $parts = [];
 
-    /**
-     * @param string[] $parts
-     *
-     * @return PathTemplate
-     */
-    private function parsePathTemplate(array $parts, array $requirements): PathTemplate
-    {
-        $template = $pattern = $parameters = [];
-        $identifier = null;
-
-        $num = \count($parts);
-        for ($i = 0; $i < $num; $i += 2) {
-            $plain = $parts[$i];
-            $template[] = $plain;
-            $pattern[] = preg_quote($plain, '/');
+        $num = \count($stringParts);
+        for ($i = 0; $i < $num; ++$i) {
+            if ($stringParts[$i]) {
+                $parts[] = new FixedPathPart($stringParts[$i]);
+            }
+            ++$i;
 
-            if (!isset($parts[$i + 1])) {
+            if ($i >= $num) {
                 break;
             }
-            $name = $parts[$i + 1];
+
+            $name = $stringParts[$i];
 
             if (isset($this->properties[$name])) {
                 $property = $this->properties[$name];
-                $isIdentifier = $property->isIdentifier();
                 $type = $property->getType();
             } else {
-                $isIdentifier = $name === 'id';
                 $type = BuiltinType::get('string');
             }
-            if ($isIdentifier) {
-                $identifier = $name;
-                //$type = Union::create([$type, new IRI([$this->resource])]);
-            }
-
-            $template[] = '${';
-            $template[] = $name;
-            $template[] = '}';
 
-            $pattern[] = $isIdentifier ? '(' : '(?:';
-            $pattern[] = isset($requirements[$name]) ? trim($requirements[$name], '^$') : '[^\\/]*';
-            $pattern[] = ')';
+            $parameter = new Parameter($name, $type);
+            $requirement = isset($requirements[$name]) ? trim($requirements[$name], '^$') : '[^\\/]*';
 
-            $parameters[$name] = new Parameter($name, $type);
+            $parts[] = new ParameterPathPart($parameter, $requirement);
         }
 
-        return new PathTemplate(
-            implode('', $template),
-            implode('', $pattern),
-            $parameters,
-             $identifier
-        );
+        return new Path($parts);
     }
 }