diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 7ec491698a6e134ade2ff19ca1a76429a0390335..112be4284359582cc9505d8af7adb74fc241ce76 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -6,31 +6,35 @@
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
-        <module name="rdf" />
-        <module name="unl-parser" />
+        <module name="unl-tools-infrastructure" />
+        <module name="unl-tools-core" />
       </profile>
-      <profile name="Annotation profile for unlTools" enabled="true">
+      <profile name="Annotation profile for unl-tools-main" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
-        <option name="project" value="fr.tetras_libre.unl/unl2rdf-app" />
+        <option name="project" value="fr.tetras_libre.unl/unl-tools-app" />
         <processorPath useClasspath="false">
           <entry name="$MAVEN_REPOSITORY$/info/picocli/picocli-codegen/4.2.0/picocli-codegen-4.2.0.jar" />
           <entry name="$MAVEN_REPOSITORY$/info/picocli/picocli/4.2.0/picocli-4.2.0.jar" />
         </processorPath>
-        <module name="unl2rdf-app" />
+        <module name="unl-tools-app" />
       </profile>
     </annotationProcessing>
     <bytecodeTargetLevel target="11">
-      <module name="rdf" target="11" />
       <module name="unl-parser" target="11" />
+      <module name="unl-tools-app" target="11" />
+      <module name="unl-tools-core" target="11" />
+      <module name="unl-tools-infrastructure" target="11" />
       <module name="unl2rdf" target="11" />
       <module name="unl2rdf-app" target="11" />
+      <module name="unlTools" target="11" />
     </bytecodeTargetLevel>
   </component>
   <component name="JavacSettings">
     <option name="ADDITIONAL_OPTIONS_OVERRIDE">
       <module name="unl-parser" options="" />
+      <module name="unl-tools-app" options="-Aproject=fr.tetras_libre.unl/unl-tools-app" />
       <module name="unl2rdf" options="-Aproject=unl2rdf/unl2rdf" />
       <module name="unl2rdf-app" options="-Aproject=fr.tetras_libre.unl/unl2rdf-app" />
     </option>
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 574f024d40961b1a31dae99281a61850566fdd4d..63015661248eb3b451adc962d2e580f43ff4f262 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="Encoding">
-    <file url="file://$PROJECT_DIR$/rdf/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/rdf/src/main/resources" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/unl-parser/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/unl2rdf-app/src/main/java" charset="UTF-8" />
-    <file url="file://$PROJECT_DIR$/unl2rdf-app/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/unl-tools-app/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/unl-tools-app/src/main/resources" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/unl-tools-core/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/unl-tools-infrastructure/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/unl-tools-infrastructure/src/main/resources" charset="UTF-8" />
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ee0869e4848b1bf27ebe83f42480980b3d8c759e..ba348da52a2036664ece6f26fd26a12926a42c27 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,11 +7,6 @@
         <option value="$PROJECT_DIR$/../rdf/pom.xml" />
       </list>
     </option>
-    <option name="ignoredFiles">
-      <set>
-        <option value="$PROJECT_DIR$/pom.xml" />
-      </set>
-    </option>
   </component>
   <component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="11" project-jdk-type="JavaSDK" />
 </project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 0224bb787dcab87e5036092ce3e15d7da8eef59c..7e06cd3831aed4b348841c6396e6e389af854885 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,9 +2,10 @@
 <project version="4">
   <component name="ProjectModuleManager">
     <modules>
-      <module fileurl="file://$PROJECT_DIR$/rdf/rdf.iml" filepath="$PROJECT_DIR$/rdf/rdf.iml" />
-      <module fileurl="file://$PROJECT_DIR$/unl-parser/unl-parser.iml" filepath="$PROJECT_DIR$/unl-parser/unl-parser.iml" />
-      <module fileurl="file://$PROJECT_DIR$/unl2rdf-app/unl2rdf-app.iml" filepath="$PROJECT_DIR$/unl2rdf-app/unl2rdf-app.iml" />
+      <module fileurl="file://$PROJECT_DIR$/unl-tools-app/unl-tools-app.iml" filepath="$PROJECT_DIR$/unl-tools-app/unl-tools-app.iml" />
+      <module fileurl="file://$PROJECT_DIR$/unl-tools-core/unl-tools-core.iml" filepath="$PROJECT_DIR$/unl-tools-core/unl-tools-core.iml" />
+      <module fileurl="file://$PROJECT_DIR$/unl-tools-infrastructure/unl-tools-infrastructure.iml" filepath="$PROJECT_DIR$/unl-tools-infrastructure/unl-tools-infrastructure.iml" />
+      <module fileurl="file://$PROJECT_DIR$/unlTools.iml" filepath="$PROJECT_DIR$/unlTools.iml" />
     </modules>
   </component>
 </project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e68c2c6d56e034d77f849547f185411052d74088..cd3992aa66c9b50064b1ac1ed070d7b6ab3c047d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,10 +13,12 @@
 
     <packaging>pom</packaging>
 
+    <name>unl-tools-main</name>
+
     <modules>
-        <module>unl-parser</module>
-        <module>unl2rdf-app</module>
-        <module>rdf</module>
+        <module>unl-tools-core</module>
+        <module>unl-tools-app</module>
+        <module>unl-tools-infrastructure</module>
     </modules>
 
     <build>
