diff --git a/Command/EntitySchemaCommand.php b/Command/EntitySchemaCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7335149b6d43511b4e0d8695b652b6f49959f2c
--- /dev/null
+++ b/Command/EntitySchemaCommand.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * Copyright (C) 2015 IRSTEA
+ * All rights reserved.
+ */
+namespace Irstea\PlantUmlBundle\Command;
+
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Irstea\PlantUmlBundle\Model\Orm\EntityVisitor;
+use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Description of ImportAffiliationCommand
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class EntitySchemaCommand extends ContainerAwareCommand
+{
+    protected function configure()
+    {
+        $this
+            ->setName('irstea:plantuml:entities')
+            ->setDescription("Génère un schéma PlantUML à partir des métadonnées de Doctrine.");
+    }
+
+    /**
+     *
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     *
+     * @SuppressWarnings(UnusedFormalParameter)
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        /* @var $manager EntityManagerInterface */
+        $manager = $this->getContainer()->get('doctrine.orm.entity_manager');
+        $factory = $manager->getMetadataFactory();
+
+        $allMetadata = $factory->getAllMetadata();
+
+        $visitor = new EntityVisitor($allMetadata);
+        foreach($allMetadata as $metadata) {
+            /* @var $metadata ClassMetadata */
+            $visitor->visitClass($metadata->getName());
+        }
+
+        $stream = fopen("php://output", "wt");
+        fputs($stream, "@startuml\n");
+        fputs($stream, 'set namespaceSeparator \\\\'."\n");
+        $visitor->outputTo($stream);
+        fputs($stream, "@enduml\n");
+        fclose($stream);
+    }
+}
diff --git a/Commmand/OrmSchemaCommand.php b/Commmand/OrmSchemaCommand.php
deleted file mode 100644
index 0c4e057aabc3735e59ed4f238bec016f53cafb04..0000000000000000000000000000000000000000
--- a/Commmand/OrmSchemaCommand.php
+++ /dev/null
@@ -1,201 +0,0 @@
-<?php
-
-/*
- * Copyright (C) 2015 IRSTEA
- * All rights reserved.
- */
-namespace Irstea\PlantUmlBundle\Command;
-
-use Doctrine\ORM\EntityManagerInterface;
-use Doctrine\ORM\Mapping\ClassMetadata;
-use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Output\OutputInterface;
-
-/**
- * Description of ImportAffiliationCommand
- *
- * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
- */
-class UmlSchemaCommand extends ContainerAwareCommand
-{
-    /**
-     * @var array[]
-     */
-    private $namespaces = [];
-
-    /**
-     * @var string[]
-     */
-    private $nodes = [];
-
-    /**
-     * @var string[]
-     */
-    private $arrows = [];
-
-    protected function configure()
-    {
-        $this
-            ->setName('irstea:plantuml:doctrine')
-            ->setDescription("Génère un schéma PlantUML à partir des métadonnées Doctrine.");
-    }
-
-    /**
-     *
-     * @param InputInterface $input
-     * @param OutputInterface $output
-     *
-     * @SuppressWarnings(UnusedFormalParameter)
-     */
-    protected function execute(InputInterface $input, OutputInterface $output)
-    {
-        /* @var $manager EntityManagerInterface */
-        $manager = $this->getContainer()->get('doctrine.orm.entity_manager');
-        $factory = $manager->getMetadataFactory();
-        $allMetadata = $factory->getAllMetadata();
-
-        array_walk($allMetadata, [$this, 'processClass']);
-
-        echo "@startuml\n";
-        $this->writeNamespaces($this->namespaces);
-        echo "\n\n";
-        echo implode("\n", $this->nodes);
-        echo "\n\n";
-        echo implode("\n", $this->arrows);
-        echo "\n\n@enduml\n";
-    }
-
-    protected  function processClass(ClassMetadata $metadata)
-    {
-        $node = [];
-
-        $name = $this->formatName($metadata->getName());
-        $class =  $metadata->getReflectionClass();
-
-        $node[] = sprintf(
-            '%s%s %s%s {',
-            $class->isAbstract() ? 'abstract ' : '',
-            $class->isInterface() ? 'interface' : 'class',
-            $name,
-            $metadata->isMappedSuperclass ? ' <<mappedSuperClass>>' : ''
-        );
-
-        foreach($metadata->fieldMappings as $field) {
-            $node[] = sprintf("\t%s: %s", $field['fieldName'], $field['type']);
-        }
-
-        $node[] = '}';
-
-        $this->addNode($name, implode("\n", $node), true);
-
-        foreach($metadata->getAssociationMappings() as $association) {
-            if (!$association['isOwningSide']) {
-                continue;
-            }
-
-            $sourceCard = ($association['type'] & (ClassMetadata::MANY_TO_MANY | ClassMetadata::MANY_TO_ONE)) ? '*' : '1';
-            $sourceArrow = $association['inversedBy'] ? '<' : '';
-            $targetCard = ($association['type'] & ClassMetadata::TO_MANY) ? '*' : '1';
-            $targetArrow = '>';
-
-            $this->addArrow(
-                $association['sourceEntity'],
-                $association['targetEntity'],
-                sprintf('"%s" %s--%s "%s"', $sourceCard, $sourceArrow, $targetArrow, $targetCard)
-            );
-        }
-
-        $parentClass = $class->getParentClass();
-        if ($parentClass) {
-            $this->addArrow($parentClass->getName(), $metadata->getName(), '<|--');
-        }
-        foreach($class->getInterfaceNames() as $interfaceName) {
-            $this->addNode($interfaceName, sprintf("interface %s {\n}", $this->formatName($interfaceName)));
-            $this->addArrow($interfaceName, $metadata->getName(), '<|..');
-        }
-        foreach($class->getTraitNames() as $traitName) {
-            $this->addNode($traitName, sprintf("class %s <<trait>> {\n}", $this->formatName($traitName)));
-            $this->addArrow($traitName, $metadata->getName(), '<|--');
-        }
-    }
-
-    /**
-     * Enregistre un namespace.
-     *
-     * @param string[] $components
-     */
-    protected function addNamespace(array $components)
-    {
-        $current = &$this->namespaces;
-        foreach($components as $next) {
-            if (!isset($current[$next])) {
-                $current[$next] = [];
-            }
-            $current =& $current[$next];
-        }
-    }
-
-    /**
-     *
-     * @param string $name
-     * @return string
-     */
-    protected function formatName($name)
-    {
-        return strtr($name, '\\', '.');
-    }
-
-    /**
-     * Ajoute un noeud.
-     *
-     * @param string $name Nom du noeud.
-     * @param string $body Déclaration du noeud.
-     * @param bool $overwrite Doit-on remplacer la déclaration s'il existe déjà ?
-     *
-     * @return string Le nom de noeud "PlantUml-compatible".
-     */
-    protected function addNode($name, $body = '', $overwrite = false)
-    {
-        $fullName = $this->formatName($name);
-        $components = explode('\\', $name);
-        array_pop($components);
-        $this->addNamespace($components);
-
-        if (!isset($this->nodes[$fullName]) || $overwrite) {
-            $this->nodes[$fullName] = $body;
-        }
-        return $fullName;
-    }
-
-    /**
-     * Ajoute une flèche sur le schéma.
-     *
-     * @param string $source Nom de l'origine.
-     * @param string $target Nom de la destination.
-     * @param string $type Description du lien.
-     */
-    protected function addArrow($source, $target, $type = '-->')
-    {
-        $sourceName = $this->addNode($source);
-        $targetName = $this->addNode($target);
-
-        $this->arrows[] = sprintf('%s %s %s', $sourceName, $type, $targetName);
-    }
-
-    /**
-     * Génère les déclarations de namespaces.
-     *
-     * @param array $current
-     */
-    protected function writeNamespaces(array $current)
-    {
-        foreach($current as $name => $children) {
-            echo sprintf("namespace %s {\n", $name);
-            $this->writeNamespaces($children);
-            echo "}\n";
-        }
-    }
-}
diff --git a/Model/Arrow/BaseArrow.php b/Model/Arrow/BaseArrow.php
new file mode 100644
index 0000000000000000000000000000000000000000..432ffe631f6694b254adcde6ebb965b7935970a9
--- /dev/null
+++ b/Model/Arrow/BaseArrow.php
@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Arrow;
+
+use Irstea\PlantUmlBundle\Model\UmlComponentInterface;
+use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
+
+/**
+ * Description of Arrow
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class BaseArrow implements UmlComponentInterface
+{
+    /**
+     * @var UmlNodeInterface
+     */
+    private $source;
+
+    /**
+     * @var UmlNodeInterface
+     */
+    private $target;
+
+    /**
+     * @var string
+     */
+    private $link;
+
+    public function __construct(UmlNodeInterface $source, UmlNodeInterface $target, $link = "--", $label = null)
+    {
+        $this->source = $source;
+        $this->target = $target;
+        $this->link   = $link;
+        $this->label  = $label;
+    }
+
+    public function outputTo($stream)
+    {
+        $this->source->outputAliasTo($stream);
+        $this->outputLinkTo($stream);
+        $this->target->outputAliasTo($stream);
+        $this->outputLabelTo($stream);
+        fputs($stream, "\n");
+    }
+
+    protected function outputLabelTo($stream)
+    {
+        if ($this->label) {
+            fputs($stream, " : {$this->label}");
+        }
+    }
+
+    protected function outputLinkTo($stream)
+    {
+        fputs($stream, " ".$this->link." ");
+    }
+}
diff --git a/Model/Arrow/ExtendsClass.php b/Model/Arrow/ExtendsClass.php
new file mode 100644
index 0000000000000000000000000000000000000000..384541da2bef1d5d2985975e3a27d078acbf0cf1
--- /dev/null
+++ b/Model/Arrow/ExtendsClass.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Arrow;
+
+/**
+ * Description of ExtendsClass
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class ExtendsClass extends BaseArrow
+{
+    public function __construct(\Irstea\PlantUmlBundle\Model\UmlNodeInterface $source, \Irstea\PlantUmlBundle\Model\UmlNodeInterface $target, $link = "--", $label = null)
+    {
+        parent::__construct($source, $target, $link, $label);
+    }
+}
diff --git a/Model/Arrow/ImplementsInterface.php b/Model/Arrow/ImplementsInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..dbdede845ccdaca7a8f3c025d3c63dea2fc1dbab
--- /dev/null
+++ b/Model/Arrow/ImplementsInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Arrow;
+
+use Irstea\PlantUmlBundle\Model\Node\Interface_;
+use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
+
+/**
+ * Description of ImplementsInterface
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class ImplementsInterface extends BaseArrow
+{
+    public function __construct(UmlNodeInterface $source, Interface_ $target)
+    {
+        parent::__construct($source, $target, "..|>");
+    }
+}
diff --git a/Model/Arrow/UsesTrait.php b/Model/Arrow/UsesTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..925284d35f90091c736ab7d12613392711c31d54
--- /dev/null
+++ b/Model/Arrow/UsesTrait.php
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Arrow;
+
+use Irstea\PlantUmlBundle\Model\Node\Trait_;
+use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
+
+/**
+ * Description of UseTrait
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class UsesTrait extends BaseArrow
+{
+    public function __construct(UmlNodeInterface $source, Trait_ $trait)
+    {
+        parent::__construct($source, $trait, "--|>");
+    }
+}
diff --git a/Model/ClassVisitor.php b/Model/ClassVisitor.php
new file mode 100644
index 0000000000000000000000000000000000000000..10d61d51f4f1d521781b6cbdd5d6dde78471a960
--- /dev/null
+++ b/Model/ClassVisitor.php
@@ -0,0 +1,113 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model;
+
+use Irstea\PlantUmlBundle\Model\Node\Class_;
+use Irstea\PlantUmlBundle\Model\Node\Interface_;
+use Irstea\PlantUmlBundle\Model\Node\Trait_;
+use ReflectionClass;
+
+/**
+ * Description of Visitor
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class ClassVisitor implements ClassVisitorInterface, UmlComponentInterface
+{
+    /**
+     * @var UmlNodeInterface[]
+     */
+    protected $nodes = [];
+
+    /**
+     * @var UmlComponentInterface[]
+     */
+    protected $arrows = [];
+
+    public function visitClass($className)
+    {
+        assert('is_string($className)', $className);
+        if (isset($this->nodes[$className])) {
+            return $this->nodes[$className];
+        }
+
+        return $this->doVisitClass($className);
+    }
+
+    /**
+     *
+     * @param string $className
+     * @return UmlNodeInterface
+     */
+    protected function doVisitClass($className)
+    {
+        $reflection = new ReflectionClass($className);
+        return $this->visitClassReflection($reflection);
+    }
+
+    /**
+     * @param ReflectionClass $class
+     * @return UmlNodeInterface
+     */
+    protected function visitClassReflection(ReflectionClass $class)
+    {
+        if ($class->isTrait()) {
+            $node  = new Trait_($class->getName());
+        } elseif ($class->isInterface()) {
+            $node  = new Interface_($class->getName());
+        } else {
+            $node = new Class_($class->getName(), $class->isAbstract(), $class->isFinal());
+        }
+        $this->nodes[$class->getName()] = $node;
+
+        $parentClass = $class->getParentClass();
+        $traitNames = $class->getTraitNames();
+
+        $indirectInterfaces = array_filter(
+                array_map(
+                    function ($i) { return $i->getInterfaceNames(); },
+                    $class->getInterfaces()
+                )
+        );
+        $interfaceNames = $class->getInterfaceNames();
+        if (!empty($indirectInterfaces)) {
+            $indirectInterfaces = call_user_func_array('array_merge', $indirectInterfaces);
+            $interfaceNames = array_diff($interfaceNames, $indirectInterfaces);
+        }
+
+        if ($parentClass) {
+            $traitNames = array_diff($traitNames, $parentClass->getTraitNames());
+            $interfaceNames = array_diff($interfaceNames, $parentClass->getInterfaceNames());
+            $this->visitRelations($node, [$parentClass->getName()], 'Irstea\PlantUmlBundle\Model\Arrow\ExtendsClass');
+        }
+
+        $this->visitRelations($node, $interfaceNames, 'Irstea\PlantUmlBundle\Model\Arrow\ImplementsInterface');
+        $this->visitRelations($node, $traitNames, 'Irstea\PlantUmlBundle\Model\Arrow\UsesTrait');
+
+        return $node;
+    }
+
+    protected function visitRelations(UmlNodeInterface $source, array $classNames, $relationClass)
+    {
+        foreach ($classNames as $className) {
+            $target = $this->visitClass($className);
+            $this->arrows[] = new $relationClass($source, $target);
+        }
+    }
+
+    public function outputTo($stream)
+    {
+        foreach ($this->nodes as $node) {
+            $node->outputTo($stream);
+        }
+        foreach ($this->arrows as $arrow) {
+            $arrow->outputTo($stream);
+        }
+    }
+}
diff --git a/Model/ClassVisitorInterface.php b/Model/ClassVisitorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..6afc61e98b5513d2d835fabe2e3feb416f78af5c
--- /dev/null
+++ b/Model/ClassVisitorInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model;
+
+/**
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+interface ClassVisitorInterface
+{
+    /**
+     * @return UmlNodeInterface
+     */
+    public function visitClass($className);
+}
diff --git a/Model/Node/BaseNode.php b/Model/Node/BaseNode.php
new file mode 100644
index 0000000000000000000000000000000000000000..a0d74c44709d22d6e528e1bb84cfe900b5c9dd9a
--- /dev/null
+++ b/Model/Node/BaseNode.php
@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Node;
+
+use Irstea\PlantUmlBundle\Model\UmlNodeInterface;
+
+/**
+ * Description of Class
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class BaseNode implements UmlNodeInterface
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var string
+     */
+    private $nodeType;
+
+    /**
+     * @var string[]
+     */
+    private $classifiers;
+
+    /**
+     * @var string[]
+     */
+    private $stereotypes;
+
+    /**
+     * @param string $name
+     * @param string $nodeType
+     * @param array $classifiers
+     * @param array $stereotypes
+     */
+    function __construct($name, $nodeType, array $classifiers = [], array $stereotypes = [])
+    {
+        $this->name        = $name;
+        $this->nodeType    = $nodeType;
+        $this->classifiers = $classifiers;
+        $this->stereotypes = $stereotypes;
+    }
+
+    public function outputTo($stream)
+    {
+        $this->outputClassifiersTo($stream);
+        $this->outputNodeTypeTo($stream);
+        $this->outputAliasTo($stream);
+        $this->outputStereotypesTo($stream);
+        fputs($stream, " {\n");
+        $this->outputAttributesTo($stream);
+        $this->outputMethodsTo($stream);
+        fputs($stream, "}\n");
+    }
+
+    public function outputAliasTo($stream)
+    {
+        fputs($stream, '"'.str_replace('\\', '\\\\', $this->name).'"');
+    }
+
+    protected function outputClassifiersTo($stream)
+    {
+        foreach($this->classifiers as $classifier) {
+            fputs($stream, "$classifier ");
+        }
+    }
+
+    protected function outputNodeTypeTo($stream)
+    {
+        fputs($stream, $this->nodeType." ");
+    }
+
+    protected function outputStereotypesTo($stream)
+    {
+        foreach($this->stereotypes as $stereotypes) {
+            fputs($stream, " <<$stereotypes>>");
+        }
+    }
+
+    protected function outputAttributesTo($stream)
+    {
+        // NOP
+    }
+
+    protected function outputMethodsTo($stream)
+    {
+        // NOP
+    }
+}
diff --git a/Model/Node/Class_.php b/Model/Node/Class_.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef3f838674a315f3b47da6373f22713c813393c4
--- /dev/null
+++ b/Model/Node/Class_.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Node;
+
+/**
+ * Description of Class_
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class Class_ extends BaseNode
+{
+    public function __construct($name, $isAbstract, $isFinal)
+    {
+        $classifiers = [];
+        if ($isAbstract) {
+            $classifiers[] = 'abstract';
+        } elseif ($isFinal) {
+            $classifiers[] = 'final';
+        }
+        parent::__construct($name, "class", $classifiers);
+    }
+}
diff --git a/Model/Node/Interface_.php b/Model/Node/Interface_.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f53b3fbdfc23cd3e725a9abbf2dc6c7ef236b13
--- /dev/null
+++ b/Model/Node/Interface_.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Node;
+
+/**
+ * Description of Interface_
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class Interface_ extends BaseNode
+{
+    public function __construct($name)
+    {
+        parent::__construct($name, 'interface', [], ['interface']);
+    }
+}
diff --git a/Model/Node/Trait_.php b/Model/Node/Trait_.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b7001f74bff238afefe7c8b4c4480bdefc52a94
--- /dev/null
+++ b/Model/Node/Trait_.php
@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Node;
+
+/**
+ * Description of Trait_
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class Trait_ extends BaseNode
+{
+    public function __construct($name)
+    {
+        parent::__construct($name, 'class', [], ['trait']);
+    }
+}
diff --git a/Model/Orm/EntityVisitor.php b/Model/Orm/EntityVisitor.php
new file mode 100644
index 0000000000000000000000000000000000000000..74786379d83ad55bdf43bc254b6fdb184ba29d64
--- /dev/null
+++ b/Model/Orm/EntityVisitor.php
@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model\Orm;
+
+use Doctrine\ORM\Mapping\ClassMetadata;
+use Irstea\PlantUmlBundle\Model\ClassVisitor;
+
+/**
+ * Description of Visitor
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+class EntityVisitor extends ClassVisitor
+{
+    /**
+     * @var ClassMetadata[]
+     */
+    protected $metadata = [];
+
+    public function __construct(array $metadata)
+    {
+        foreach($metadata as $md) {
+            /* @var $md ClassMetadata */
+            $this->metadata[$md->getReflectionClass()->getName()] = $md;
+        }
+    }
+
+    public function doVisitClass($className)
+    {
+        if (isset($this->metadata[$className])) {
+            return $this->visitClassMetadata($this->metadata[$className]);
+        }
+
+        return parent::doVisitClass($className);
+    }
+
+    /**
+     * @param ClassMetadata $metadata
+     * @return AbstractNode
+     */
+    protected function visitClassMetadata(ClassMetadata $metadata)
+    {
+        return $this->visitClassReflection($metadata->getReflectionClass());
+    }
+}
diff --git a/Model/UmlComponentInterface.php b/Model/UmlComponentInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..65b13c7c9a726f136343582ff99f523a4fd4d4a2
--- /dev/null
+++ b/Model/UmlComponentInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model;
+
+/**
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+interface UmlComponentInterface
+{
+    public function outputTo($stream);
+}
diff --git a/Model/UmlNodeInterface.php b/Model/UmlNodeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..2594948c2e1b514c08c20a6cc57f7c31ea0baceb
--- /dev/null
+++ b/Model/UmlNodeInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+/*
+ * © 2016 IRSTEA
+ * Guillaume Perréal <guillaume.perreal@irstea.fr>
+ * Tous droits réservés.
+ */
+
+namespace Irstea\PlantUmlBundle\Model;
+
+/**
+ *
+ * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
+ */
+interface UmlNodeInterface extends \Irstea\PlantUmlBundle\Model\UmlComponentInterface
+{
+    public function outputAliasTo($stream);
+}