diff --git a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/R1SentenceWithExpectedRdf.java b/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/R1SentenceWithExpectedRdf.java
deleted file mode 100644
index 98534b938853e58d9335b6a0363abdfcc3a82137..0000000000000000000000000000000000000000
--- a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/R1SentenceWithExpectedRdf.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
-
-import fr.tetras_libre.unltools.unl.*;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.ArgumentsProvider;
-
-import java.util.*;
-import java.util.stream.Stream;
-
-public class R1SentenceWithExpectedRdf implements ArgumentsProvider {
-    private static LinkedList<UnlDocument> unlDocuments;
-
-    public R1SentenceWithExpectedRdf() {
-        unlDocuments = new LinkedList<>();
-    }
-
-    private static void buildUnlDocument() {
-
-        var unlDocument = new UnlDocument("[D]");
-
-        var graph = new Graph();
-
-        // aoj
-        var restrictions = new TreeSet<Restriction>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("ben", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("uw", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("make_possible", new TreeSet<>())));
-        var uw = new UniversalWord("allow", restrictions);
-        var uwNode = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@entry")));
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("group", new TreeSet<>())));
-        uw = new UniversalWord("system", restrictions);
-        var uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@def")));
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "aoj"));
-
-        // obj
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("ben", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("uw", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("make_possible", new TreeSet<>())));
-        uw = new UniversalWord("allow", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@entry")));
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("assume", new TreeSet<>())));
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("change", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
-        uw = new UniversalWord("take_on", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>());
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "obj"));
-
-        // ben
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("ben", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("uw", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("make_possible", new TreeSet<>())));
-        uw = new UniversalWord("allow", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@entry")));
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
-        uw = new UniversalWord("radiowave", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "ben"));
-
-        // aoj
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("assume", new TreeSet<>())));
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("change", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
-        uw = new UniversalWord("take_on", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>());
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
-        uw = new UniversalWord("channel", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "aoj"));
-
-        // obj
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
-        restrictions.add(new Restriction("equ", '>', new UniversalWord("assume", new TreeSet<>())));
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("change", new TreeSet<>())));
-        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
-        uw = new UniversalWord("take_on", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>());
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("attribute", new TreeSet<>())));
-        uw = new UniversalWord("state", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@plu")));
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "obj"));
-
-        // qua
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("attribute", new TreeSet<>())));
-        uw = new UniversalWord("state", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@plu")));
-
-        uwNode2 = new UniversalWordNode(new UniversalWord("2", new TreeSet<>()), new HashSet<>());
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "qua"));
-
-        // cnt
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("attribute", new TreeSet<>())));
-        uw = new UniversalWord("state", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@plu")));
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("sensing", new TreeSet<>())));
-        uw = new UniversalWord("listening", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@plu")));
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "cnt"));
-
-        // and
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("sensing", new TreeSet<>())));
-        uw = new UniversalWord("listening", restrictions);
-        uwNode = new UniversalWordNode(uw, new HashSet<>());
-
-        restrictions = new TreeSet<>();
-        restrictions.add(new Restriction("icl", '>', new UniversalWord("communication", new TreeSet<>())));
-        uw = new UniversalWord("traffic", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>());
-
-        graph.add(new GraphRelation(uwNode, uwNode2, "and"));
-
-        var unlDocumentNode = new UnlDocumentNode(UnlDocumentNodeType.Sentence, "[S:R1]", graph);
-
-        unlDocument.add(unlDocumentNode);
-
-        unlDocuments.add(unlDocument);
-    }
-
-    private static List<UnlDocument> getUnlDocuments() {
-        if (null == unlDocuments) {
-            buildUnlDocument();
-        }
-
-        return unlDocuments;
-    }
-
-
-    @Override
-    public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
-        buildUnlDocument();
-        return Stream.of(Arguments.of(unlDocuments));
-    }
-}
diff --git a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverterTest.java b/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverterTest.java
deleted file mode 100644
index e1edbec6831d4654e939f27c8fae306df9d15cb4..0000000000000000000000000000000000000000
--- a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverterTest.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class RelationLabelToUnlPropertyConverterTest {
-
-    @Test
-    public void Check(){
-        var relationLabelToUnlProperty = new RelationLabelToUnlPropertyConverter();
-
-        Assertions.assertDoesNotThrow(() -> relationLabelToUnlProperty.getObjectProperty("aoj", null));
-    }
-}
\ No newline at end of file
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/AllSetenceWithExpectedDotChecker.java b/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/AllSetenceWithExpectedDotChecker.java
deleted file mode 100644
index 60bd471cc04bcdc36e2cbcdeea080ed63ad1aed5..0000000000000000000000000000000000000000
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/AllSetenceWithExpectedDotChecker.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package fr.tetras_libre.unltools.unl;
-
-public class AllSetenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
-    private static final String ALL = "[D]" +
-            "[S:R1]\n" +
-            "{org:en}\n" +
-            "The system allows a radio channel to take on two states: Listening and Traffic.\n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "aoj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, system(icl>group).@def )\n" +
-            "obj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, take_on(aoj>thing, equ>assume,icl>change, obj>thing) )\n" +
-            "ben( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, channel(icl>radiowave).@indef)\n" +
-            "aoj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), channel(icl>radiowave).@indef) \n" +
-            "obj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), state(icl>attribute).@plu )   \n" +
-            "qua( state(icl>attribute).@plu, 2 )   \n" +
-            "cnt( state(icl>attribute).@plu, listening(icl>sensing) )\n" +
-            "and( listening(icl>sensing),traffic(icl>communication) )\n" +
-            "{/unl}\n" +
-            "[/S]\n" +
-            "[S:R2]\n" +
-            "{org:en}\n" +
-            "The system displays a channel in green when it is in broadcast state. \n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "agt(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,system(icl>group).@def)\n" +
-            "mod(channel(icl>radiowave).@indef,green(icl>adj,iof>color))\n" +
-            "obj(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,channel(icl>radiowave).@indef)\n" +
-            "tim(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,when(icl>how,com>always,tim<uw,obj>uw))\n" +
-            "aoj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,channel(icl>radiowave).@indef)\n" +
-            "plc:01(state(icl>attribute),broadcast(icl>message))\n" +
-            "obj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,state(icl>attribute))\n" +
-            "obj(when(icl>how,com>always,tim<uw,obj>uw),:01)\n" +
-            "{/unl}\n" +
-            "[/S] \n" +
-            "[S:00]\n" +
-            "{org:en}\n" +
-            "The black cat and the white cat are eating.\n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "mod:01(cat1(icl>feline>thing).@entry.@def,black(icl>adj))\n" +
-            "and:01(cat1(icl>feline>thing).@entry.@def,cat2(icl>feline>thing).@entry.@def)\n" +
-            "mod:01(cat2(icl>feline>thing).@entry.@def,white(icl>adj))\n" +
-            "agt(eat(icl>consume>do,agt>living_thing,obj>concrete_thing,ins>thing).@entry.@pl.@present.@progress,:01.@_hn-scope)\n" +
-            "{/unl}\n" +
-            "[/S]\n" +
-            "[/D]";
-
-    private static final String ALL_DOT = "digraph G {\n" +
-            "graph [fontname=\"courier\", compound=\"true\"];\n" +
-            "1 [\n" +
-            "label=\"allow(aoj>thing,ben>thing,equ>make_possible,icl>be,obj>uw)\\n.@entry\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "2 [\n" +
-            "label=\"system(icl>group)\\n.@def\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "3 [\n" +
-            "label=\"take_on(aoj>thing,equ>assume,icl>change,obj>thing)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "4 [\n" +
-            "label=\"channel(icl>radiowave)\\n.@indef\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "5 [\n" +
-            "label=\"state(icl>attribute)\\n.@plu\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "6 [\n" +
-            "label=\"2\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "7 [\n" +
-            "label=\"listening(icl>sensing)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "8 [\n" +
-            "label=\"traffic(icl>communication)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "1 -> 2 [label=\"aoj\" ];\n" +
-            "1 -> 3 [label=\"obj\" ];\n" +
-            "1 -> 4 [label=\"ben\" ];\n" +
-            "3 -> 4 [label=\"aoj\" ];\n" +
-            "3 -> 5 [label=\"obj\" ];\n" +
-            "5 -> 6 [label=\"qua\" ];\n" +
-            "5 -> 7 [label=\"cnt\" ];\n" +
-            "7 -> 8 [label=\"and\" ];\n" +
-            "}\n" +
-            "digraph G {\n" +
-            "graph [fontname=\"courier\", compound=\"true\"];\n" +
-            "1 [\n" +
-            "label=\"display(agt>thing,equ>exhibit,icl>show,obj>thing)\\n.@entry.@present\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "2 [\n" +
-            "label=\"system(icl>group)\\n.@def\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "3 [\n" +
-            "label=\"channel(icl>radiowave)\\n.@indef\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "4 [\n" +
-            "label=\"green(icl>adj,iof>color)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "5 [\n" +
-            "label=\"when(com>always,icl>how,obj>uw,tim<uw)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "6 [\n" +
-            "label=\"be_in_a_state(aoj>thing,icl>be,obj>state)\\n.@entry.@present\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "7 [\n" +
-            "label=\"state(icl>attribute)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "8 [\n" +
-            "label=\"broadcast(icl>message)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "subgraph cluster_01{\n" +
-            "   color = black;\n" +
-            "   label = \":01\"6 -> 3 [label=\"aoj\" ];\n" +
-            "7 -> 8 [label=\"plc\" ];\n" +
-            "6 -> 7 [label=\"obj\" ];\n" +
-            "}\n" +
-            "1 -> 2 [label=\"agt\" ];\n" +
-            "3 -> 4 [label=\"mod\" ];\n" +
-            "1 -> 3 [label=\"obj\" ];\n" +
-            "1 -> 5 [label=\"tim\" ];\n" +
-            "5 -> 6 [label=\"obj\"  lhead=\"cluster_01\"];\n" +
-            "}\n" +
-            "digraph G {\n" +
-            "graph [fontname=\"courier\", compound=\"true\"];\n" +
-            "1 [\n" +
-            "label=\"cat1(icl>feline(icl>thing))\\n.@def.@entry\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "2 [\n" +
-            "label=\"black(icl>adj)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "3 [\n" +
-            "label=\"cat2(icl>feline(icl>thing))\\n.@def.@entry\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "4 [\n" +
-            "label=\"white(icl>adj)\\n\"\n" +
-            "shape=\"box\"\n" +
-            "];\n" +
-            "5 [\n" +
-            "label=\"eat(agt>living_thing,icl>consume(icl>do),ins>thing,obj>concrete_thing)\\n.@entry.@pl.@present.@progress\"\n" +
-            "shape=\"box\"\n" +
-            "fontname=\"courb\"\n" +
-            "];\n" +
-            "subgraph cluster_01{\n" +
-            "   color = black;\n" +
-            "   label = \":01\"1 -> 2 [label=\"mod\" ];\n" +
-            "1 -> 3 [label=\"and\" ];\n" +
-            "3 -> 4 [label=\"mod\" ];\n" +
-            "}\n" +
-            "5 -> 1 [label=\"agt\"  lhead=\"cluster_01\"];\n" +
-            "}\n";
-
-    public AllSetenceWithExpectedDotChecker() {
-        super(ALL, ALL_DOT);
-    }
-}
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/CatSentenceWithExpectedDotChecker.java b/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/CatSentenceWithExpectedDotChecker.java
deleted file mode 100644
index 4d398d2ba188013ecfed92bab9d46c3a9dc1177c..0000000000000000000000000000000000000000
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/CatSentenceWithExpectedDotChecker.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package fr.tetras_libre.unltools.unl;
-
-public class CatSentenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
-
-    private static final String CAT_UNL = "[D]" +
-            "[S:00]\n" +
-            "{org:en}\n" +
-            "The black cat and the white cat are eating.\n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "mod:01(cat(icl>feline>thing):01.@entry.@def,black(icl>adj))\n" +
-            "and:01(cat(icl>feline>thing):01.@entry.@def,cat(icl>feline>thing):02.@entry.@def)\n" +
-            "mod:01(cat(icl>feline>thing):02.@entry.@def,white(icl>adj))\n" +
-            "agt(eat(icl>consume>do,agt>living_thing,obj>concrete_thing,ins>thing).@entry.@pl.@present.@progress,:01.@_hn-scope)\n" +
-            "{/unl}\n" +
-            "[/S]\n";
-
-    private static final String CAT_DOT = "digraph G {\n" +
-            "graph [fontname=\"courier\", compound=\"true\"];\n" +
-            "1 [ label=\"cat(icl>feline(icl>thing)):01\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
-            "2 [ label=\"black(icl>adj)\\n\" shape=\"box\" ];\n" +
-            "3 [ label=\"cat(icl>feline(icl>thing)):02\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
-            "4 [ label=\"white(icl>adj)\\n\" shape=\"box\" ];\n" +
-            "5 [ label=\"eat(agt>living_thing,icl>consume(icl>do),ins>thing,obj>concrete_thing)\\n.@entry.@pl.@present.@progress\" shape=\"box\" fontname=\"courb\" ];" +
-            " subgraph cluster_01{ color = black; label = \":01\"1 -> 2 [label=\"mod\" ]; 1 -> 3 [label=\"and\" ]; 3 -> 4 [label=\"mod\" ]; }\n" +
-            "5 -> 1 [label=\"agt\" lhead=\"cluster_01\"];\n" +
-            "}\n";
-
-    public CatSentenceWithExpectedDotChecker() {
-        super(CAT_UNL, CAT_DOT);
-    }
-}
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java b/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java
deleted file mode 100644
index c6e0a2bd38de304ced67e07471d18596bc91406a..0000000000000000000000000000000000000000
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package fr.tetras_libre.unltools.unl;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class EqualsBuilderTest {
-
-    @Test
-    void isEqual() {
-        String s1 = "qdfsf";
-        int i1 = 1;
-        long l1 = 2L;
-        EqualsBuilder builder = new EqualsBuilder()
-                .append(s1)
-                .append(i1)
-                .append(l1);
-
-        EqualsBuilder builder2 = new EqualsBuilder()
-                .append(s1)
-                .append(i1)
-                .append(l1);
-
-        Assertions.assertTrue(() -> builder.isEqual(builder2));
-    }
-}
\ No newline at end of file
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilderTest.java b/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilderTest.java
deleted file mode 100644
index 3ec313b0088c404fa826b3f95dcd44b3634a6fac..0000000000000000000000000000000000000000
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilderTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package fr.tetras_libre.unltools.unl.print.dotFile;
-
-import fr.tetras_libre.unltools.unl.*;
-import fr.tetras_libre.unltools.unl.parser.UnlParser;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestReporter;
-import org.junit.jupiter.api.io.TempDir;
-
-import java.io.*;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Vector;
-import java.util.concurrent.atomic.AtomicReference;
-
-class DotFileBuilderTest {
-
-    @TempDir
-    static Path SharedTempDir;
-
-    private TestReporter testReporter;
-
-    @Test
-    public void dotFileBuilderShouldCreateDotFileR1WithoutException(TestReporter testReporter) {
-        this.testReporter = testReporter;
-        Path dotPAth = SharedTempDir.resolve("r1.dot");
-        writeInFile(dotPAth, new R1SentenceWithExpectedDotChecker());
-    }
-
-    @Test
-    public void dotFileBuilderShouldCreateDotFileWithoutException(TestReporter testReporter) {
-        this.testReporter = testReporter;
-        Path dotPAth = SharedTempDir.resolve("r2.dot");
-        this.writeInFile(dotPAth, new R2SentenceWithExpectedDotChecker());
-    }
-
-    @Test
-    public void dotFileBuilderShouldCreateDotFileCatWithoutException(TestReporter testReporter) {
-        this.testReporter = testReporter;
-        Path dotPAth = SharedTempDir.resolve("cat.dot");
-        this.writeInFile(dotPAth, new CatSentenceWithExpectedDotChecker());
-    }
-
-    @Test
-    public void dotFileBuilderShouldCreateDotFileAllWithoutException(TestReporter testReporter) {
-        this.testReporter = testReporter;
-        Path dotPAth = SharedTempDir.resolve("all.dot");
-
-        this.writeInFile(dotPAth, new AllSetenceWithExpectedDotChecker());
-    }
-
-    private void writeInFile(Path filePath, SentenceWithExpectedDotChecker sentenceChecker) {
-        // Startup
-        UnlParser parser = new UnlParser(new BufferedReader(new StringReader(sentenceChecker.getUnlSentence())));
-        AtomicReference<Vector<UnlDocument>> documentsAtomic = new AtomicReference<>();
-        Assertions.assertDoesNotThrow(() -> documentsAtomic.set(parser.createUnlDocumentList()));
-
-        AtomicReference<File> outFile = new AtomicReference<>();
-        Assertions.assertDoesNotThrow(() -> outFile.set(new File(filePath.toAbsolutePath().toString())));
-
-        AtomicReference<BufferedWriter> outFileWriter = new AtomicReference<>();
-        Assertions.assertDoesNotThrow(() -> outFileWriter.set(new BufferedWriter(new FileWriter(outFile.get(), false))));
-
-        DotFileBuilder dotFileBuilder = new DotFileBuilder(outFileWriter.get());
-
-        // Test
-
-        Assertions.assertDoesNotThrow(() -> dotFileBuilder.write(documentsAtomic.get()));
-
-
-        // Teardown
-        Assertions.assertDoesNotThrow(() -> {
-            outFileWriter.get().flush();
-            outFileWriter.get().close();
-        });
-        Assertions.assertTrue(() -> outFile.get().length() != 0);
-        Assertions.assertDoesNotThrow(() -> sentenceChecker.setCurrentDotContent(Files.readString(filePath, StandardCharsets.UTF_8)));
-        sentenceChecker.assetSameAsExpected();
-
-    }
-}
\ No newline at end of file
diff --git a/unl2rdf-app/pom.xml b/unl-tools-app/pom.xml
similarity index 90%
rename from unl2rdf-app/pom.xml
rename to unl-tools-app/pom.xml
index 2c661efcf95d2a9336980a5f51b20df0a0937582..450e947391aaf51b1276ed61ba6907df113b4bd8 100644
--- a/unl2rdf-app/pom.xml
+++ b/unl-tools-app/pom.xml
@@ -10,8 +10,7 @@
         <version>1.0-SNAPSHOT</version>
     </parent>
 
-    <name>main-App</name>
-    <artifactId>unl2rdf-app</artifactId>
+    <artifactId>unl-tools-app</artifactId>
 
     <packaging>jar</packaging>
 
@@ -101,21 +100,10 @@
         </dependency>
         <dependency>
             <groupId>fr.tetras_libre.unl</groupId>
-            <artifactId>unl-parser</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>fr.tetras_libre.unl</groupId>
-            <artifactId>rdf</artifactId>
+            <artifactId>unl-tools-infrastructure</artifactId>
             <version>1.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-nop</artifactId>
-            <version>1.7.30</version>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 
 
diff --git a/unl2rdf-app/src/main/java/unl2rdf/Options.java b/unl-tools-app/src/main/java/unl2rdf/Options.java
similarity index 100%
rename from unl2rdf-app/src/main/java/unl2rdf/Options.java
rename to unl-tools-app/src/main/java/unl2rdf/Options.java
diff --git a/unl2rdf-app/src/main/java/unl2rdf/OutFileType.java b/unl-tools-app/src/main/java/unl2rdf/OutFileType.java
similarity index 100%
rename from unl2rdf-app/src/main/java/unl2rdf/OutFileType.java
rename to unl-tools-app/src/main/java/unl2rdf/OutFileType.java
diff --git a/unl2rdf-app/src/main/java/unl2rdf/Unl2Rdf.java b/unl-tools-app/src/main/java/unl2rdf/Unl2Rdf.java
similarity index 77%
rename from unl2rdf-app/src/main/java/unl2rdf/Unl2Rdf.java
rename to unl-tools-app/src/main/java/unl2rdf/Unl2Rdf.java
index fb796a3890e4f03f678f72ed3b41e97cf139b080..2761d69efabc367227fe6f839e378c1fd9e77698 100644
--- a/unl2rdf-app/src/main/java/unl2rdf/Unl2Rdf.java
+++ b/unl-tools-app/src/main/java/unl2rdf/Unl2Rdf.java
@@ -1,11 +1,11 @@
 package unl2rdf;
 
-import fr.tetras_libre.unltools.rdf.vocabulary.RdfFileBuilder;
 import fr.tetras_libre.unltools.unl.GraphExporter;
 import fr.tetras_libre.unltools.unl.UnlDocument;
+import fr.tetras_libre.unltools.unl.exporters.DefaultGraphExporterFactory;
+import fr.tetras_libre.unltools.unl.exporters.GraphExporterNames;
 import fr.tetras_libre.unltools.unl.parser.ParseException;
 import fr.tetras_libre.unltools.unl.parser.UnlParser;
-import fr.tetras_libre.unltools.unl.print.dotFile.DotFileBuilder;
 import picocli.CommandLine;
 
 import java.io.BufferedReader;
@@ -14,7 +14,9 @@ import java.io.IOException;
 import java.util.Vector;
 
 public class Unl2Rdf {
-    public static void main(String[] args) throws ParseException, IOException {
+
+    private static DefaultGraphExporterFactory defaultGraphExporterFactory = new DefaultGraphExporterFactory();
+    public static void main(String[] args) throws IOException, ParseException {
 
         Options options = new Options();
 
@@ -58,13 +60,17 @@ public class Unl2Rdf {
     }
 
     private static GraphExporter GetExporter(OutFileType fileType, FileWriter writer) {
+        String exporterName = null;
         switch (fileType) {
             case rdf:
-                return new RdfFileBuilder(writer);
+                exporterName = GraphExporterNames.RDF_EXPORTER;
+                break;
             case dot:
-                return new DotFileBuilder(writer);
+                exporterName = GraphExporterNames.DOT_EXPORTER;
+                break;
         }
-        throw new IllegalArgumentException(String.format("unknown fileType '%s", fileType.toString()));
+
+        return defaultGraphExporterFactory.createGraphExporter(writer, exporterName);
     }
 
 }
diff --git a/unl2rdf-app/src/main/resources/META-INF/MANIFEST.MF b/unl-tools-app/src/main/resources/META-INF/MANIFEST.MF
similarity index 100%
rename from unl2rdf-app/src/main/resources/META-INF/MANIFEST.MF
rename to unl-tools-app/src/main/resources/META-INF/MANIFEST.MF
diff --git a/unl2rdf-app/src/test/java/unl2rdf/Unl2RdfTest.java b/unl-tools-app/src/test/java/unl2rdf/Unl2RdfTest.java
similarity index 100%
rename from unl2rdf-app/src/test/java/unl2rdf/Unl2RdfTest.java
rename to unl-tools-app/src/test/java/unl2rdf/Unl2RdfTest.java
diff --git a/unl2rdf-app/unl2rdf-app.iml b/unl-tools-app/unl-tools-app.iml
similarity index 90%
rename from unl2rdf-app/unl2rdf-app.iml
rename to unl-tools-app/unl-tools-app.iml
index 11ac91247d26916d56b0b81938debd3e3cc3d25a..f7b5b3cb5757c81444b3202361e561f99827e677 100644
--- a/unl2rdf-app/unl2rdf-app.iml
+++ b/unl-tools-app/unl-tools-app.iml
@@ -5,19 +5,15 @@
     <output-test url="file://$MODULE_DIR$/target/test-classes" />
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
       <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
       <excludeFolder url="file://$MODULE_DIR$/target" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="Maven: fr.tetras_libre.rdf:rdf-unl-java-vocabulary:1.0-SNAPSHOT" level="project" />
-    <orderEntry type="library" scope="TEST" name="Maven: org.slf4j:slf4j-nop:1.7.30" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
     <orderEntry type="library" name="Maven: info.picocli:picocli:4.2.0" level="project" />
-    <orderEntry type="module" module-name="unl-parser" />
-    <orderEntry type="module" module-name="rdf" />
+    <orderEntry type="module" module-name="unl-tools-infrastructure" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-shacl:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-arq:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-core:3.14.0" level="project" />
@@ -49,9 +45,9 @@
     <orderEntry type="library" name="Maven: org.apache.jena:jena-dboe-base:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-dboe-index:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-rdfconnection:3.14.0" level="project" />
+    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.26" level="project" />
     <orderEntry type="library" name="Maven: fr.tetras_libre.rdf:rdf-unl-java-vocabulary:1.0-SNAPSHOT" level="project" />
-    <orderEntry type="library" scope="TEST" name="Maven: org.slf4j:slf4j-nop:1.7.30" level="project" />
-    <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
+    <orderEntry type="module" module-name="unl-tools-core" />
     <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.0-M1" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.0-M1" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
diff --git a/unl-parser/pom.xml b/unl-tools-core/pom.xml
similarity index 87%
rename from unl-parser/pom.xml
rename to unl-tools-core/pom.xml
index 1ab77b913090ab018cc1f5b2212ea422eeb40049..cbdcd7d89703384cc11fb49ce8392987f2055587 100644
--- a/unl-parser/pom.xml
+++ b/unl-tools-core/pom.xml
@@ -10,8 +10,6 @@
         <version>1.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>unl-parser</artifactId>
+    <artifactId>unl-tools-core</artifactId>
     <packaging>jar</packaging>
-
-    <name>Unl-parser</name>
 </project>
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/EqualsBuilder.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/EqualsBuilder.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/EqualsBuilder.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/EqualsBuilder.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/Graph.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/Graph.java
similarity index 91%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/Graph.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/Graph.java
index 28288be4e75e1a8c84402fc63629265a3808c027..6d710a787ace2d6bf8eb9736abd75c77703bf0d9 100644
--- a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/Graph.java
+++ b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/Graph.java
@@ -140,4 +140,21 @@ public class Graph {
         return strBuilder.toString();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if(null == obj) {
+            return false;
+        }
+
+        if(!(obj instanceof Graph)) {
+            return false;
+        }
+
+        return equals((Graph) obj);
+    }
+
+    private boolean equals(Graph other){
+        return this.nodes.equals(other.nodes)
+                && this.relations.equals(other.relations);
+    }
 }
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphExporter.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphExporter.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphExporter.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphExporter.java
diff --git a/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphExporterFactory.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphExporterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c9a32aa70687b5997478b0c1635f6e5f651a293
--- /dev/null
+++ b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphExporterFactory.java
@@ -0,0 +1,7 @@
+package fr.tetras_libre.unltools.unl;
+
+import java.io.Writer;
+
+public interface GraphExporterFactory {
+    GraphExporter createGraphExporter(Writer writer, String exporterName);
+}
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphNode.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphNode.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphNode.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphNode.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java
similarity index 63%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java
index bbd12483d008989f880e59c1965589b6b0eea689..7b56dc5279f05b3f20337ccf7b9fe91cebaa73d9 100644
--- a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java
+++ b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/GraphRelation.java
@@ -49,4 +49,32 @@ public class GraphRelation {
                 this.node1.toString(),
                 this.node2.toString());
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(null == obj) {
+            return false;
+        }
+
+        if(!(obj instanceof GraphRelation)) {
+            return false;
+        }
+
+        return equals((GraphRelation) obj);
+    }
+
+    private boolean equals(GraphRelation other) {
+        var equalityBuilder = new EqualsBuilder()
+                .append(this.node1)
+                .append(this.node2)
+                .append(this.SubGraphReferenceLabel)
+                .append(this.relationLabel);
+
+        return new EqualsBuilder()
+                .append(other.node1)
+                .append(other.node2)
+                .append(other.SubGraphReferenceLabel)
+                .append(other.relationLabel)
+                .isEqual(equalityBuilder);
+    }
 }
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/HashCodeBuilder.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/HashCodeBuilder.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/HashCodeBuilder.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/HashCodeBuilder.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/NoEntryNodeException.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/NoEntryNodeException.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/NoEntryNodeException.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/NoEntryNodeException.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/Restriction.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/Restriction.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/Restriction.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/Restriction.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNode.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNode.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNode.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNode.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UniversalWord.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UniversalWord.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UniversalWord.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UniversalWord.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UniversalWordNode.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UniversalWordNode.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UniversalWordNode.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UniversalWordNode.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java
similarity index 81%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java
index 816ab213fef0493639ed39a0aacb7f2ee1b9b199..eb5552965911d1fec14a3be07c5a2573f3de1241 100644
--- a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java
+++ b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocument.java
@@ -70,4 +70,21 @@ public class UnlDocument {
 
         return sb.toString();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(obj == null) {
+            return false;
+        }
+
+        if(!(obj instanceof UnlDocument)){
+            return false;
+        }
+        return equals((UnlDocument)obj);
+    }
+
+    private boolean equals(UnlDocument other){
+        return this.docElements.equals(other.docElements)
+                && (this.hasError() == other.hasError());
+    }
 }
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java
similarity index 78%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java
index 2b7084175a6e1a2adf762a2df5d9474f9a90e231..08e63d8380013bdc45a106e47bf14cb5b02664e3 100644
--- a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java
+++ b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNode.java
@@ -72,4 +72,25 @@ public class UnlDocumentNode {
 
         throw new IllegalStateException(String.format("Unknown type of sentence node '%s'", this.kindOfNode.toString()));
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if(null == obj){
+            return false;
+        }
+
+        if(!(obj instanceof UnlDocumentNode)){
+            return false;
+        }
+
+        return this.equals((UnlDocumentNode) obj);
+    }
+
+    private boolean equals(UnlDocumentNode other) {
+        return this.docNodeLabel.equals(other.docNodeLabel)
+                && this.kindOfNode.equals(other.kindOfNode)
+                && this.hasError() == other.hasError()
+                && this.graph.equals(other.graph);
+
+    }
 }
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNodeType.java b/unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNodeType.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNodeType.java
rename to unl-tools-core/src/main/java/fr/tetras_libre/unltools/unl/UnlDocumentNodeType.java
diff --git a/unl-tools-core/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java b/unl-tools-core/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9918265676dbd518249eac9869bf1cf15be69c03
--- /dev/null
+++ b/unl-tools-core/src/test/java/fr/tetras_libre/unltools/unl/EqualsBuilderTest.java
@@ -0,0 +1,24 @@
+package fr.tetras_libre.unltools.unl;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class EqualsBuilderTest {
+        @Test
+        void isEqual() {
+            String s1 = "qdfsf";
+            int i1 = 1;
+            long l1 = 2L;
+            EqualsBuilder builder = new EqualsBuilder()
+                    .append(s1)
+                    .append(i1)
+                    .append(l1);
+
+            EqualsBuilder builder2 = new EqualsBuilder()
+                    .append(s1)
+                    .append(i1)
+                    .append(l1);
+
+            Assertions.assertTrue(() -> builder.isEqual(builder2));
+        }
+}
\ No newline at end of file
diff --git a/unl-parser/unl-parser.iml b/unl-tools-core/unl-tools-core.iml
similarity index 100%
rename from unl-parser/unl-parser.iml
rename to unl-tools-core/unl-tools-core.iml
diff --git a/rdf/pom.xml b/unl-tools-infrastructure/pom.xml
similarity index 92%
rename from rdf/pom.xml
rename to unl-tools-infrastructure/pom.xml
index 5f2225f9a4bd1eed0e2180a1c31686e8ff63cf3e..537df2bcb9d23ca0659008738e030e12f130198c 100644
--- a/rdf/pom.xml
+++ b/unl-tools-infrastructure/pom.xml
@@ -11,9 +11,7 @@
     </parent>
 
 
-    <name>rdf2Unl</name>
-
-    <artifactId>rdf</artifactId>
+    <artifactId>unl-tools-infrastructure</artifactId>
     <packaging>jar</packaging>
 
     <dependencies>
@@ -23,12 +21,6 @@
             <type>pom</type>
             <version>3.14.0</version>
         </dependency>
-        <dependency>
-            <groupId>fr.tetras_libre.unl</groupId>
-            <artifactId>unl-parser</artifactId>
-            <version>1.0-SNAPSHOT</version>
-            <scope>compile</scope>
-        </dependency>
         <dependency>
             <groupId>fr.tetras_libre.rdf</groupId>
             <artifactId>rdf-unl-java-vocabulary</artifactId>
@@ -40,6 +32,12 @@
             <version>1.7.30</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>fr.tetras_libre.unl</groupId>
+            <artifactId>unl-tools-core</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
 
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphExtensions.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphExtensions.java
similarity index 95%
rename from rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphExtensions.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphExtensions.java
index 817f7091812c973bb9560e132b515786a7cf6b0b..73bf85de6124c9c7eacdbf95b41a9c2834988a45 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphExtensions.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphExtensions.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.unl.extensions;
+package fr.tetras_libre.unltools.unl;
 
 import fr.tetras_libre.unltools.unl.Graph;
 import fr.tetras_libre.unltools.unl.GraphRelation;
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphRelationExtensions.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphRelationExtensions.java
similarity index 93%
rename from rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphRelationExtensions.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphRelationExtensions.java
index 429f4c4691437edd236333bbec89cfe385c3bbc6..d66455aca3e056009f39172b9114523da3c06e7d 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/GraphRelationExtensions.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/GraphRelationExtensions.java
@@ -1,6 +1,4 @@
-package fr.tetras_libre.unltools.unl.extensions;
-
-import fr.tetras_libre.unltools.unl.*;
+package fr.tetras_libre.unltools.unl;
 
 public class GraphRelationExtensions {
     public static String getRealNode1Id(GraphRelation graphRelation, Graph g) throws NoEntryNodeException {
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/SubGraphReferenceNodeExtensions.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNodeExtensions.java
similarity index 92%
rename from rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/SubGraphReferenceNodeExtensions.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNodeExtensions.java
index 6a9f80d90f0f9addadb0307f79c879d6bb89a0b5..19144d2e570cec690bec0560e6020b25743ac4b6 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/unl/extensions/SubGraphReferenceNodeExtensions.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/SubGraphReferenceNodeExtensions.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.unl.extensions;
+package fr.tetras_libre.unltools.unl;
 
 import fr.tetras_libre.unltools.unl.Graph;
 import fr.tetras_libre.unltools.unl.NoEntryNodeException;
diff --git a/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/DefaultGraphExporterFactory.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/DefaultGraphExporterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb1259519a5af312d7fafeb85efaaacf346a9c06
--- /dev/null
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/DefaultGraphExporterFactory.java
@@ -0,0 +1,26 @@
+package fr.tetras_libre.unltools.unl.exporters;
+
+import fr.tetras_libre.unltools.unl.GraphExporter;
+import fr.tetras_libre.unltools.unl.GraphExporterFactory;
+import fr.tetras_libre.unltools.unl.exporters.dot.DotFileGraphExporter;
+import fr.tetras_libre.unltools.unl.exporters.rdf.RdfFileGraphExporter;
+
+import java.io.Writer;
+
+public class DefaultGraphExporterFactory implements GraphExporterFactory {
+    @Override
+    public GraphExporter createGraphExporter(Writer writer, String name) {
+        if (null == writer) throw new IllegalArgumentException("A writer should be provided");
+        if (null == name || name.isBlank()) throw new IllegalArgumentException("Graph exporter should be provided");
+
+        if (GraphExporterNames.RDF_EXPORTER.equals(name)) {
+            return new RdfFileGraphExporter(writer);
+        }
+
+        if (GraphExporterNames.DOT_EXPORTER.equals(name)) {
+            return new DotFileGraphExporter(writer);
+        }
+
+        throw new IllegalArgumentException(String.format("Unknown graph exporter name (current:'%s')", name));
+    }
+}
diff --git a/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/GraphExporterNames.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/GraphExporterNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..50a8355395539c0a5a8ada9249db5a842769e700
--- /dev/null
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/GraphExporterNames.java
@@ -0,0 +1,6 @@
+package fr.tetras_libre.unltools.unl.exporters;
+
+public class GraphExporterNames {
+    public static final String DOT_EXPORTER = "DOT";
+    public static final String RDF_EXPORTER = "RDF";
+}
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilder.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileGraphExporter.java
similarity index 97%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilder.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileGraphExporter.java
index 133bbd22145336f98bb042ac09ee3536153c6dc7..77105988e8891fe7eb1494c935e0d029715b39fa 100644
--- a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/print/dotFile/DotFileBuilder.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileGraphExporter.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.unl.print.dotFile;
+package fr.tetras_libre.unltools.unl.exporters.dot;
 
 import fr.tetras_libre.unltools.unl.*;
 
@@ -8,7 +8,7 @@ import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
-public class DotFileBuilder implements GraphExporter {
+public class DotFileGraphExporter implements GraphExporter {
     private final Writer writer;
 
     /**
@@ -17,7 +17,7 @@ public class DotFileBuilder implements GraphExporter {
      * @param writer the writer
      * @throws IllegalArgumentException the writer is null
      */
-    public DotFileBuilder(Writer writer) {
+    public DotFileGraphExporter(Writer writer) {
         if (null == writer) throw new IllegalArgumentException("The writer is null");
         this.writer = writer;
     }
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfFileBuilder.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfFileGraphExporter.java
similarity index 82%
rename from rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfFileBuilder.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfFileGraphExporter.java
index 856ffc633f78935f9cdafa5c457ecee66f45dc1d..c8c7364b29ac3f2b524c5c2c635aa480cb793b57 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfFileBuilder.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfFileGraphExporter.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 import fr.tetras_libre.unltools.unl.GraphExporter;
 import fr.tetras_libre.unltools.unl.UnlDocument;
@@ -8,11 +8,11 @@ import org.apache.jena.rdf.model.ModelFactory;
 import java.io.Writer;
 import java.util.List;
 
-public class RdfFileBuilder implements GraphExporter {
+public class RdfFileGraphExporter implements GraphExporter {
     private final UnlDocumentToRdfConverter converter;
     private final Writer writer;
 
-    public RdfFileBuilder(Writer writer) {
+    public RdfFileGraphExporter(Writer writer) {
         this.writer = writer;
         this.converter = new UnlDocumentToRdfConverter();
     }
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverter.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverter.java
similarity index 94%
rename from rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverter.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverter.java
index ac906bcc00bb80125fe18df87cf4c00c4ffb7310..87152027a7d342539b5278ea45db98c4ee6d4c3f 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/RelationLabelToUnlPropertyConverter.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverter.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 import org.apache.jena.ontology.ObjectProperty;
 import org.apache.jena.ontology.OntModel;
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/Slugify.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/Slugify.java
similarity index 73%
rename from rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/Slugify.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/Slugify.java
index 0fa0ed9b21544a98f35e257a9959133865481154..ef9d7178051a5d8f51b5f67d577dc4160231371b 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/Slugify.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/Slugify.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 class Slugify {
     public static String slugify(String stringSource) {
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UnlDocumentToRdfConverter.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UnlDocumentToRdfConverter.java
similarity index 95%
rename from rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UnlDocumentToRdfConverter.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UnlDocumentToRdfConverter.java
index 83d04c6db0a79b025b2ff708d624e858bf4ec31b..19667199cf77c4b3346bcc13d96f28d05689406a 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UnlDocumentToRdfConverter.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UnlDocumentToRdfConverter.java
@@ -1,9 +1,9 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 import fr.tetras_libre.rdf.vocabulary.UNL;
 import fr.tetras_libre.unltools.unl.*;
-import fr.tetras_libre.unltools.unl.extensions.GraphExtensions;
-import fr.tetras_libre.unltools.unl.extensions.SubGraphReferenceNodeExtensions;
+import fr.tetras_libre.unltools.unl.GraphExtensions;
+import fr.tetras_libre.unltools.unl.SubGraphReferenceNodeExtensions;
 import org.apache.jena.ontology.Individual;
 import org.apache.jena.ontology.OntModel;
 import org.apache.jena.ontology.Ontology;
@@ -15,13 +15,11 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
-import static fr.tetras_libre.unltools.rdf.vocabulary.Slugify.slugify;
-
 public class UnlDocumentToRdfConverter {
     private final static String DOMAIN = "http://rdf-unl.org";
     private final static String Lang = null;
 
-    private RelationLabelToUnlPropertyConverter relationLabelToUnlPropertyConverter;
+    private final RelationLabelToUnlPropertyConverter relationLabelToUnlPropertyConverter;
 
     public UnlDocumentToRdfConverter() {
         relationLabelToUnlPropertyConverter = new RelationLabelToUnlPropertyConverter();
@@ -78,7 +76,7 @@ public class UnlDocumentToRdfConverter {
                             var uw = (UniversalWordNode) sentenceNode;
 
                             // Création de l'universalWord dans le dictionnaire
-                            var universalWordIndividual = sentenceOntModel.createIndividual(String.format("%s/%s#%s", DOMAIN, uniqId, slugify(uw.getUniversalWord().toString())), UNL.UW_Lexeme);
+                            var universalWordIndividual = sentenceOntModel.createIndividual(String.format("%s/%s#%s", DOMAIN, uniqId, Slugify.slugify(uw.getUniversalWord().toString())), UNL.UW_Lexeme);
                             universalWordIndividual.addLabel(uw.getUniversalWord().toString(), UnlDocumentToRdfConverter.Lang);
 
                             // Création de l'occurence du l'universalWord
@@ -164,9 +162,9 @@ public class UnlDocumentToRdfConverter {
         public static String constructRelationUri(String scopeName, GraphRelation graphRelation) {
             return String.format("%s%s_%s_%s",
                     scopeName,
-                    slugify(constructRelationUri(graphRelation.getNode1())),
+                    Slugify.slugify(constructRelationUri(graphRelation.getNode1())),
                     graphRelation.getRelationLabel(),
-                    slugify(constructRelationUri(graphRelation.getNode2())));
+                    Slugify.slugify(constructRelationUri(graphRelation.getNode2())));
         }
 
         private static String constructRelationUri(GraphNode node) {
diff --git a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UtilGraphNodeUri.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UtilGraphNodeUri.java
similarity index 84%
rename from rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UtilGraphNodeUri.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UtilGraphNodeUri.java
index 9737041f4a44cd5141f3bc6d43826dba1896766c..f8d5e5c5d2952823d25c1dced45f9b5c51068e5b 100644
--- a/rdf/src/main/java/fr/tetras_libre/unltools/rdf/vocabulary/UtilGraphNodeUri.java
+++ b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/exporters/rdf/UtilGraphNodeUri.java
@@ -1,11 +1,11 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 import fr.tetras_libre.unltools.unl.GraphNode;
 import fr.tetras_libre.unltools.unl.SubGraphReferenceNode;
 import fr.tetras_libre.unltools.unl.UniversalWordNode;
-import fr.tetras_libre.unltools.unl.extensions.SubGraphReferenceNodeExtensions;
+import fr.tetras_libre.unltools.unl.SubGraphReferenceNodeExtensions;
 
-import static fr.tetras_libre.unltools.rdf.vocabulary.Slugify.slugify;
+import static fr.tetras_libre.unltools.unl.exporters.rdf.Slugify.slugify;
 
 class UtilGraphNodeUri {
     public static String constructGraphNodeUri(String namespace, GraphNode node) {
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/ParseException.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/ParseException.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/ParseException.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/ParseException.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/SimpleCharStream.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/SimpleCharStream.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/SimpleCharStream.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/SimpleCharStream.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/Token.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/Token.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/Token.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/Token.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/TokenMgrError.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/TokenMgrError.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/TokenMgrError.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/TokenMgrError.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.jj b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.jj
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.jj
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParser.jj
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserConstants.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserConstants.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserConstants.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserConstants.java
diff --git a/unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserTokenManager.java b/unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserTokenManager.java
similarity index 100%
rename from unl-parser/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserTokenManager.java
rename to unl-tools-infrastructure/src/main/java/fr/tetras_libre/unltools/unl/parser/UnlParserTokenManager.java
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractFlyweightUnlArgumentProvider.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractFlyweightUnlArgumentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f09f058b3c27203c99c9669a8e13ec43f58f65f5
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractFlyweightUnlArgumentProvider.java
@@ -0,0 +1,25 @@
+package fr.tetras_libre.unltools.unl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+public abstract class AbstractFlyweightUnlArgumentProvider extends AbstractUnlArgumentProvider {
+    private static final Dictionary<Class<?>, List<UnlDocument>> values = new Hashtable<>();
+
+    protected abstract List<UnlDocument> buildUnlDocuments();
+
+    final public List<UnlDocument> getUnlDocuments() {
+        if (null == values.get(this.getClass())) {
+            synchronized (AbstractFlyweightUnlArgumentProvider.class) {
+                if (null == values.get(this.getClass())) {
+                    values.put(this.getClass(), this.buildUnlDocuments());
+                }
+            }
+        }
+
+        return values.get(this.getClass());
+    }
+
+
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractUnlArgumentProvider.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractUnlArgumentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c9ac6b0b3171ad35bb0431bd53c49713ef0f930
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AbstractUnlArgumentProvider.java
@@ -0,0 +1,18 @@
+package fr.tetras_libre.unltools.unl;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+public abstract class AbstractUnlArgumentProvider implements ArgumentsProvider {
+
+    public abstract List<UnlDocument> getUnlDocuments();
+
+    @Override
+    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
+        return Stream.of(Arguments.of(getUnlDocuments()));
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AllUnl.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AllUnl.java
new file mode 100644
index 0000000000000000000000000000000000000000..106890ec237eb60ef0abd93fbd3caa633c761b4b
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/AllUnl.java
@@ -0,0 +1,18 @@
+package fr.tetras_libre.unltools.unl;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class AllUnl extends AbstractFlyweightUnlArgumentProvider {
+    @Override
+    protected List<UnlDocument> buildUnlDocuments() {
+        var unlDocument = new UnlDocument("[D]");
+
+        unlDocument.add(R1Unl.buildSentenceNode());
+        unlDocument.add(R2Unl.buildSentenceNode());
+        unlDocument.add(CatsUnl.buildSentenceNode());
+
+        return Collections.singletonList(unlDocument);
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/CatsUnl.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/CatsUnl.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6562094eb97d53be9f02df63ad450a72a3873ef
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/CatsUnl.java
@@ -0,0 +1,74 @@
+package fr.tetras_libre.unltools.unl;
+
+import java.util.*;
+
+public class CatsUnl extends AbstractFlyweightUnlArgumentProvider {
+    public static UnlDocumentNode buildSentenceNode() {
+        var graph = new Graph();
+
+        // mod
+        var restrictions = new TreeSet<Restriction>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("feline", new TreeSet<>(Collections.singletonList(new Restriction("icl", '>', new UniversalWord("thing", new TreeSet<>())))))));
+        var uw = new UniversalWord("cat1", restrictions);
+        GraphNode uwNode = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@def")), ":01");
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("adj", new TreeSet<>())));
+        uw = new UniversalWord("black", restrictions);
+        GraphNode uwNode2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(uwNode, uwNode2, "mod", ":01"));
+
+        // and
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("feline", new TreeSet<>(Collections.singletonList(new Restriction("icl", '>', new UniversalWord("thing", new TreeSet<>())))))));
+        uw = new UniversalWord("cat1", restrictions);
+        uwNode = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@def")), ":01");
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("feline", new TreeSet<>(Collections.singletonList(new Restriction("icl", '>', new UniversalWord("thing", new TreeSet<>())))))));
+        uw = new UniversalWord("cat2", restrictions);
+        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@def")), ":02");
+
+        graph.add(new GraphRelation(uwNode, uwNode2, "and", ":01"));
+
+        // mod
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("feline", new TreeSet<>(Collections.singletonList(new Restriction("icl", '>', new UniversalWord("thing", new TreeSet<>())))))));
+        uw = new UniversalWord("cat2", restrictions);
+        uwNode = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@def")), ":02");
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("adj", new TreeSet<>())));
+        uw = new UniversalWord("white", restrictions);
+        uwNode2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(uwNode, uwNode2, "mod", ":01"));
+
+
+        // agt
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("consume", new TreeSet<>(Collections.singletonList(new Restriction("icl", '>', new UniversalWord("do", new TreeSet<>())))))));
+        restrictions.add(new Restriction("agt", '>', new UniversalWord("living_thing", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("concrete_thing", new TreeSet<>())));
+        restrictions.add(new Restriction("ins", '>', new UniversalWord("thing", new TreeSet<>())));
+        uw = new UniversalWord("eat", restrictions);
+        uwNode = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@pl", ".@present", ".@progress")));
+
+        uwNode2 = new SubGraphReferenceNode(":01", new HashSet<>(Collections.singletonList(".@_hn-scope")));
+
+        graph.add(new GraphRelation(uwNode, uwNode2, "agt"));
+
+        return new UnlDocumentNode(UnlDocumentNodeType.Sentence, "[S:00]", graph);
+    }
+
+    protected List<UnlDocument> buildUnlDocuments() {
+        var unlDocument = new UnlDocument("[D]");
+
+        UnlDocumentNode unlDocumentNode = buildSentenceNode();
+
+        unlDocument.add(unlDocumentNode);
+
+        return Collections.singletonList(unlDocument);
+    }
+}
diff --git a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/CatUnlSentence.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R1Unl.java
similarity index 89%
rename from rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/CatUnlSentence.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R1Unl.java
index 3e6d86d168740f2284f48960fc570d52f66f3bae..323ed470cc681605c3db02e147ec9f9fd4530179 100644
--- a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/CatUnlSentence.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R1Unl.java
@@ -1,21 +1,25 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl;
 
-import fr.tetras_libre.unltools.unl.*;
+import java.util.*;
 
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.TreeSet;
+public class R1Unl extends AbstractFlyweightUnlArgumentProvider {
 
-public class CatUnlSentence {
-    private static List<UnlDocument> unlDocuments;
+    @Override
+    final protected List<UnlDocument> buildUnlDocuments() {
 
-    private static void buildUnlDocuments() {
         var unlDocument = new UnlDocument("[D]");
 
+        UnlDocumentNode unlDocumentNode = buildSentenceNode();
+
+        unlDocument.add(unlDocumentNode);
+
+        return Collections.singletonList(unlDocument);
+    }
+
+    public static UnlDocumentNode buildSentenceNode() {
         var graph = new Graph();
 
-        // aoj
+        // agt
         var restrictions = new TreeSet<Restriction>();
         restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
         restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
@@ -64,7 +68,7 @@ public class CatUnlSentence {
 
         restrictions = new TreeSet<>();
         restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
-        uw = new UniversalWord("radiowave", restrictions);
+        uw = new UniversalWord("channel", restrictions);
         uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
 
         graph.add(new GraphRelation(uwNode, uwNode2, "ben"));
@@ -120,7 +124,7 @@ public class CatUnlSentence {
         restrictions = new TreeSet<>();
         restrictions.add(new Restriction("icl", '>', new UniversalWord("sensing", new TreeSet<>())));
         uw = new UniversalWord("listening", restrictions);
-        uwNode2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@plu")));
+        uwNode2 = new UniversalWordNode(uw, new HashSet<>());
 
         graph.add(new GraphRelation(uwNode, uwNode2, "cnt"));
 
@@ -137,18 +141,6 @@ public class CatUnlSentence {
 
         graph.add(new GraphRelation(uwNode, uwNode2, "and"));
 
-        var unlDocumentNode = new UnlDocumentNode(UnlDocumentNodeType.Sentence, "[S:R1]", graph);
-
-        unlDocument.add(unlDocumentNode);
-
-        unlDocuments.add(unlDocument);
-    }
-
-    public static List<UnlDocument> getUnlDocuments() {
-        if (null == unlDocuments) {
-            buildUnlDocuments();
-        }
-
-        return unlDocuments;
+        return new UnlDocumentNode(UnlDocumentNodeType.Sentence, "[S:R1]", graph);
     }
 }
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R2Unl.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R2Unl.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b0ad0d6e42cfe27281f95fe89d649e7cae53083
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/R2Unl.java
@@ -0,0 +1,145 @@
+package fr.tetras_libre.unltools.unl;
+
+import java.util.*;
+
+public class R2Unl extends AbstractFlyweightUnlArgumentProvider {
+
+    @Override
+    final protected List<UnlDocument> buildUnlDocuments() {
+
+        var unlDocument = new UnlDocument("[D]");
+
+        UnlDocumentNode unlDocumentNode = buildSentenceNode();
+
+        unlDocument.add(unlDocumentNode);
+
+        return Collections.singletonList(unlDocument);
+    }
+
+    public static UnlDocumentNode buildSentenceNode() {
+        var graph = new Graph();
+
+        // agt
+        var restrictions = new TreeSet<Restriction>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("show", new TreeSet<>())));
+        restrictions.add(new Restriction("equ", '>', new UniversalWord("exhibit", new TreeSet<>())));
+        restrictions.add(new Restriction("agt", '>', new UniversalWord("thing", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
+        var uw = new UniversalWord("display", restrictions);
+        GraphNode node1 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@present")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("group", new TreeSet<>())));
+        uw = new UniversalWord("system", restrictions);
+        GraphNode node2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@def")));
+
+        graph.add(new GraphRelation(node1, node2, "agt"));
+
+        // mod
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
+        uw = new UniversalWord("channel", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("adj", new TreeSet<>())));
+        restrictions.add(new Restriction("iof", '>', new UniversalWord("color", new TreeSet<>())));
+        uw = new UniversalWord("green", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(node1, node2, "mod"));
+
+        // obj
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("show", new TreeSet<>())));
+        restrictions.add(new Restriction("equ", '>', new UniversalWord("exhibit", new TreeSet<>())));
+        restrictions.add(new Restriction("agt", '>', new UniversalWord("thing", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
+        uw = new UniversalWord("display", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@present")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
+        uw = new UniversalWord("channel", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
+
+        graph.add(new GraphRelation(node1, node2, "obj"));
+
+        // tim
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("show", new TreeSet<>())));
+        restrictions.add(new Restriction("equ", '>', new UniversalWord("exhibit", new TreeSet<>())));
+        restrictions.add(new Restriction("agt", '>', new UniversalWord("thing", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("thing", new TreeSet<>())));
+        uw = new UniversalWord("display", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@present")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("how", new TreeSet<>())));
+        restrictions.add(new Restriction("com", '>', new UniversalWord("always", new TreeSet<>())));
+        restrictions.add(new Restriction("tim", '<', new UniversalWord("uw", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("uw", new TreeSet<>())));
+        uw = new UniversalWord("when", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(node1, node2, "tim"));
+
+        // aoj
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("state", new TreeSet<>())));
+        uw = new UniversalWord("be_in_a_state", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@present")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("radiowave", new TreeSet<>())));
+        uw = new UniversalWord("channel", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>(Collections.singletonList(".@indef")));
+
+        graph.add(new GraphRelation(node1, node2, "aoj", ":01"));
+
+        // plc
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("attribute", new TreeSet<>())));
+        uw = new UniversalWord("state", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>());
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("message", new TreeSet<>())));
+        uw = new UniversalWord("broadcast", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(node1, node2, "plc", ":01"));
+
+        // obj
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("aoj", '>', new UniversalWord("thing", new TreeSet<>())));
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("be", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("state", new TreeSet<>())));
+        uw = new UniversalWord("be_in_a_state", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>(Arrays.asList(".@entry", ".@present")));
+
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("attribute", new TreeSet<>())));
+        uw = new UniversalWord("state", restrictions);
+        node2 = new UniversalWordNode(uw, new HashSet<>());
+
+        graph.add(new GraphRelation(node1, node2, "obj", ":01"));
+
+        // obj
+        restrictions = new TreeSet<>();
+        restrictions.add(new Restriction("icl", '>', new UniversalWord("how", new TreeSet<>())));
+        restrictions.add(new Restriction("com", '>', new UniversalWord("always", new TreeSet<>())));
+        restrictions.add(new Restriction("tim", '<', new UniversalWord("uw", new TreeSet<>())));
+        restrictions.add(new Restriction("obj", '>', new UniversalWord("uw", new TreeSet<>())));
+        uw = new UniversalWord("when", restrictions);
+        node1 = new UniversalWordNode(uw, new HashSet<>());
+
+        node2 = new SubGraphReferenceNode(":01");
+
+        graph.add(new GraphRelation(node1, node2, "obj"));
+
+        return new UnlDocumentNode(UnlDocumentNodeType.Sentence, "[S:R2]", graph);
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/UnlSentences.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/UnlSentences.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c4c52527cd1caedf7d7090a10d26d500f7080bb
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/UnlSentences.java
@@ -0,0 +1,96 @@
+package fr.tetras_libre.unltools.unl;
+
+public class UnlSentences {
+    public static final String R1 = "[D]\n" +
+            "[S:R1]\n" +
+            "{org:en}\n" +
+            "The system allows a radio channel to take on two states: Listening and Traffic.\n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "aoj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, system(icl>group).@def )\n" +
+            "obj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, take_on(aoj>thing, equ>assume,icl>change, obj>thing) )\n" +
+            "ben( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, channel(icl>radiowave).@indef)\n" +
+            "aoj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), channel(icl>radiowave).@indef ) \n" +
+            "obj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), state(icl>attribute).@plu )   \n" +
+            "qua( state(icl>attribute).@plu, 2 )   \n" +
+            "cnt( state(icl>attribute).@plu, listening(icl>sensing) )\n" +
+            "and( listening(icl>sensing),traffic(icl>communication) )\n" +
+            "{/unl}\n" +
+            "[/S]\n" +
+            "[/D]";
+
+    public static final String R2 = "[D]\n" +
+            "[S:R2]\n" +
+            "{org:en}\n" +
+            "The system displays a channel in green when it is in broadcast state. \n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "agt(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,system(icl>group).@def)\n" +
+            "mod(channel(icl>radiowave).@indef,green(icl>adj,iof>color))\n" +
+            "obj(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,channel(icl>radiowave).@indef)\n" +
+            "tim(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,when(icl>how,com>always,tim<uw,obj>uw))\n" +
+            "aoj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,channel(icl>radiowave).@indef)\n" +
+            "plc:01(state(icl>attribute),broadcast(icl>message))\n" +
+            "obj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,state(icl>attribute))\n" +
+            "obj(when(icl>how,com>always,tim<uw,obj>uw),:01)\n" +
+            "{/unl}\n" +
+            "[/S] \n" +
+            "[/D] \n";
+
+    public static final String Cats = "[D]" +
+            "[S:00]\n" +
+            "{org:en}\n" +
+            "The black cat and the white cat are eating.\n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "mod:01(cat1(icl>feline>thing):01.@entry.@def,black(icl>adj))\n" +
+            "and:01(cat1(icl>feline>thing):01.@entry.@def,cat2(icl>feline>thing):02.@entry.@def)\n" +
+            "mod:01(cat2(icl>feline>thing):02.@entry.@def,white(icl>adj))\n" +
+            "agt(eat(icl>consume>do,agt>living_thing,obj>concrete_thing,ins>thing).@entry.@pl.@present.@progress,:01.@_hn-scope)\n" +
+            "{/unl}\n" +
+            "[/S]\n";
+
+    public static final String All = "[D]" +
+            "[S:R1]\n" +
+            "{org:en}\n" +
+            "The system allows a radio channel to take on two states: Listening and Traffic.\n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "aoj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, system(icl>group).@def )\n" +
+            "obj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, take_on(aoj>thing, equ>assume,icl>change, obj>thing) )\n" +
+            "ben( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, channel(icl>radiowave).@indef)\n" +
+            "aoj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), channel(icl>radiowave).@indef) \n" +
+            "obj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), state(icl>attribute).@plu )   \n" +
+            "qua( state(icl>attribute).@plu, 2 )   \n" +
+            "cnt( state(icl>attribute).@plu, listening(icl>sensing) )\n" +
+            "and( listening(icl>sensing),traffic(icl>communication) )\n" +
+            "{/unl}\n" +
+            "[/S]\n" +
+            "[S:R2]\n" +
+            "{org:en}\n" +
+            "The system displays a channel in green when it is in broadcast state. \n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "agt(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,system(icl>group).@def)\n" +
+            "mod(channel(icl>radiowave).@indef,green(icl>adj,iof>color))\n" +
+            "obj(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,channel(icl>radiowave).@indef)\n" +
+            "tim(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,when(icl>how,com>always,tim<uw,obj>uw))\n" +
+            "aoj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,channel(icl>radiowave).@indef)\n" +
+            "plc:01(state(icl>attribute),broadcast(icl>message))\n" +
+            "obj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,state(icl>attribute))\n" +
+            "obj(when(icl>how,com>always,tim<uw,obj>uw),:01)\n" +
+            "{/unl}\n" +
+            "[/S] \n" +
+            "[S:00]\n" +
+            "{org:en}\n" +
+            "The black cat and the white cat are eating.\n" +
+            "{/org}\n" +
+            "{unl}\n" +
+            "mod:01(cat1(icl>feline>thing):01.@entry.@def,black(icl>adj))\n" +
+            "and:01(cat1(icl>feline>thing):01.@entry.@def,cat2(icl>feline>thing):02.@entry.@def)\n" +
+            "mod:01(cat2(icl>feline>thing):02.@entry.@def,white(icl>adj))\n" +
+            "agt(eat(icl>consume>do,agt>living_thing,obj>concrete_thing,ins>thing).@entry.@pl.@present.@progress,:01.@_hn-scope)\n" +
+            "{/unl}\n" +
+            "[/S]\n" +
+            "[/D]";
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/AllSetenceWithExpectedDotChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/AllSetenceWithExpectedDotChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d014e578ca4384825db0cbb2eedc01c9a79dbd6
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/AllSetenceWithExpectedDotChecker.java
@@ -0,0 +1,114 @@
+package fr.tetras_libre.unltools.unl.exporters.dot;
+
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class AllSetenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
+    private static final String ALL = UnlSentences.All;
+
+    private static final String ALL_DOT = "digraph G {\n" +
+            "graph [fontname=\"courier\", compound=\"true\"];\n" +
+            "1 [\n" +
+            "label=\"allow(aoj>thing,ben>thing,equ>make_possible,icl>be,obj>uw)\\n.@entry\"\n" +
+            "shape=\"box\"\n" +
+            "fontname=\"courb\"\n" +
+            "];\n" +
+            "2 [\n" +
+            "label=\"system(icl>group)\\n.@def\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "3 [\n" +
+            "label=\"take_on(aoj>thing,equ>assume,icl>change,obj>thing)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "4 [\n" +
+            "label=\"channel(icl>radiowave)\\n.@indef\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "5 [\n" +
+            "label=\"state(icl>attribute)\\n.@plu\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "6 [\n" +
+            "label=\"2\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "7 [\n" +
+            "label=\"listening(icl>sensing)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "8 [\n" +
+            "label=\"traffic(icl>communication)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "1 -> 2 [label=\"aoj\" ];\n" +
+            "1 -> 3 [label=\"obj\" ];\n" +
+            "1 -> 4 [label=\"ben\" ];\n" +
+            "3 -> 4 [label=\"aoj\" ];\n" +
+            "3 -> 5 [label=\"obj\" ];\n" +
+            "5 -> 6 [label=\"qua\" ];\n" +
+            "5 -> 7 [label=\"cnt\" ];\n" +
+            "7 -> 8 [label=\"and\" ];\n" +
+            "}\n" +
+            "digraph G {\n" +
+            "graph [fontname=\"courier\", compound=\"true\"];\n" +
+            "1 [\n" +
+            "label=\"display(agt>thing,equ>exhibit,icl>show,obj>thing)\\n.@entry.@present\"\n" +
+            "shape=\"box\"\n" +
+            "fontname=\"courb\"\n" +
+            "];\n" +
+            "2 [\n" +
+            "label=\"system(icl>group)\\n.@def\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "3 [\n" +
+            "label=\"channel(icl>radiowave)\\n.@indef\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "4 [\n" +
+            "label=\"green(icl>adj,iof>color)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "5 [\n" +
+            "label=\"when(com>always,icl>how,obj>uw,tim<uw)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "6 [\n" +
+            "label=\"be_in_a_state(aoj>thing,icl>be,obj>state)\\n.@entry.@present\"\n" +
+            "shape=\"box\"\n" +
+            "fontname=\"courb\"\n" +
+            "];\n" +
+            "7 [\n" +
+            "label=\"state(icl>attribute)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "8 [\n" +
+            "label=\"broadcast(icl>message)\\n\"\n" +
+            "shape=\"box\"\n" +
+            "];\n" +
+            "subgraph cluster_01{\n" +
+            "   color = black;\n" +
+            "   label = \":01\"6 -> 3 [label=\"aoj\" ];\n" +
+            "7 -> 8 [label=\"plc\" ];\n" +
+            "6 -> 7 [label=\"obj\" ];\n" +
+            "}\n" +
+            "1 -> 2 [label=\"agt\" ];\n" +
+            "3 -> 4 [label=\"mod\" ];\n" +
+            "1 -> 3 [label=\"obj\" ];\n" +
+            "1 -> 5 [label=\"tim\" ];\n" +
+            "5 -> 6 [label=\"obj\"  lhead=\"cluster_01\"];\n" +
+            "}\n" +
+            "digraph G {\n" +
+            "graph [fontname=\"courier\", compound=\"true\"];\n" +
+            "1 [ label=\"cat1(icl>feline(icl>thing)):01\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
+            "2 [ label=\"black(icl>adj)\\n\" shape=\"box\" ];\n" +
+            "3 [ label=\"cat2(icl>feline(icl>thing)):02\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
+            "4 [ label=\"white(icl>adj)\\n\" shape=\"box\" ];\n" +
+            "5 [ label=\"eat(agt>living_thing,icl>consume(icl>do),ins>thing,obj>concrete_thing)\\n.@entry.@present.@pl.@progress\" shape=\"box\" fontname=\"courb\" ];" +
+            " subgraph cluster_01{ color = black; label = \":01\"1 -> 2 [label=\"mod\" ]; 1 -> 3 [label=\"and\" ]; 3 -> 4 [label=\"mod\" ]; }\n" +
+            "5 -> 1 [label=\"agt\" lhead=\"cluster_01\"];\n" +
+            "}\n";
+
+    public AllSetenceWithExpectedDotChecker() {
+        super(ALL, ALL_DOT);
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/CatSentenceWithExpectedDotChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/CatSentenceWithExpectedDotChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a0e545c1e4ccbe6d90591dd1769126916b4f473
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/CatSentenceWithExpectedDotChecker.java
@@ -0,0 +1,23 @@
+package fr.tetras_libre.unltools.unl.exporters.dot;
+
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class CatSentenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
+
+    private static final String CAT_UNL = UnlSentences.Cats;
+
+    private static final String CAT_DOT = "digraph G {\n" +
+            "graph [fontname=\"courier\", compound=\"true\"];\n" +
+            "1 [ label=\"cat1(icl>feline(icl>thing)):01\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
+            "2 [ label=\"black(icl>adj)\\n\" shape=\"box\" ];\n" +
+            "3 [ label=\"cat2(icl>feline(icl>thing)):02\\n.@def.@entry\" shape=\"box\" fontname=\"courb\" ];\n" +
+            "4 [ label=\"white(icl>adj)\\n\" shape=\"box\" ];\n" +
+            "5 [ label=\"eat(agt>living_thing,icl>consume(icl>do),ins>thing,obj>concrete_thing)\\n.@entry.@present.@pl.@progress\" shape=\"box\" fontname=\"courb\" ];" +
+            " subgraph cluster_01{ color = black; label = \":01\"1 -> 2 [label=\"mod\" ]; 1 -> 3 [label=\"and\" ]; 3 -> 4 [label=\"mod\" ]; }\n" +
+            "5 -> 1 [label=\"agt\" lhead=\"cluster_01\"];\n" +
+            "}\n";
+
+    public CatSentenceWithExpectedDotChecker() {
+        super(CAT_UNL, CAT_DOT);
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileBuilderTest.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..daba156b658c145b6815099d0f69851f5130489f
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/DotFileBuilderTest.java
@@ -0,0 +1,49 @@
+package fr.tetras_libre.unltools.unl.exporters.dot;
+
+import fr.tetras_libre.unltools.unl.*;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.List;
+
+class DotFileBuilderTest {
+
+    @Test
+    public void r1ShouldBeConvertedToDotFileWithoutException() {
+        writeInFile(new R1Unl().getUnlDocuments(), new R1SentenceWithExpectedDotChecker());
+    }
+
+    @Test
+    public void r2ShouldBeConvertedToDotFileWithoutException() {
+        this.writeInFile(new R2Unl().getUnlDocuments(), new R2SentenceWithExpectedDotChecker());
+    }
+
+    @Test
+    public void CatsShouldBeConvertedToDotFileWithoutException() {
+        this.writeInFile(new CatsUnl().getUnlDocuments(), new CatSentenceWithExpectedDotChecker());
+    }
+
+    @Test
+    public void allShouldBeConvertedToDotFileWithoutException() {
+        this.writeInFile(new AllUnl().getUnlDocuments(), new AllSetenceWithExpectedDotChecker());
+    }
+
+    private void writeInFile(List<UnlDocument> unlDocuments, SentenceWithExpectedDotChecker sentenceChecker) {
+        // Startup
+        Writer strResult = new StringWriter();
+        DotFileGraphExporter dotFileBuilder = new DotFileGraphExporter(strResult);
+
+        // Test
+        Assertions.assertDoesNotThrow(() -> dotFileBuilder.write(unlDocuments));
+
+        // Teardown
+        Assertions.assertDoesNotThrow(() -> strResult.flush());
+        Assertions.assertTrue(() -> strResult.toString().length() != 0);
+        Assertions.assertDoesNotThrow(() -> sentenceChecker.setCurrentDotContent(strResult.toString()));
+        sentenceChecker.assetSameAsExpected();
+        Assertions.assertDoesNotThrow(() -> strResult.close());
+
+    }
+}
\ No newline at end of file
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R1SentenceWithExpectedDotChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R1SentenceWithExpectedDotChecker.java
similarity index 60%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R1SentenceWithExpectedDotChecker.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R1SentenceWithExpectedDotChecker.java
index 39601661872053bdbd59ee07a0e9206d05ad8d60..62f8bb45e2738b0d148596c28a639fc37e27e08a 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R1SentenceWithExpectedDotChecker.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R1SentenceWithExpectedDotChecker.java
@@ -1,23 +1,9 @@
-package fr.tetras_libre.unltools.unl;
+package fr.tetras_libre.unltools.unl.exporters.dot;
+
+import fr.tetras_libre.unltools.unl.UnlSentences;
 
 public class R1SentenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
-    private static final String R1_UNL = "[D]\n" +
-            "[S:R1]\n" +
-            "{org:en}\n" +
-            "The system allows a radio channel to take on two states: Listening and Traffic.\n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "aoj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, system(icl>group).@def )\n" +
-            "obj( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, take_on(aoj>thing, equ>assume,icl>change, obj>thing) )\n" +
-            "ben( allow(icl>be, aoj>thing, ben>thing, obj>uw, equ>make_possible).@entry, channel(icl>radiowave).@indef)\n" +
-            "aoj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), channel(icl>radiowave).@indef ) \n" +
-            "obj( take_on(aoj>thing, equ>assume, icl>change, obj>thing), state(icl>attribute).@plu )   \n" +
-            "qua( state(icl>attribute).@plu, 2 )   \n" +
-            "cnt( state(icl>attribute).@plu, listening(icl>sensing) )\n" +
-            "and( listening(icl>sensing),traffic(icl>communication) )\n" +
-            "{/unl}\n" +
-            "[/S]\n" +
-            "[/D]";
+    private static final String R1_UNL = UnlSentences.R1;
 
     private static final String R1_DOT = "digraph G {\n" +
             "graph [fontname=\"courier\", compound=\"true\"];\n" +
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R2SentenceWithExpectedDotChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R2SentenceWithExpectedDotChecker.java
similarity index 63%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R2SentenceWithExpectedDotChecker.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R2SentenceWithExpectedDotChecker.java
index 141d786c2a8d37075b65bcedaddf9c72c70ae143..374cc97df7dfe70b20263f70d5a99d615058653f 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/R2SentenceWithExpectedDotChecker.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/R2SentenceWithExpectedDotChecker.java
@@ -1,23 +1,9 @@
-package fr.tetras_libre.unltools.unl;
+package fr.tetras_libre.unltools.unl.exporters.dot;
+
+import fr.tetras_libre.unltools.unl.UnlSentences;
 
 public class R2SentenceWithExpectedDotChecker extends SentenceWithExpectedDotChecker {
-    private static final String R2_UNL = "[D]\n" +
-            "[S:R2]\n" +
-            "{org:en}\n" +
-            "The system displays a channel in green when it is in broadcast state. \n" +
-            "{/org}\n" +
-            "{unl}\n" +
-            "agt(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,system(icl>group).@def)\n" +
-            "mod(channel(icl>radiowave).@indef,green(icl>adj,iof>color))\n" +
-            "obj(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,channel(icl>radiowave).@indef)\n" +
-            "tim(display(icl>show,equ>exhibit,agt>thing,obj>thing).@entry.@present,when(icl>how,com>always,tim<uw,obj>uw))\n" +
-            "aoj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,channel(icl>radiowave).@indef)\n" +
-            "plc:01(state(icl>attribute),broadcast(icl>message))\n" +
-            "obj:01(be_in_a_state(aoj>thing,icl>be,obj>state).@entry.@present,state(icl>attribute))\n" +
-            "obj(when(icl>how,com>always,tim<uw,obj>uw),:01)\n" +
-            "{/unl}\n" +
-            "[/S] \n" +
-            "[/D] \n";
+    private static final String R2_UNL = UnlSentences.R2;
 
     private static final String R2_DOT = "digraph G {\n" +
             "graph [fontname=\"courier\", compound=\"true\"];\n" +
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotArgumentProvider.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotArgumentProvider.java
similarity index 64%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotArgumentProvider.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotArgumentProvider.java
index f8a239dfe1ffefd7480d0966b5cd3915d3085824..62451693695ae5291dd96a25fc9874829e73101c 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotArgumentProvider.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotArgumentProvider.java
@@ -1,5 +1,9 @@
-package fr.tetras_libre.unltools.unl;
+package fr.tetras_libre.unltools.unl.exporters.dot;
 
+import fr.tetras_libre.unltools.unl.exporters.dot.AllSetenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.CatSentenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.R1SentenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.R2SentenceWithExpectedDotChecker;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.ArgumentsProvider;
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotChecker.java
similarity index 79%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotChecker.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotChecker.java
index 93b691de260eab2c65d41b4e45f703c1b54145dd..e59ac0be81b363497640cde1c95b414168103347 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/SentenceWithExpectedDotChecker.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/dot/SentenceWithExpectedDotChecker.java
@@ -1,4 +1,4 @@
-package fr.tetras_libre.unltools.unl;
+package fr.tetras_libre.unltools.unl.exporters.dot;
 
 import org.junit.jupiter.api.Assertions;
 
@@ -19,18 +19,10 @@ public abstract class SentenceWithExpectedDotChecker {
         return this.unlSentence;
     }
 
-    protected void setUnlSentence(String unlSentence) {
-        this.unlSentence = null == unlSentence ? "" : unlSentence;
-    }
-
     public String getExpectedDotContent() {
         return this.expectedDotContent;
     }
 
-    public void setExpectedDotContent(String expectedDotContent) {
-        this.expectedDotContent = expectedDotContent;
-    }
-
     public String getCurrentDotContent() {
         return currentDotContent;
     }
diff --git a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfSchemaOfUNLTest.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfSchemaOfUNLTest.java
similarity index 67%
rename from rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfSchemaOfUNLTest.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfSchemaOfUNLTest.java
index 7e0ec8743d592a551f9cdf1e7df5afa6603263c4..701283599fd9e618f7dee6ebd678c5b61b799233 100644
--- a/rdf/src/test/java/fr/tetras_libre/unltools/rdf/vocabulary/RdfSchemaOfUNLTest.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RdfSchemaOfUNLTest.java
@@ -1,6 +1,7 @@
-package fr.tetras_libre.unltools.rdf.vocabulary;
+package fr.tetras_libre.unltools.unl.exporters.rdf;
 
 import fr.tetras_libre.rdf.vocabulary.UNL;
+import fr.tetras_libre.unltools.unl.R1Unl;
 import fr.tetras_libre.unltools.unl.UnlDocument;
 import org.apache.jena.rdf.model.ModelFactory;
 import org.junit.jupiter.api.Assertions;
@@ -11,21 +12,28 @@ import org.junit.jupiter.params.provider.ArgumentsSource;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
-import java.util.LinkedList;
+import java.util.List;
 
 class RdfSchemaOfUNLTest {
 
     @Test
-    void createRdfOfUnlSentence() throws IOException {
+    void createIndividualsWithSameUriGivesSameInstance() {
         var model = ModelFactory.createOntologyModel();
         var el1 = model.createIndividual("blablabla", UNL.UNL_Sentence);
         var el2 = model.createIndividual("blablabla", UNL.UNL_Sentence);
 
-        // egalité réference
-        var refEquals = el1 == el2;
+        // reference equality
+        Assertions.assertSame(el1, el2);
+
+        // content equality
+        Assertions.assertEquals(el1, el2);
+    }
 
-        // egalite de contenu
-        var contentEquals = el1.equals(el2);
+    @Test
+    void createRdfOfUnlSentence() throws IOException {
+        var model = ModelFactory.createOntologyModel();
+        var el1 = model.createIndividual("blablabla", UNL.UNL_Sentence);
+        var el2 = model.createIndividual("blablabla", UNL.UNL_Sentence);
 
         Writer writer = new StringWriter();
         model.write(writer, "TURTLE");
@@ -35,8 +43,8 @@ class RdfSchemaOfUNLTest {
     }
 
     @ParameterizedTest
-    @ArgumentsSource(R1SentenceWithExpectedRdf.class)
-    void createUnlDocument(LinkedList<UnlDocument> unlDocuments) throws IOException {
+    @ArgumentsSource(R1Unl.class)
+    void createUnlDocument(List<UnlDocument> unlDocuments) throws IOException {
         var unlDocumentToRdfConverter = new UnlDocumentToRdfConverter();
         var ontModels = unlDocumentToRdfConverter.convertToOntModel(unlDocuments);
 
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverterTest.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a7c80f97dea84ded2a3932f10dd180801cf1a6f
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/exporters/rdf/RelationLabelToUnlPropertyConverterTest.java
@@ -0,0 +1,29 @@
+package fr.tetras_libre.unltools.unl.exporters.rdf;
+
+import org.apache.jena.ontology.ObjectProperty;
+import org.apache.jena.ontology.OntModel;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+class RelationLabelToUnlPropertyConverterTest {
+
+    @Test
+    public void getObjectPropertyShouldGiveAssociatedUNLressource(){
+        var relationLabelToUnlProperty = new RelationLabelToUnlPropertyConverter();
+
+        Assertions.assertDoesNotThrow(() -> relationLabelToUnlProperty.getObjectProperty("aoj", null));
+    }
+
+    @Test
+    public void getObjectPropertyShouldNotThrowOnUnknownResource(){
+        AtomicReference<ObjectProperty> objectPropertyAtomicReference = new AtomicReference<>();
+        var relationLabelToUnlProperty = new RelationLabelToUnlPropertyConverter();
+        var unknownProperty = "dfadsfijoplknkjadfljgliukjelkjkdm";
+        var docModel = ModelFactory.createOntologyModel();
+
+        Assertions.assertDoesNotThrow(() -> objectPropertyAtomicReference.set(relationLabelToUnlProperty.getObjectProperty(unknownProperty, docModel)));
+    }
+}
\ No newline at end of file
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/AllSentenceWithExpectedUnlDocumentsChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/AllSentenceWithExpectedUnlDocumentsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..da29d161263291a6eda0e3651471db6586a2a165
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/AllSentenceWithExpectedUnlDocumentsChecker.java
@@ -0,0 +1,10 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.AllUnl;
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class AllSentenceWithExpectedUnlDocumentsChecker extends SentenceWithExpectedUnlDocumentsChecker {
+    public AllSentenceWithExpectedUnlDocumentsChecker() {
+        super(UnlSentences.All, new AllUnl().getUnlDocuments());
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/CatsSentenceWithExpectedUnlDocumentsChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/CatsSentenceWithExpectedUnlDocumentsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..51b46f7da87e32ae411d8aa0a45b6a9c55773d12
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/CatsSentenceWithExpectedUnlDocumentsChecker.java
@@ -0,0 +1,10 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.CatsUnl;
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class CatsSentenceWithExpectedUnlDocumentsChecker extends SentenceWithExpectedUnlDocumentsChecker {
+    public CatsSentenceWithExpectedUnlDocumentsChecker() {
+        super(UnlSentences.Cats, new CatsUnl().getUnlDocuments());
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R1SentenceWithExpectedUnlDocumentsChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R1SentenceWithExpectedUnlDocumentsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6aa89b3d068946c1e7b6be56276e949ba3b3e43
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R1SentenceWithExpectedUnlDocumentsChecker.java
@@ -0,0 +1,10 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.R1Unl;
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class R1SentenceWithExpectedUnlDocumentsChecker extends SentenceWithExpectedUnlDocumentsChecker {
+    public R1SentenceWithExpectedUnlDocumentsChecker() {
+        super(UnlSentences.R1, new R1Unl().getUnlDocuments());
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R2SentenceWithExpectedUnlDocumentsChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R2SentenceWithExpectedUnlDocumentsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..15fdeb5ed60b5895e6ce6848770d32cafc3e2593
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/R2SentenceWithExpectedUnlDocumentsChecker.java
@@ -0,0 +1,10 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.R2Unl;
+import fr.tetras_libre.unltools.unl.UnlSentences;
+
+public class R2SentenceWithExpectedUnlDocumentsChecker extends SentenceWithExpectedUnlDocumentsChecker {
+    public R2SentenceWithExpectedUnlDocumentsChecker() {
+        super(UnlSentences.R2, new R2Unl().getUnlDocuments());
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsArgumentProvider.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsArgumentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3d7f6ba1249646b6b984bbe3808a220d82d6fda
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsArgumentProvider.java
@@ -0,0 +1,21 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.exporters.dot.AllSetenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.CatSentenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.R1SentenceWithExpectedDotChecker;
+import fr.tetras_libre.unltools.unl.exporters.dot.R2SentenceWithExpectedDotChecker;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+
+import java.util.stream.Stream;
+
+public class SentenceWithExpectedUnlDocumentsArgumentProvider implements ArgumentsProvider {
+    @Override
+    public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
+        return Stream.of(Arguments.of(new R1SentenceWithExpectedDotChecker()),
+                Arguments.of(new R2SentenceWithExpectedDotChecker()),
+                Arguments.of(new CatSentenceWithExpectedDotChecker()),
+                Arguments.of(new AllSetenceWithExpectedDotChecker()));
+    }
+}
diff --git a/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsChecker.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsChecker.java
new file mode 100644
index 0000000000000000000000000000000000000000..6aebfb090b220c1edb6b0fafdf0b8b6736ff9f1f
--- /dev/null
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/SentenceWithExpectedUnlDocumentsChecker.java
@@ -0,0 +1,40 @@
+package fr.tetras_libre.unltools.unl.parser;
+
+import fr.tetras_libre.unltools.unl.UnlDocument;
+import org.junit.jupiter.api.Assertions;
+
+import java.util.List;
+
+public class SentenceWithExpectedUnlDocumentsChecker {
+    private final List<UnlDocument> expectedUnlDocuments;
+    private String unlSentence;
+    private List<UnlDocument> currentUnlDocuments;
+
+    public SentenceWithExpectedUnlDocumentsChecker(String unlSentence, List<UnlDocument> expectedUnlDocuments) {
+        if ((null == unlSentence) || (null == expectedUnlDocuments)) {
+            throw new IllegalArgumentException();
+        }
+        this.unlSentence = unlSentence;
+        this.expectedUnlDocuments = expectedUnlDocuments;
+    }
+
+    public String getUnlSentence() {
+        return this.unlSentence;
+    }
+
+    public List<UnlDocument> getExpectedDotContent() {
+        return this.expectedUnlDocuments;
+    }
+
+    public List<UnlDocument> getCurrentDotContent() {
+        return this.currentUnlDocuments;
+    }
+
+    public void setCurrentUnlDocuments(List<UnlDocument> currentUnlDocuments) {
+        this.currentUnlDocuments = currentUnlDocuments;
+    }
+
+    public void assetSameAsExpected() {
+        Assertions.assertIterableEquals(this.expectedUnlDocuments, this.currentUnlDocuments);
+    }
+}
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactory.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactory.java
similarity index 100%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactory.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactory.java
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactoryFromString.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactoryFromString.java
similarity index 100%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactoryFromString.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserFactoryFromString.java
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java
similarity index 55%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java
index ff62eaa5989bf05b825fbd28fc1d9ddd344dc933..28dd3e0cf93a31e05bbbdd17c5050e82f02bd47e 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/UnlParserTest.java
@@ -1,28 +1,23 @@
 package fr.tetras_libre.unltools.unl.parser;
 
-import fr.tetras_libre.unltools.unl.SentenceWithExpectedDotArgumentProvider;
-import fr.tetras_libre.unltools.unl.SentenceWithExpectedDotChecker;
 import fr.tetras_libre.unltools.unl.UnlDocument;
+import fr.tetras_libre.unltools.unl.UnlSentences;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.junit.jupiter.params.provider.ArgumentsSources;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
 
 class UnlParserTest {
 
     @ParameterizedTest
-    @ArgumentsSource(SentenceWithExpectedDotArgumentProvider.class)
-    void UnlParserShouldParseSimpleSentenceWithoutException(SentenceWithExpectedDotChecker input) {
-        UnlParserFactory unlFactory = new UnlParserFactoryFromString(input.getUnlSentence());
-        createAndCheckUnlParserValidity(unlFactory);
-    }
-
-    @ParameterizedTest
-    @ArgumentsSource(SentenceWithExpectedDotArgumentProvider.class)
-    void UnlDocumentsShouldBeSerializedToStringWithoutException(SentenceWithExpectedDotChecker input) {
-        UnlParserFactory unlFactory = new UnlParserFactoryFromString(input.getUnlSentence());
-        WrapperUnlParser wrapper = createAndCheckUnlParserValidity(unlFactory);
+    @ValueSource(strings = {UnlSentences.R1, UnlSentences.R2, UnlSentences.Cats, UnlSentences.All})
+    void unlParserShouldParseSimpleSentenceWithoutException(String input) {
+        UnlParserFactory unlFactory = new UnlParserFactoryFromString(input);
+        var wrapper = createAndCheckUnlParserValidity(unlFactory);
 
         for (UnlDocument unlDocument : wrapper.getUnlDocumentList()) {
             AtomicReference<String> stringOfUnlDocument = new AtomicReference<>();
@@ -48,4 +43,23 @@ class UnlParserTest {
         return unlParser.get();
     }
 
+
+
+    @ParameterizedTest
+    @MethodSource
+    void unlParserShouldParseSentenceAndGetExpectedUnlDocuments(SentenceWithExpectedUnlDocumentsChecker input) {
+        UnlParserFactory unlFactory = new UnlParserFactoryFromString(input.getUnlSentence());
+        var wrapper = createAndCheckUnlParserValidity(unlFactory);
+
+        input.setCurrentUnlDocuments(wrapper.getUnlDocumentList());
+        input.assetSameAsExpected();
+    }
+
+    private static Stream<SentenceWithExpectedUnlDocumentsChecker> unlParserShouldParseSentenceAndGetExpectedUnlDocuments(){
+        return Stream.of(new R1SentenceWithExpectedUnlDocumentsChecker(),
+                new R2SentenceWithExpectedUnlDocumentsChecker(),
+                new CatsSentenceWithExpectedUnlDocumentsChecker(),
+                new AllSentenceWithExpectedUnlDocumentsChecker());
+    }
+
 }
\ No newline at end of file
diff --git a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java
similarity index 66%
rename from unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java
rename to unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java
index 054fa22bcb57b8c828df85aab5574cca994b436d..97c487f78ef0d51b8dab9343b510dcdf93f4bc35 100644
--- a/unl-parser/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java
+++ b/unl-tools-infrastructure/src/test/java/fr/tetras_libre/unltools/unl/parser/WrapperUnlParser.java
@@ -15,18 +15,30 @@ public class WrapperUnlParser {
         this.errors = null;
     }
 
+    /**
+     * get UnlDocuments once the parsing is done
+     * @return A list of UnlDocuments
+     */
     public Vector<UnlDocument> getUnlDocumentList() {
-        initializeIfNecessary();
+        parseContentIfNecessary();
         return this.documents;
     }
 
-    private void initializeIfNecessary() {
+    /**
+     * Parse the content with the local parser if not already done.
+     */
+    private void parseContentIfNecessary() {
         if (null == this.documents) {
-            initialize();
+            parseContentAndRetrieveErrors();
         }
     }
 
-    private void initialize() {
+
+    /**
+     * Parse the content with the local parser.
+     * Retrieves also errors during parsing
+     */
+    private void parseContentAndRetrieveErrors() {
         this.errors = new ErrorCollection(false);
         try {
             this.documents = this.parser.createUnlDocumentList();
@@ -40,10 +52,13 @@ public class WrapperUnlParser {
             return;
         }
 
-        fillErrors();
+        getUnlDocumentsErrors();
     }
 
-    private void fillErrors() {
+    /**
+     * retrieves errors within documents
+     */
+    private void getUnlDocumentsErrors() {
         for (UnlDocument document : this.documents) {
             if (document.HasError()) {
                 this.errors.addError(document.getError());
@@ -51,13 +66,21 @@ public class WrapperUnlParser {
         }
     }
 
+    /**
+     * An error as occured during the parsing
+     * @return true if an error occured, false otherwise
+     */
     public boolean hasError() {
-        initializeIfNecessary();
+        parseContentIfNecessary();
         return errors.hasError;
     }
 
+    /**
+     * Get errors of parsing
+     * @return A vector of errors
+     */
     public Vector<String> getErrors() {
-        initializeIfNecessary();
+        parseContentIfNecessary();
         return errors.errors;
     }
 
@@ -71,7 +94,7 @@ public class WrapperUnlParser {
         }
 
         public void addError(String error) {
-            hasError = true;
+            this.hasError = true;
             if (!this.errors.contains(error)) {
                 this.errors.add(error);
             }
diff --git a/rdf/rdf.iml b/unl-tools-infrastructure/unl-tools-infrastructure.iml
similarity index 97%
rename from rdf/rdf.iml
rename to unl-tools-infrastructure/unl-tools-infrastructure.iml
index deff3312d1d1ac24100482078914fcd3a8045f3e..095605ea887b71d3fd448e107c5a36bbf7367980 100644
--- a/rdf/rdf.iml
+++ b/unl-tools-infrastructure/unl-tools-infrastructure.iml
@@ -11,7 +11,6 @@
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="rdf-vocabulary-1.0-SNAPSHOT" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-shacl:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-arq:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-core:3.14.0" level="project" />
@@ -44,9 +43,9 @@
     <orderEntry type="library" name="Maven: org.apache.jena:jena-dboe-index:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.jena:jena-rdfconnection:3.14.0" level="project" />
     <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.26" level="project" />
-    <orderEntry type="module" module-name="unl-parser" />
     <orderEntry type="library" name="Maven: fr.tetras_libre.rdf:rdf-unl-java-vocabulary:1.0-SNAPSHOT" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.slf4j:slf4j-nop:1.7.30" level="project" />
+    <orderEntry type="module" module-name="unl-tools-core" />
     <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.0-M1" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.0-M1" level="project" />
     <orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
diff --git a/unlTools.iml b/unlTools.iml
new file mode 100644
index 0000000000000000000000000000000000000000..a10c5f4e27a534c8a61e3aee63c689c48e514499
--- /dev/null
+++ b/unlTools.iml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.0-M1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.0-M1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.7.0-M1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.7.0-M1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.7.0-M1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.7.0-M1" level="project" />
+  </component>
+</module>
\ No newline at end of file