diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 34d4b879179990ab1ff26faf7d5e13d14aa11eda..ea5b480b26afda4f096ae0b19a961e9e15858b11 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,7 +21,7 @@ build:
     - ./gradlew --console=plain --no-daemon assemble jar
   artifacts:
     paths:
-      - "/builds/jastadd/ragconnect/ragconnect.base/build/libs/ragconnect-*.jar"
+      - "ragconnect.base/build/libs/ragconnect-*.jar"
     expire_in: 1 week
 
 test:
@@ -34,7 +34,7 @@ test:
     - ./gradlew --console=plain --no-daemon allTests
   artifacts:
     reports:
-      junit: "/builds/jastadd/ragconnect/ragconnect.tests/build/test-results/test/TEST-*.xml"
+      junit: "ragconnect.tests/build/test-results/test/**/TEST-*.xml"
     expire_in: 1 week
 
 publish:
diff --git a/ragconnect.base/src/main/jastadd/Analysis.jrag b/ragconnect.base/src/main/jastadd/Analysis.jrag
index a1367a9c21bf2f293dd33216ea20a8941f5411b5..f3724eab6217b2e76472ee57f8f341ab31730861 100644
--- a/ragconnect.base/src/main/jastadd/Analysis.jrag
+++ b/ragconnect.base/src/main/jastadd/Analysis.jrag
@@ -12,6 +12,19 @@ aspect Analysis {
     return result;
   }
 
+  // --- lookupTypeEndpointDefinition ---
+  inh java.util.List<TypeEndpointDefinition> TypeEndpointDefinition.lookupTypeEndpointDefinitions(TypeComponent type);
+  eq RagConnect.getEndpointDefinition().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
+  syn java.util.List<TypeEndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
+    java.util.List<TypeEndpointDefinition> result = new java.util.ArrayList<>();
+    for (EndpointDefinition def : getEndpointDefinitionList()) {
+      if (def.isTypeEndpointDefinition() && def.asTypeEndpointDefinition().getType().equals(type)) {
+        result.add(def.asTypeEndpointDefinition());
+      }
+    }
+    return result;
+  }
+
   // --- lookupDependencyDefinition ---
   inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
   eq RagConnect.getDependencyDefinition().lookupDependencyDefinition(TypeDecl source, String id) {
@@ -31,10 +44,12 @@ aspect Analysis {
   }
   syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
 
+  // --- matchesType ---
   syn boolean TokenEndpointDefinition.matchesType(TokenEndpointDefinition other);
   eq ReceiveTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isReceiveTokenEndpointDefinition();
   eq SendTokenEndpointDefinition.matchesType(TokenEndpointDefinition other) = other.isSendTokenEndpointDefinition();
 
+  // --- assignableTo ---
   syn boolean MappingDefinitionType.assignableTo(JavaTypeUse target);
   eq JavaMappingDefinitionType.assignableTo(JavaTypeUse target) = getType().assignableTo(target);
   eq JavaArrayMappingDefinitionType.assignableTo(JavaTypeUse target) {
@@ -65,6 +80,7 @@ aspect Analysis {
 
   // --- shouldSendValue ---
   syn boolean TokenEndpointDefinition.shouldSendValue() = isSendTokenEndpointDefinition() && !getToken().getNTA();
+  syn boolean TypeEndpointDefinition.shouldSendValue() = isSendTypeEndpointDefinition() && !getType().getNTA();
 
   // --- needProxyToken ---
   syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointDefinitionList().stream().anyMatch(TokenEndpointDefinition::shouldSendValue);
diff --git a/ragconnect.base/src/main/jastadd/Errors.jrag b/ragconnect.base/src/main/jastadd/Errors.jrag
index 88f3aece6a64041a95f2784be4882d01008ff8c5..c3ecc643a4edc8319da82f49438dc46f167a3618 100644
--- a/ragconnect.base/src/main/jastadd/Errors.jrag
+++ b/ragconnect.base/src/main/jastadd/Errors.jrag
@@ -32,6 +32,10 @@ aspect Errors {
       when isAlreadyDefined()
       to RagConnect.errors();
 
+    SendTypeEndpointDefinition contributes error("At least one mapping needs to be used")
+      when effectiveMappings().isEmpty()
+      to RagConnect.errors();
+
     DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
       when isAlreadyDefined()
       to RagConnect.errors();
diff --git a/ragconnect.base/src/main/jastadd/NameResolution.jrag b/ragconnect.base/src/main/jastadd/NameResolution.jrag
index f282ba55983feeae814e12823ff31f1d6480b6a8..253f1dabf8ed0f15e2483aafde9144d992d87cf2 100644
--- a/ragconnect.base/src/main/jastadd/NameResolution.jrag
+++ b/ragconnect.base/src/main/jastadd/NameResolution.jrag
@@ -11,4 +11,20 @@ aspect NameResolution {
     return null;
   }
 
+  refine RefResolverStubs eq ASTNode.globallyResolveTypeComponentByToken(String id) {
+    // return a TypeComponent. id is of the form 'parent_type_name + "." + child_type_name'
+    int dotIndex = id.indexOf(".");
+    String parentTypeName = id.substring(0, dotIndex);
+    String childTypeName = id.substring(dotIndex + 1);
+    TypeDecl type = program().resolveTypeDecl(parentTypeName);
+    // iterate over components and find the matching typeComponent
+    for (Component comp : type.getComponentList()) {
+      if (comp.isTypeComponent() && comp.getName().equals(childTypeName)) {
+        return comp.asTypeComponent();
+      }
+    }
+    System.err.println("Could not resolve TypeComponent '" + id + "'.");
+    return null;
+  }
+
 }
diff --git a/ragconnect.base/src/main/jastadd/Navigation.jrag b/ragconnect.base/src/main/jastadd/Navigation.jrag
index 1c2c4af4a40db2ff7ffb0bf962716d515553fd97..c6432a14d9258795f1f402366ec5c418993d4aa1 100644
--- a/ragconnect.base/src/main/jastadd/Navigation.jrag
+++ b/ragconnect.base/src/main/jastadd/Navigation.jrag
@@ -27,13 +27,13 @@ aspect Navigation {
   syn TokenEndpointDefinition EndpointDefinition.asTokenEndpointDefinition() = null;
   eq TokenEndpointDefinition.asTokenEndpointDefinition() = this;
 
-  // --- isSendTokenEndpointDefinition ---
-  syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false;
-  eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true;
+  // --- isTypeEndpointDefinition ---
+  syn boolean EndpointDefinition.isTypeEndpointDefinition() = false;
+  eq TypeEndpointDefinition.isTypeEndpointDefinition() = true;
 
-  // --- asSendTokenEndpointDefinition ---
-  syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null;
-  eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this;
+  // --- asTypeEndpointDefinition ---
+  syn TypeEndpointDefinition EndpointDefinition.asTypeEndpointDefinition() = null;
+  eq TypeEndpointDefinition.asTypeEndpointDefinition() = this;
 
   // --- isReceiveTokenEndpointDefinition ---
   syn boolean EndpointDefinition.isReceiveTokenEndpointDefinition() = false;
@@ -43,6 +43,30 @@ aspect Navigation {
   syn ReceiveTokenEndpointDefinition EndpointDefinition.asReceiveTokenEndpointDefinition() = null;
   eq ReceiveTokenEndpointDefinition.asReceiveTokenEndpointDefinition() = this;
 
+  // --- isSendTokenEndpointDefinition ---
+  syn boolean EndpointDefinition.isSendTokenEndpointDefinition() = false;
+  eq SendTokenEndpointDefinition.isSendTokenEndpointDefinition() = true;
+
+  // --- asSendTokenEndpointDefinition ---
+  syn SendTokenEndpointDefinition EndpointDefinition.asSendTokenEndpointDefinition() = null;
+  eq SendTokenEndpointDefinition.asSendTokenEndpointDefinition() = this;
+
+  // --- isReceiveTypeEndpointDefinition ---
+  syn boolean EndpointDefinition.isReceiveTypeEndpointDefinition() = false;
+  eq ReceiveTypeEndpointDefinition.isReceiveTypeEndpointDefinition() = true;
+
+  // --- asReceiveTypeEndpointDefinition ---
+  syn ReceiveTypeEndpointDefinition EndpointDefinition.asReceiveTypeEndpointDefinition() = null;
+  eq ReceiveTypeEndpointDefinition.asReceiveTypeEndpointDefinition() = this;
+
+  // --- isSendTypeEndpointDefinition ---
+  syn boolean EndpointDefinition.isSendTypeEndpointDefinition() = false;
+  eq SendTypeEndpointDefinition.isSendTypeEndpointDefinition() = true;
+
+  // --- asSendTypeEndpointDefinition ---
+  syn SendTypeEndpointDefinition EndpointDefinition.asSendTypeEndpointDefinition() = null;
+  eq SendTypeEndpointDefinition.asSendTypeEndpointDefinition() = this;
+
   // --- targetEndpointDefinition ---
   syn SendTokenEndpointDefinition DependencyDefinition.targetEndpointDefinition() {
     // resolve definition in here, as we do not need resolveMethod in any other place (yet)
diff --git a/ragconnect.base/src/main/jastadd/RagConnect.relast b/ragconnect.base/src/main/jastadd/RagConnect.relast
index 9958106ba82c6efe32126c9f7b795ac7c66378e6..bd3e5c0d964881a7d7d49a68da83f1b27b79d0a7 100644
--- a/ragconnect.base/src/main/jastadd/RagConnect.relast
+++ b/ragconnect.base/src/main/jastadd/RagConnect.relast
@@ -10,6 +10,12 @@ rel TokenEndpointDefinition.Token <-> TokenComponent.TokenEndpointDefinition*;
 ReceiveTokenEndpointDefinition : TokenEndpointDefinition;
 SendTokenEndpointDefinition : TokenEndpointDefinition;
 
+abstract TypeEndpointDefinition : EndpointDefinition;
+rel TypeEndpointDefinition.Type <-> TypeComponent.TypeEndpointDefinition*;
+
+ReceiveTypeEndpointDefinition : TypeEndpointDefinition;
+SendTypeEndpointDefinition : TypeEndpointDefinition;
+
 DependencyDefinition ::= <ID>;
 rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
 rel DependencyDefinition.Target -> TokenComponent;
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
index 9ee95b7ba4d87ecbaf60a75271dadcd82e38d190..00ef291fa015596647a6f15756ce6f4380929501 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
+++ b/ragconnect.base/src/main/jastadd/intermediate/Generation.jadd
@@ -18,29 +18,42 @@ aspect AttributesForMustache {
   // --- MEndpointDefinition ---
   syn String MEndpointDefinition.preemptiveExpectedValue();
   syn String MEndpointDefinition.preemptiveReturn();
-  syn TokenEndpointDefinition MEndpointDefinition.endpointDef();
+  syn EndpointDefinition MEndpointDefinition.endpointDef();
   syn String MEndpointDefinition.firstInputVarName();
+  syn String MEndpointDefinition.parentTypeName();
+  syn String MEndpointDefinition.entityName();
 
   eq MEndpointDefinition.getInnerMappingDefinition(int i).isLast() = i == getNumInnerMappingDefinition() - 1;
   eq MEndpointDefinition.getInnerMappingDefinition(int i).inputVarName() = i == 0 ? firstInputVarName() : getInnerMappingDefinition(i - 1).outputVarName();
 
   syn String MEndpointDefinition.connectParameterName() = "uriString";
-  syn String MEndpointDefinition.connectMethod() = "connect" + tokenName();
+  syn String MEndpointDefinition.connectMethod() = "connect" + entityName();
 
   syn String MEndpointDefinition.disconnectMethod() {
     // if both (send and receive) are defined for the token, ensure methods with different names
-    String extra = endpointDef().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
-    return "disconnect" + extra + tokenName();
+    String extra;
+    if (endpointDef().isTokenEndpointDefinition()) {
+      extra = endpointDef().asTokenEndpointDefinition().lookupTokenEndpointDefinitions(token()).size() > 1 ? uniqueSuffix() : "";
+    } else if (endpointDef().isTypeEndpointDefinition()) {
+      extra = endpointDef().asTypeEndpointDefinition().lookupTypeEndpointDefinitions(type()).size() > 1 ? uniqueSuffix() : "";
+    } else {
+      extra = "";
+    }
+    return "disconnect" + extra + entityName();
   }
-  //
+
   syn String MEndpointDefinition.uniqueSuffix();
-  eq MSendDefinition.uniqueSuffix() = "Send";
-  eq MReceiveDefinition.uniqueSuffix() = "Receive";
+  eq MTokenSendDefinition.uniqueSuffix() = "Send";
+  eq MTokenReceiveDefinition.uniqueSuffix() = "Receive";
+  eq MTypeSendDefinition.uniqueSuffix() = "Send";
+  eq MTypeReceiveDefinition.uniqueSuffix() = "Receive";
 
-  syn TokenComponent MEndpointDefinition.token() = endpointDef().getToken();
+  // TODO potentially dangerous because asXEndpointDefinition can return null
+  syn TokenComponent MEndpointDefinition.token() = endpointDef().asTokenEndpointDefinition().getToken();
+  syn TypeComponent MEndpointDefinition.type() = endpointDef().asTypeEndpointDefinition().getType();
   syn boolean MEndpointDefinition.alwaysApply() = endpointDef().getAlwaysApply();
-  syn String MEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName();
   syn String MEndpointDefinition.tokenName() = token().getName();
+  syn String MEndpointDefinition.typeName() = type().getName();
   syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
   syn String MEndpointDefinition.lastDefinitionToType() = lastDefinition().toType();
   syn String MEndpointDefinition.lastResult() = lastDefinition().outputVarName();
@@ -51,12 +64,20 @@ aspect AttributesForMustache {
     if (token().isPrimitiveType() && lastDefinition().mappingDef().getToType().isPrimitiveType()) {
       return preemptiveExpectedValue() + " == " + lastResult();
     }
-    if (lastDefinition().mappingDef().isDefaultMappingDefinition()) {
+    if (lastDefinition().mappingDef().getToType().isPrimitiveType() || lastDefinition().mappingDef().isDefaultMappingDefinition()) {
       return preemptiveExpectedValue() + " != null && " + preemptiveExpectedValue() + ".equals(" + lastResult() + ")";
     }
     return preemptiveExpectedValue() + " != null ? " + preemptiveExpectedValue() + ".equals(" + lastResult() + ") : " + lastResult() + " == null";
   }
 
+  // --- MTokenEndpointDefinition ---
+  eq MTokenEndpointDefinition.parentTypeName() = token().containingTypeDecl().getName();
+  eq MTokenEndpointDefinition.entityName() = tokenName();
+
+  // --- MTypeEndpointDefinition ---
+  eq MTypeEndpointDefinition.parentTypeName() = type().containingTypeDecl().getName();
+  eq MTypeEndpointDefinition.entityName() = typeName();
+
   // --- MInnerMappingDefinition ---
   inh boolean MInnerMappingDefinition.isLast();
   inh String MInnerMappingDefinition.inputVarName();
@@ -65,24 +86,43 @@ aspect AttributesForMustache {
   syn MappingDefinition MInnerMappingDefinition.mappingDef() = getMMappingDefinition().getMappingDefinition();
   syn String MInnerMappingDefinition.outputVarName() = "result" + methodName();  // we do not need "_" in between here, because methodName begins with one
 
-  // --- MReceiveDefinition ---
-  eq MReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()";
-  eq MReceiveDefinition.preemptiveReturn() = "return;";
-  eq MReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
-  eq MReceiveDefinition.firstInputVarName() = "message";
-
-  // --- MSendDefinition ---
-  eq MSendDefinition.preemptiveExpectedValue() = lastValue();
-  eq MSendDefinition.preemptiveReturn() = "return false;";
-  eq MSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
-  eq MSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
-
-  syn String MSendDefinition.sender() = "_sender_" + tokenName();
-  syn String MSendDefinition.lastValue() = "_lastValue" + tokenName();
-  syn String MSendDefinition.updateMethod() = "_update_" + tokenName();
-  syn String MSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
-  syn String MSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset";
-  syn boolean MSendDefinition.shouldSendValue() = endpointDef().shouldSendValue();
+  // --- MTokenReceiveDefinition ---
+  eq MTokenReceiveDefinition.preemptiveExpectedValue() = "get" + tokenName() + "()";
+  eq MTokenReceiveDefinition.preemptiveReturn() = "return;";
+  eq MTokenReceiveDefinition.endpointDef() = getReceiveTokenEndpointDefinition();
+  eq MTokenReceiveDefinition.firstInputVarName() = "message";
+
+  // --- MTokenSendDefinition ---
+  eq MTokenSendDefinition.preemptiveExpectedValue() = lastValue();
+  eq MTokenSendDefinition.preemptiveReturn() = "return false;";
+  eq MTokenSendDefinition.endpointDef() = getSendTokenEndpointDefinition();
+  eq MTokenSendDefinition.firstInputVarName() = "get" + tokenName() + "()";
+
+  syn String MTokenSendDefinition.sender() = "_sender_" + tokenName();
+  syn String MTokenSendDefinition.lastValue() = "_lastValue" + tokenName();
+  syn String MTokenSendDefinition.updateMethod() = "_update_" + tokenName();
+  syn String MTokenSendDefinition.writeMethod() = "_writeLastValue_" + tokenName();
+  syn String MTokenSendDefinition.tokenResetMethod() = "get" + tokenName() + "_reset";
+  syn boolean MTokenSendDefinition.shouldSendValue() = endpointDef().asTokenEndpointDefinition().shouldSendValue();
+
+  // MTypeReceiveDefinition
+  eq MTypeReceiveDefinition.preemptiveExpectedValue() = null;
+  eq MTypeReceiveDefinition.preemptiveReturn() = null;
+  eq MTypeReceiveDefinition.endpointDef() = getReceiveTypeEndpointDefinition();
+  eq MTypeReceiveDefinition.firstInputVarName() = null;
+
+  // MTypeSendDefinition
+  eq MTypeSendDefinition.preemptiveExpectedValue() = null;
+  eq MTypeSendDefinition.preemptiveReturn() = null;
+  eq MTypeSendDefinition.endpointDef() = getSendTypeEndpointDefinition();
+  eq MTypeSendDefinition.firstInputVarName() = null;
+
+  syn String MTypeSendDefinition.sender() = "_sender_" + typeName();
+  syn String MTypeSendDefinition.lastValue() = "_lastValue" + typeName();
+  syn String MTypeSendDefinition.updateMethod() = "_update_" + typeName();
+  syn String MTypeSendDefinition.writeMethod() = "_writeLastValue_" + typeName();
+  syn String MTypeSendDefinition.tokenResetMethod() = "get" + typeName() + "_reset";
+  syn boolean MTypeSendDefinition.shouldSendValue() = endpointDef().asTypeEndpointDefinition().shouldSendValue();
 
   // --- MMappingDefinition ---
   syn String MMappingDefinition.toType() = getMappingDefinition().getToType().prettyPrint();
@@ -96,7 +136,7 @@ aspect AttributesForMustache {
   syn String MDependencyDefinition.dependencyMethod() = "add" + capitalize(getDependencyDefinition().getID());
   syn String MDependencyDefinition.sourceParentTypeName() = getDependencyDefinition().getSource().containingTypeDecl().getName();
   syn String MDependencyDefinition.internalRelationPrefix() = "_internal_" + getDependencyDefinition().getID();
-  syn nta MSendDefinition MDependencyDefinition.targetEndpointDefinition() {
+  syn nta MTokenSendDefinition MDependencyDefinition.targetEndpointDefinition() {
     return getDependencyDefinition().targetEndpointDefinition().toMustache();
   }
 
@@ -110,7 +150,7 @@ aspect AttributesForMustache {
   syn String MTokenComponent.javaType() = getTokenComponent().effectiveJavaTypeUse().prettyPrint();
   syn String MTokenComponent.internalName() = getTokenComponent().needProxyToken() ? "_internal_" + name() : externalName();
   syn String MTokenComponent.externalName() = name();
-  syn MSendDefinition MTokenComponent.normalTokenSendDef() {
+  syn MTokenSendDefinition MTokenComponent.normalTokenSendDef() {
     for (TokenEndpointDefinition endpointDef : getTokenComponent().getTokenEndpointDefinitionList()) {
       if (endpointDef.shouldSendValue()) {
         return endpointDef.asSendTokenEndpointDefinition().toMustache();
@@ -124,11 +164,16 @@ aspect AttributesForMustache {
     MRagConnect result = new MRagConnect();
     result.setRagConnect(this);
     for (EndpointDefinition def : getEndpointDefinitionList()) {
-      if (def.isSendTokenEndpointDefinition()) {
-        SendTokenEndpointDefinition sendDef = def.asSendTokenEndpointDefinition();
-        result.addSendDefinition(sendDef.toMustache());
+      if (def.isReceiveTokenEndpointDefinition()) {
+        result.addTokenReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
+      } else if (def.isSendTokenEndpointDefinition()) {
+        result.addTokenSendDefinition(def.asSendTokenEndpointDefinition().toMustache());
+      } else if (def.isReceiveTypeEndpointDefinition()) {
+        result.addTypeReceiveDefinition(def.asReceiveTypeEndpointDefinition().toMustache());
+      } else if (def.isSendTypeEndpointDefinition()) {
+        result.addTypeSendDefinition(def.asSendTypeEndpointDefinition().toMustache());
       } else {
-        result.addReceiveDefinition(def.asReceiveTokenEndpointDefinition().toMustache());
+        throw new RuntimeException("Unknown endpoint definition: " + def);
       }
     }
     for (MappingDefinition def : allMappingDefinitions()) {
@@ -163,20 +208,34 @@ aspect AttributesForMustache {
     }
   }
 
-  syn lazy MReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
-    MReceiveDefinition result = new MReceiveDefinition();
+  syn lazy MTokenReceiveDefinition ReceiveTokenEndpointDefinition.toMustache() {
+    MTokenReceiveDefinition result = new MTokenReceiveDefinition();
     result.setReceiveTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
 
-  syn lazy MSendDefinition SendTokenEndpointDefinition.toMustache() {
-    MSendDefinition result = new MSendDefinition();
+  syn lazy MTokenSendDefinition SendTokenEndpointDefinition.toMustache() {
+    MTokenSendDefinition result = new MTokenSendDefinition();
     result.setSendTokenEndpointDefinition(this);
     result.addInnerMappings();
     return result;
   }
 
+  syn lazy MTypeReceiveDefinition ReceiveTypeEndpointDefinition.toMustache() {
+    MTypeReceiveDefinition result = new MTypeReceiveDefinition();
+    result.setReceiveTypeEndpointDefinition(this);
+    result.addInnerMappings();
+    return result;
+  }
+
+  syn lazy MTypeSendDefinition SendTypeEndpointDefinition.toMustache() {
+    MTypeSendDefinition result = new MTypeSendDefinition();
+    result.setSendTypeEndpointDefinition(this);
+    result.addInnerMappings();
+    return result;
+  }
+
   syn lazy MMappingDefinition MappingDefinition.toMustache() {
     MMappingDefinition result = new MMappingDefinition();
     result.setMappingDefinition(this);
diff --git a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
index 7aa82d304079592d605997484d1348a3d3681875..1ef7d167efd07d06d9dbea7ca8d37ba3a30e1186 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
+++ b/ragconnect.base/src/main/jastadd/intermediate/Mappings.jrag
@@ -79,44 +79,32 @@ aspect DefaultMappings {
 
 aspect Mappings {
   // --- effectiveMappings ---
-  syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings();
-  eq ReceiveTokenEndpointDefinition.effectiveMappings() {
-    // if there is a first mapping, check if it is suitable.
-    //  or if no mappings are specified.
-    // then prepend the suitable default mapping
+  syn java.util.List<MappingDefinition> EndpointDefinition.effectiveMappings() {
     java.util.List<MappingDefinition> result;
-    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
-      result = new java.util.ArrayList();
-      result.add(suitableDefaultMapping());
-      result.addAll(getMappingList());
+    if (isReceiveTokenEndpointDefinition() || isReceiveTypeEndpointDefinition()) {
+      // if no mappings are specified, or if first mapping is not suitable.
+      // then prepend the suitable default mapping
+      if (getMappingList().isEmpty() || !getMappingList().get(0).getFromType().isByteArray()) {
+        result = new java.util.ArrayList();
+        result.add(suitableReceiveDefaultMapping());
+        result.addAll(getMappingList());
+      } else {
+        result = getMappingList();
+      }
+    } else if (isSendTokenEndpointDefinition() || isSendTypeEndpointDefinition()) {
+      // if no mappings are specified, or if last mapping is not suitable
+      // then append the suitable default mapping
+      if (getMappingList().isEmpty() || !getMappingList().get(getMappingList().size() - 1).getToType().isByteArray()) {
+        result = new java.util.ArrayList(getMappingList());
+        result.add(suitableSendDefaultMapping());
+      } else {
+        result = getMappingList();
+      }
     } else {
-      result = getMappingList();
+      throw new RuntimeException("Unknown endpoint definition: " + this);
     }
     return result;
   }
-  eq SendTokenEndpointDefinition.effectiveMappings() {
-    // if there is a mapping, check if it is suitable.
-    //  or if no mappings are specified.
-    // then append the suitable default mapping
-    java.util.List<MappingDefinition> result;
-    if (getMappingList().isEmpty() || !hasSuitableEdgeMapping()) {
-      result = new java.util.ArrayList(getMappingList());
-      result.add(suitableDefaultMapping());
-    } else {
-      result = getMappingList();
-    }
-    return result;
-  }
-
-  // --- hasSuitableEdgeMapping ---
-  syn boolean TokenEndpointDefinition.hasSuitableEdgeMapping();
-  eq ReceiveTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(0));
-  eq SendTokenEndpointDefinition.hasSuitableEdgeMapping() = isSuitableEdgeMapping(getMappingList().get(getMappingList().size() - 1));
-
-  // --- isSuitableEdgeMapping(def) ---
-  syn boolean TokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def);
-  eq ReceiveTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getFromType().isByteArray();
-  eq SendTokenEndpointDefinition.isSuitableEdgeMapping(MappingDefinition def) = def.getToType().isByteArray();
 
   // --- isPrimitiveType ---
   syn boolean TokenComponent.isPrimitiveType() = effectiveJavaTypeUse().isPrimitiveType();
@@ -140,13 +128,9 @@ aspect Mappings {
   syn boolean MappingDefinitionType.isArray() = false;
   eq JavaArrayMappingDefinitionType.isArray() = true;
 
-  // --- suitableDefaultMapping ---
-  syn DefaultMappingDefinition EndpointDefinition.suitableDefaultMapping();
-  eq ReceiveTokenEndpointDefinition.suitableDefaultMapping() {
-    String typeName = getMappingList().isEmpty() ?
-        getToken().effectiveJavaTypeUse().getName() :
-        getMappingList().get(0).getFromType().prettyPrint();
-    switch(typeName) {
+  // --- suitableReceiveDefaultMapping ---
+  syn DefaultMappingDefinition EndpointDefinition.suitableReceiveDefaultMapping() {
+    switch (targetTypeName()) {
       case "int":
       case "Integer": return ragconnect().defaultBytesToIntMapping();
       case "short":
@@ -163,11 +147,9 @@ aspect Mappings {
       default: return null;
     }
   }
-  eq SendTokenEndpointDefinition.suitableDefaultMapping() {
-    String typeName = getMappingList().isEmpty() ?
-        getToken().effectiveJavaTypeUse().getName() :
-        getMappingList().get(getMappingList().size() - 1).getFromType().prettyPrint();
-    switch(typeName) {
+  // --- suitableSendDefaultMapping ---
+  syn DefaultMappingDefinition EndpointDefinition.suitableSendDefaultMapping() {
+    switch (targetTypeName()) {
       case "int":
       case "Integer": return ragconnect().defaultIntToBytesMapping();
       case "short":
@@ -184,6 +166,26 @@ aspect Mappings {
       default: return null;
     }
   }
+
+  // --- targetTypeName ---
+  syn String EndpointDefinition.targetTypeName();
+  eq ReceiveTokenEndpointDefinition.targetTypeName() {
+    return getMappingList().isEmpty() ?
+           getToken().effectiveJavaTypeUse().getName() :
+           getMappingList().get(0).getFromType().prettyPrint();
+  }
+  eq ReceiveTypeEndpointDefinition.targetTypeName() {
+    return getMappingList().get(0).getFromType().prettyPrint();
+  }
+  eq SendTokenEndpointDefinition.targetTypeName() {
+    return getMappingList().isEmpty() ?
+           getToken().effectiveJavaTypeUse().getName() :
+           getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
+  }
+  eq SendTypeEndpointDefinition.targetTypeName() {
+    return getMappingList().get(getMappingList().size() - 1).getToType().prettyPrint();
+  }
+
 //  eq ReceiveFromRestDefinition.suitableDefaultMapping() {
 //    String typeName = getMappingList().isEmpty() ?
 //        getToken().getJavaTypeUse().getName() :
diff --git a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast b/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast
index 06843177098ba889b665257c9ba2f965f5ecda88..8255cfe5ad9e67e00e53e95b4ddc110a480dce28 100644
--- a/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast
+++ b/ragconnect.base/src/main/jastadd/intermediate/MustacheNodes.relast
@@ -1,7 +1,13 @@
-MRagConnect ::= ReceiveDefinition:MReceiveDefinition* SendDefinition:MSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent* Handler:MHandler*;
+MRagConnect ::= TokenReceiveDefinition:MTokenReceiveDefinition* TokenSendDefinition:MTokenSendDefinition* TypeReceiveDefinition:MTypeReceiveDefinition* TypeSendDefinition:MTypeSendDefinition* MappingDefinition:MMappingDefinition* DependencyDefinition:MDependencyDefinition* RootTypeComponent:MTypeComponent* TokenComponent:MTokenComponent* Handler:MHandler*;
+
 abstract MEndpointDefinition ::= InnerMappingDefinition:MInnerMappingDefinition*;
-MReceiveDefinition : MEndpointDefinition;
-MSendDefinition : MEndpointDefinition;
+abstract MTokenEndpointDefinition : MEndpointDefinition;
+MTokenReceiveDefinition : MTokenEndpointDefinition;
+MTokenSendDefinition : MTokenEndpointDefinition;
+abstract MTypeEndpointDefinition : MEndpointDefinition;
+MTypeReceiveDefinition : MTypeEndpointDefinition;
+MTypeSendDefinition : MTypeEndpointDefinition;
+
 MMappingDefinition;
 MInnerMappingDefinition;
 MDependencyDefinition;
@@ -11,8 +17,10 @@ MHandler ::= <ClassName> <Construction> <AttributeName> <FieldName> <InUse:boole
 
 rel MRagConnect.RagConnect -> RagConnect;
 rel MInnerMappingDefinition.MMappingDefinition -> MMappingDefinition;
-rel MReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition;
-rel MSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition;
+rel MTokenReceiveDefinition.ReceiveTokenEndpointDefinition -> ReceiveTokenEndpointDefinition;
+rel MTokenSendDefinition.SendTokenEndpointDefinition -> SendTokenEndpointDefinition;
+rel MTypeReceiveDefinition.ReceiveTypeEndpointDefinition -> ReceiveTypeEndpointDefinition;
+rel MTypeSendDefinition.SendTypeEndpointDefinition -> SendTypeEndpointDefinition;
 rel MMappingDefinition.MappingDefinition -> MappingDefinition;
 rel MDependencyDefinition.DependencyDefinition -> DependencyDefinition;
 rel MTypeComponent.TypeComponent -> TypeComponent;
diff --git a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag b/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag
index 337527db4b44261a30c6768450ded30ad99446db..9de1c1e6ecf3e2f05580b3c324316708e353ad40 100644
--- a/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag
+++ b/ragconnect.base/src/main/jastadd/intermediate2mustache/MustacheNodesToYAML.jrag
@@ -25,19 +25,33 @@ aspect MustacheNodesToYAML {
     root.put("restHandlerField", restHandlerField());
     root.put("restHandlerAttribute", restHandlerAttribute());
 
-    // ReceiveDefinitions
+    // TokenReceiveDefinitions
     ListElement receiveDefinitions = new ListElement();
-    for (MReceiveDefinition def : getReceiveDefinitionList()) {
+    for (MTokenReceiveDefinition def : getTokenReceiveDefinitionList()) {
       receiveDefinitions.addElement(def.toYAML());
     }
-    root.put("ReceiveDefinitions", receiveDefinitions);
+    root.put("TokenReceiveDefinitions", receiveDefinitions);
 
-    // SendDefinitions
+    // TokenSendDefinitions
     ListElement sendDefinitions = new ListElement();
-    for (MSendDefinition def : getSendDefinitionList()) {
+    for (MTokenSendDefinition def : getTokenSendDefinitionList()) {
       sendDefinitions.addElement(def.toYAML());
     }
-    root.put("SendDefinitions", sendDefinitions);
+    root.put("TokenSendDefinitions", sendDefinitions);
+
+    // TypeReceiveDefinitions
+    ListElement typeReceiveDefinitions = new ListElement();
+    for (MTypeReceiveDefinition def : getTypeReceiveDefinitionList()) {
+      typeReceiveDefinitions.addElement(def.toYAML());
+    }
+    root.put("TypeReceiveDefinitions", typeReceiveDefinitions);
+
+    // TypeSendDefinitions
+    ListElement typeSendDefinitions = new ListElement();
+    for (MTypeSendDefinition def : getTypeSendDefinitionList()) {
+      typeSendDefinitions.addElement(def.toYAML());
+    }
+    root.put("TypeSendDefinitions", typeSendDefinitions);
 
     // MappingDefinitions
     ListElement mappingDefinitions = new ListElement();
@@ -88,13 +102,30 @@ aspect MustacheNodesToYAML {
     return result;
   }
 
-  syn MappingElement MReceiveDefinition.toYAML() {
+  syn MappingElement MTokenReceiveDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.put("loggingEnabledForReads", loggingEnabledForReads);
+    return result;
+  }
+
+  syn MappingElement MTokenSendDefinition.toYAML() {
+    MappingElement result = super.toYAML();
+    result.put("sender", sender());
+    result.put("lastValue", lastValue());
+    result.put("loggingEnabledForWrites", loggingEnabledForWrites);
+    result.put("updateMethod", updateMethod());
+    result.put("writeMethod", writeMethod());
+    result.put("tokenResetMethod", tokenResetMethod());
+    return result;
+  }
+
+  syn MappingElement MTypeReceiveDefinition.toYAML() {
     MappingElement result = super.toYAML();
     result.put("loggingEnabledForReads", loggingEnabledForReads);
     return result;
   }
 
-  syn MappingElement MSendDefinition.toYAML() {
+  syn MappingElement MTypeSendDefinition.toYAML() {
     MappingElement result = super.toYAML();
     result.put("sender", sender());
     result.put("lastValue", lastValue());
diff --git a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
index 0432b0df96c44060f6df1d6918a856f62810230d..4094bfa23d62f87540939f2619274ade313f6db4 100644
--- a/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
+++ b/ragconnect.base/src/main/jastadd/parser/RagConnect.parser
@@ -33,12 +33,18 @@ EndpointDefinition endpoint_definition
 EndpointDefinition endpoint_definition_type
   = RECEIVE token_ref           {: return new ReceiveTokenEndpointDefinition().setToken(token_ref); :}
   | SEND token_ref              {: return new SendTokenEndpointDefinition().setToken(token_ref); :}
+  | RECEIVE TREE type_ref       {: return new ReceiveTypeEndpointDefinition().setType(type_ref); :}
+  | SEND TREE type_ref          {: return new SendTypeEndpointDefinition().setType(type_ref); :}
 ;
 
 TokenComponent token_ref
   = ID.type_name DOT ID.token_name  {: return TokenComponent.createRef(type_name + "." + token_name); :}
 ;
 
+TypeComponent type_ref
+  = ID.parent_type_name DOT ID.child_type_name  {: return TypeComponent.createRef(parent_type_name + "." + child_type_name); :}
+;
+
 ArrayList string_list
   = ID
   | string_list COMMA ID
diff --git a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
index 5773118d8fda8ffdc536b504aa7056b8784ddfb3..40d751b37ffab7ffe63355f50e226f794de02c3b 100644
--- a/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
+++ b/ragconnect.base/src/main/jastadd/scanner/Keywords.flex
@@ -5,3 +5,4 @@
 "maps"       { return sym(Terminals.MAPS); }
 "to"         { return sym(Terminals.TO); }
 "as"         { return sym(Terminals.AS); }
+"tree"       { return sym(Terminals.TREE); }
diff --git a/ragconnect.base/src/main/resources/ragconnect.mustache b/ragconnect.base/src/main/resources/ragconnect.mustache
index 45e5e11c33c8988286c4b209f9d04ee956f5da29..41cde06f025abc9663ce1acae3ee7549f5987956 100644
--- a/ragconnect.base/src/main/resources/ragconnect.mustache
+++ b/ragconnect.base/src/main/resources/ragconnect.mustache
@@ -1,13 +1,21 @@
 {{#usesMqtt}}{{> mqtt}}{{/usesMqtt}}
 {{> handler}}
 aspect RagConnect {
-  {{#ReceiveDefinitions}}
+  {{#TokenReceiveDefinitions}}
   {{> receiveDefinition}}
-  {{/ReceiveDefinitions}}
+  {{/TokenReceiveDefinitions}}
 
-  {{#SendDefinitions}}
+  {{#TokenSendDefinitions}}
   {{> sendDefinition}}
-  {{/SendDefinitions}}
+  {{/TokenSendDefinitions}}
+
+  {{#TypeReceiveDefinitions}}
+  {{> receiveDefinition}}
+  {{/TypeReceiveDefinitions}}
+
+  {{#TypeSendDefinitions}}
+  {{> sendDefinition}}
+  {{/TypeSendDefinitions}}
 
   {{#MappingDefinitions}}
   {{> mappingDefinition}}
@@ -20,6 +28,18 @@ aspect RagConnect {
   {{#TokenComponents}}
   {{> tokenComponent}}
   {{/TokenComponents}}
+  public void {{rootNodeName}}.ragconnectCheckIncremental() {
+  {{#incrementalOptionActive}}
+    // check if --tracing is active
+    trace().getReceiver();
+    // check if tracing of INC_FLUSH_ATTR is possible, i.e., if --tracing=flush
+    ASTState.Trace.Event checkTracing = ASTState.Trace.Event.INC_FLUSH_ATTR;
+    // check if --rewrite is active
+    mayHaveRewrite();
+    // check if --incremental is active
+    Object checkIncremental = inc_throwAway_visited;
+  {{/incrementalOptionActive}}
+  }
 }
 
 {{#incrementalOptionActive}}
diff --git a/ragconnect.tests/build.gradle b/ragconnect.tests/build.gradle
index d2e772edfb66165bc18e1bf33614d2e5468ecb59..1ff5652f946d8a8c7f8b88d03b3e10fa171e00e7 100644
--- a/ragconnect.tests/build.gradle
+++ b/ragconnect.tests/build.gradle
@@ -76,7 +76,15 @@ task allTests(type: Test, dependsOn: testClasses) {
 
     useJUnitPlatform {
         includeTags 'mqtt'
-//        excludeTags '!NewTest'
+    }
+}
+
+task specificTest(type: Test, dependsOn: testClasses) {
+    description = 'Run test tagged with "NewTest"'
+    group = 'verification'
+
+    useJUnitPlatform {
+        includeTags 'NewTest'
     }
 }
 
@@ -362,8 +370,7 @@ task preprocessIncrementalTest(type: JavaExec, group: 'verification') {
             'src/test/01-input/incremental/Test.connect',
             '--rootNode=A',
             '--tracing=cache,flush',
-            '--incremental=param',
-            '--logReads', '--logWrites', '--verbose'
+            '--incremental=param'
 }
 
 task compileIncrementalTest(type: RelastTest) {
@@ -377,11 +384,43 @@ task compileIncrementalTest(type: RelastTest) {
             'src/test/02-after-ragconnect/incremental/MqttHandler.jadd',
             'src/test/02-after-ragconnect/incremental/RagConnect.jadd'
     extraJastAddOptions '--tracing=cache,flush',
-                        '--incremental=param',
-                        '--cache=all',
-                        '--rewrite=cnta',
-                        '--flush=full'
+            '--incremental=param',
+            '--cache=all',
+            '--rewrite=cnta',
+            '--flush=full'
 }
 
 compileTestJava.dependsOn compileIncrementalTest
 compileIncrementalTest.dependsOn preprocessIncrementalTest
+
+// --- Test: mapping ---
+task preprocessMappingTest(type: JavaExec, group: 'verification') {
+    doFirst {
+        delete 'src/test/02-after-ragconnect/mapping/Test.relast',
+                'src/test/02-after-ragconnect/mapping/MqttHandler.jadd',
+                'src/test/02-after-ragconnect/mapping/RagConnect.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.ragconnect.compiler.Compiler'
+    args '--o=src/test/02-after-ragconnect/mapping',
+            'src/test/01-input/mapping/Test.relast',
+            'src/test/01-input/mapping/Test.connect',
+            '--rootNode=A',
+            '--logReads', '--logWrites', '--verbose'
+}
+
+task compileMappingTest(type: RelastTest) {
+    useJastAddNames = true
+    jastAddList = 'JastAddList'
+    relastFiles 'src/test/02-after-ragconnect/mapping/Test.relast',
+            'src/test/02-after-ragconnect/mapping/RagConnect.relast'
+    grammarName = 'src/test/03-after-relast/mapping/mapping'
+    packageName = 'mapping.ast'
+    moreInputFiles 'src/test/01-input/mapping/Test.jadd',
+            'src/test/02-after-ragconnect/mapping/MqttHandler.jadd',
+            'src/test/02-after-ragconnect/mapping/RagConnect.jadd'
+}
+
+compileTestJava.dependsOn compileMappingTest
+compileMappingTest.dependsOn preprocessMappingTest
diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect
index e274994175513ff743409068375f419b6e3141fc..475d91614073a4a3e8211a5e9e7362c6c832f0e0 100644
--- a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect
+++ b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.connect
@@ -7,9 +7,46 @@ receive NativeTypes.DoubleValue;
 receive NativeTypes.CharValue;
 receive NativeTypes.StringValue;
 
+receive NativeTypes.IntValueTransformed using IntTransformation;
+receive NativeTypes.ShortValueTransformed using ShortTransformation;
+receive NativeTypes.LongValueTransformed using LongTransformation;
+receive NativeTypes.FloatValueTransformed using FloatTransformation;
+receive NativeTypes.DoubleValueTransformed using DoubleTransformation;
+receive NativeTypes.CharValueTransformed using CharTransformation;
+receive NativeTypes.StringValueTransformed using StringTransformation;
+
 receive BoxedTypes.IntValue;
 receive BoxedTypes.ShortValue;
 receive BoxedTypes.LongValue;
 receive BoxedTypes.FloatValue;
 receive BoxedTypes.DoubleValue;
 receive BoxedTypes.CharValue;
+
+receive BoxedTypes.IntValueTransformed using IntTransformation;
+receive BoxedTypes.ShortValueTransformed using ShortTransformation;
+receive BoxedTypes.LongValueTransformed using LongTransformation;
+receive BoxedTypes.FloatValueTransformed using FloatTransformation;
+receive BoxedTypes.DoubleValueTransformed using DoubleTransformation;
+receive BoxedTypes.CharValueTransformed using CharTransformation;
+
+IntTransformation maps int i to int {:
+  return i;
+:}
+ShortTransformation maps short s to short {:
+  return s;
+:}
+LongTransformation maps long l to long {:
+  return l;
+:}
+FloatTransformation maps float f to float {:
+  return f;
+:}
+DoubleTransformation maps double d to double {:
+  return d;
+:}
+CharTransformation maps char c to char {:
+  return c;
+:}
+StringTransformation maps String s to String {:
+  return s;
+:}
diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast
index 30eba76b346dace40c72ca4e172b88ef206ea063..750dc0795e39ae42eb10d9ed733ea9a01fc05c26 100644
--- a/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast
+++ b/ragconnect.tests/src/test/01-input/defaultOnlyRead/Test.relast
@@ -1,3 +1,3 @@
 A ::= NativeTypes* BoxedTypes* ;
-NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> ;
-BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> ;
+NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <StringValue:String> <IntValueTransformed:int> <ShortValueTransformed:short> <LongValueTransformed:long> <FloatValueTransformed:float> <DoubleValueTransformed:double> <CharValueTransformed:char> <StringValueTransformed:String> ;
+BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> <IntValueTransformed:Integer> <ShortValueTransformed:Short> <LongValueTransformed:Long> <FloatValueTransformed:Float> <DoubleValueTransformed:Double> <CharValueTransformed:Character> ;
diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect
index a48f49f60f66c3358ee513e79f2bbeb43df8d5e9..076809cf16de0b52b809856e0d17ac0010c8f21f 100644
--- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect
+++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.connect
@@ -8,6 +8,15 @@ send NativeTypesSyn.DoubleValue;
 send NativeTypesSyn.CharValue;
 send NativeTypesSyn.StringValue;
 
+// native types, synthesized, transformed
+send NativeTypesSyn.IntValueTransformed using IntTransformation;
+send NativeTypesSyn.ShortValueTransformed using ShortTransformation;
+send NativeTypesSyn.LongValueTransformed using LongTransformation;
+send NativeTypesSyn.FloatValueTransformed using FloatTransformation;
+send NativeTypesSyn.DoubleValueTransformed using DoubleTransformation;
+send NativeTypesSyn.CharValueTransformed using CharTransformation;
+send NativeTypesSyn.StringValueTransformed using StringTransformation;
+
 // boxed types, synthesized
 send BoxedTypesSyn.IntValue;
 send BoxedTypesSyn.ShortValue;
@@ -16,6 +25,14 @@ send BoxedTypesSyn.FloatValue;
 send BoxedTypesSyn.DoubleValue;
 send BoxedTypesSyn.CharValue;
 
+// boxed types, synthesized, transformed
+send BoxedTypesSyn.IntValueTransformed using IntTransformation;
+send BoxedTypesSyn.ShortValueTransformed using ShortTransformation;
+send BoxedTypesSyn.LongValueTransformed using LongTransformation;
+send BoxedTypesSyn.FloatValueTransformed using FloatTransformation;
+send BoxedTypesSyn.DoubleValueTransformed using DoubleTransformation;
+send BoxedTypesSyn.CharValueTransformed using CharTransformation;
+
 // --- dependency definitions ---
 NativeTypesSyn.IntValue canDependOn NativeTypesSyn.DriverSyn as nativeIntDependency;
 NativeTypesSyn.ShortValue canDependOn NativeTypesSyn.DriverSyn as nativeShortDependency;
@@ -31,6 +48,21 @@ BoxedTypesSyn.FloatValue canDependOn BoxedTypesSyn.DriverSyn as boxedFloatDepend
 BoxedTypesSyn.DoubleValue canDependOn BoxedTypesSyn.DriverSyn as boxedDoubleDependency;
 BoxedTypesSyn.CharValue canDependOn BoxedTypesSyn.DriverSyn as boxedCharDependency;
 
+NativeTypesSyn.IntValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeIntTransformedDependency;
+NativeTypesSyn.ShortValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeShortTransformedDependency;
+NativeTypesSyn.LongValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeLongTransformedDependency;
+NativeTypesSyn.FloatValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeFloatTransformedDependency;
+NativeTypesSyn.DoubleValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeDoubleTransformedDependency;
+NativeTypesSyn.CharValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeCharTransformedDependency;
+NativeTypesSyn.StringValueTransformed canDependOn NativeTypesSyn.DriverSyn as nativeStringTransformedDependency;
+
+BoxedTypesSyn.IntValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedIntTransformedDependency;
+BoxedTypesSyn.ShortValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedShortTransformedDependency;
+BoxedTypesSyn.LongValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedLongTransformedDependency;
+BoxedTypesSyn.FloatValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedFloatTransformedDependency;
+BoxedTypesSyn.DoubleValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedDoubleTransformedDependency;
+BoxedTypesSyn.CharValueTransformed canDependOn BoxedTypesSyn.DriverSyn as boxedCharTransformedDependency;
+
 
 // --- inherited attributes not supported ---
 //// native types, inherited
@@ -49,3 +81,25 @@ BoxedTypesSyn.CharValue canDependOn BoxedTypesSyn.DriverSyn as boxedCharDependen
 //send BoxedTypesInh.FloatValue;
 //send BoxedTypesInh.DoubleValue;
 //send BoxedTypesInh.CharValue;
+
+IntTransformation maps int i to int {:
+  return i;
+:}
+ShortTransformation maps short s to short {:
+  return s;
+:}
+LongTransformation maps long l to long {:
+  return l;
+:}
+FloatTransformation maps float f to float {:
+  return f;
+:}
+DoubleTransformation maps double d to double {:
+  return d;
+:}
+CharTransformation maps char c to char {:
+  return c;
+:}
+StringTransformation maps String s to String {:
+  return s;
+:}
diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd
index 49bbf604d398f7a9d92e879b2b515c9e83dd3182..1692609251ba21e58d41049a58eadd1efdf4753c 100644
--- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd
+++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.jadd
@@ -7,6 +7,13 @@ aspect Computation {
   syn double NativeTypesSyn.getDoubleValue() = Double.parseDouble(getDriverSyn());
   syn char NativeTypesSyn.getCharValue() = getDriverSyn().charAt(0);
   syn String NativeTypesSyn.getStringValue() = new String(getDriverSyn());
+  syn int NativeTypesSyn.getIntValueTransformed() = Integer.parseInt(getDriverSyn());
+  syn short NativeTypesSyn.getShortValueTransformed() = Short.parseShort(getDriverSyn());
+  syn long NativeTypesSyn.getLongValueTransformed() = Long.parseLong(getDriverSyn());
+  syn float NativeTypesSyn.getFloatValueTransformed() = Float.parseFloat(getDriverSyn());
+  syn double NativeTypesSyn.getDoubleValueTransformed() = Double.parseDouble(getDriverSyn());
+  syn char NativeTypesSyn.getCharValueTransformed() = getDriverSyn().charAt(0);
+  syn String NativeTypesSyn.getStringValueTransformed() = new String(getDriverSyn());
 
   // boxed types, synthesized
   syn Integer BoxedTypesSyn.getIntValue() = Integer.valueOf(getDriverSyn());
@@ -15,6 +22,12 @@ aspect Computation {
   syn Float BoxedTypesSyn.getFloatValue() = Float.valueOf(getDriverSyn());
   syn Double BoxedTypesSyn.getDoubleValue() = Double.valueOf(getDriverSyn());
   syn Character BoxedTypesSyn.getCharValue() = getDriverSyn().charAt(0);
+  syn Integer BoxedTypesSyn.getIntValueTransformed() = Integer.valueOf(getDriverSyn());
+  syn Short BoxedTypesSyn.getShortValueTransformed() = Short.valueOf(getDriverSyn());
+  syn Long BoxedTypesSyn.getLongValueTransformed() = Long.valueOf(getDriverSyn());
+  syn Float BoxedTypesSyn.getFloatValueTransformed() = Float.valueOf(getDriverSyn());
+  syn Double BoxedTypesSyn.getDoubleValueTransformed() = Double.valueOf(getDriverSyn());
+  syn Character BoxedTypesSyn.getCharValueTransformed() = getDriverSyn().charAt(0);
 
 // --- inherited attributes not supported ---
 //  // native types, inherited
diff --git a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast
index e0fd7829b88b8fac04b64393e0262c69b4ac6a62..56452539efeeec9cdd67f5e17a61bb0e41061a57 100644
--- a/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast
+++ b/ragconnect.tests/src/test/01-input/defaultOnlyWrite/Test.relast
@@ -1,8 +1,8 @@
 A ::= NativeTypesSyn* BoxedTypesSyn* <DriverInh:String>;
 // native types, synthesized
-NativeTypesSyn ::= <DriverSyn:String> /<IntValue:int>/ /<ShortValue:short>/ /<LongValue:long>/ /<FloatValue:float>/ /<DoubleValue:double>/ /<CharValue:char>/ /<StringValue:String>/ ;
+NativeTypesSyn ::= <DriverSyn:String> /<IntValue:int>/ /<ShortValue:short>/ /<LongValue:long>/ /<FloatValue:float>/ /<DoubleValue:double>/ /<CharValue:char>/ /<StringValue:String>/ /<IntValueTransformed:int>/ /<ShortValueTransformed:short>/ /<LongValueTransformed:long>/ /<FloatValueTransformed:float>/ /<DoubleValueTransformed:double>/ /<CharValueTransformed:char>/ /<StringValueTransformed:String>/ ;
 // boxed types, synthesized
-BoxedTypesSyn ::= <DriverSyn:String> /<IntValue:Integer>/ /<ShortValue:Short>/ /<LongValue:Long>/ /<FloatValue:Float>/ /<DoubleValue:Double>/ /<CharValue:Character>/ ;
+BoxedTypesSyn ::= <DriverSyn:String> /<IntValue:Integer>/ /<ShortValue:Short>/ /<LongValue:Long>/ /<FloatValue:Float>/ /<DoubleValue:Double>/ /<CharValue:Character>/ /<IntValueTransformed:Integer>/ /<ShortValueTransformed:Short>/ /<LongValueTransformed:Long>/ /<FloatValueTransformed:Float>/ /<DoubleValueTransformed:Double>/ /<CharValueTransformed:Character>/ ;
 
 // --- inherited attributes not supported ---
 //// native types, inherited
diff --git a/ragconnect.tests/src/test/01-input/mapping/README.md b/ragconnect.tests/src/test/01-input/mapping/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4414f2eca557de36c2f3f0ff76f367d8fec9e20b
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/mapping/README.md
@@ -0,0 +1,3 @@
+# Default Mapping
+
+Idea: Check different numbers of sequential mappings
diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.connect b/ragconnect.tests/src/test/01-input/mapping/Test.connect
new file mode 100644
index 0000000000000000000000000000000000000000..1b38714a1a61afe69d0b494f0e163e8621d08aab
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/mapping/Test.connect
@@ -0,0 +1,46 @@
+receive NativeTypes.IntValue using String2Int ;
+receive NativeTypes.ShortValue using String2Int, Int2Short ;
+receive NativeTypes.LongValue using String2Int, Int2Short, Short2Long ;
+receive NativeTypes.FloatValue using String2Int, Int2Short, Short2Long, Long2Float ;
+receive NativeTypes.DoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ;
+receive NativeTypes.CharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ;
+
+send NativeTypes.WriteIntValue using String2Int ;
+send NativeTypes.WriteShortValue using String2Int, Int2Short ;
+send NativeTypes.WriteLongValue using String2Int, Int2Short, Short2Long ;
+send NativeTypes.WriteFloatValue using String2Int, Int2Short, Short2Long, Long2Float ;
+send NativeTypes.WriteDoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ;
+send NativeTypes.WriteCharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ;
+
+receive BoxedTypes.IntValue using String2Int ;
+receive BoxedTypes.ShortValue using String2Int, Int2Short ;
+receive BoxedTypes.LongValue using String2Int, Int2Short, Short2Long ;
+receive BoxedTypes.FloatValue using String2Int, Int2Short, Short2Long, Long2Float ;
+receive BoxedTypes.DoubleValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double ;
+receive BoxedTypes.CharValue using String2Int, Int2Short, Short2Long, Long2Float, Float2Double, Double2Char ;
+
+String2Int maps String s to int {:
+  return Integer.parseInt(s);
+:}
+Int2Short maps int i to short {:
+  return (short) i;
+:}
+Short2Long maps short s to long {:
+  return (long) s;
+:}
+Long2Float maps long l to float {:
+  return (float) (l + 0.01);
+:}
+Float2Double maps float f to double {:
+  return (double) f;
+:}
+Double2Char maps double d to char {:
+  return (char) ((int) d);
+:}
+
+NativeTypes.WriteIntValue canDependOn NativeTypes.Driver as nativeIntDependency;
+NativeTypes.WriteShortValue canDependOn NativeTypes.Driver as nativeShortDependency;
+NativeTypes.WriteLongValue canDependOn NativeTypes.Driver as nativeLongDependency;
+NativeTypes.WriteFloatValue canDependOn NativeTypes.Driver as nativeFloatDependency;
+NativeTypes.WriteDoubleValue canDependOn NativeTypes.Driver as nativeDoubleDependency;
+NativeTypes.WriteCharValue canDependOn NativeTypes.Driver as nativeCharDependency;
diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.jadd b/ragconnect.tests/src/test/01-input/mapping/Test.jadd
new file mode 100644
index 0000000000000000000000000000000000000000..7837d7dc7328b0b2ec1563e163383fb242190f5b
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/mapping/Test.jadd
@@ -0,0 +1,9 @@
+aspect Computation {
+  // native types, synthesized
+  syn String NativeTypes.getWriteIntValue() = getDriver();
+  syn String NativeTypes.getWriteShortValue() = getDriver();
+  syn String NativeTypes.getWriteLongValue() = getDriver();
+  syn String NativeTypes.getWriteFloatValue() = getDriver();
+  syn String NativeTypes.getWriteDoubleValue() = getDriver();
+  syn String NativeTypes.getWriteCharValue() = getDriver();
+}
diff --git a/ragconnect.tests/src/test/01-input/mapping/Test.relast b/ragconnect.tests/src/test/01-input/mapping/Test.relast
new file mode 100644
index 0000000000000000000000000000000000000000..c15991d5029b783f96b1d0f77fb6c242f0883bf3
--- /dev/null
+++ b/ragconnect.tests/src/test/01-input/mapping/Test.relast
@@ -0,0 +1,3 @@
+A ::= NativeTypes BoxedTypes ;
+NativeTypes ::= <IntValue:int> <ShortValue:short> <LongValue:long> <FloatValue:float> <DoubleValue:double> <CharValue:char> <Driver:String> /<WriteIntValue:String>/ /<WriteShortValue:String>/ /<WriteLongValue:String>/ /<WriteFloatValue:String>/ /<WriteDoubleValue:String>/ /<WriteCharValue:String>/;
+BoxedTypes ::= <IntValue:Integer> <ShortValue:Short> <LongValue:Long> <FloatValue:Float> <DoubleValue:Double> <CharValue:Character> ;
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
index 35fd0b77128c2e9c4735e3435dd417a48c5ae010..14b26799670088f7b3acf16bb7f6d1946ae8f914 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyReadTest.java
@@ -66,6 +66,15 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     floats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE));
     chars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR));
     chars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING));
+
+    integers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT));
+    integers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT));
+    integers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG));
+    floats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT));
+    floats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE));
+    chars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR));
+    chars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING));
+
     allBoxed.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER));
     allBoxed.connectShortValue(mqttUri(TOPIC_BOXED_SHORT));
     allBoxed.connectLongValue(mqttUri(TOPIC_BOXED_LONG));
@@ -73,6 +82,13 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     allBoxed.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE));
     allBoxed.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER));
 
+    allBoxed.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER));
+    allBoxed.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT));
+    allBoxed.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG));
+    allBoxed.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT));
+    allBoxed.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE));
+    allBoxed.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER));
+
     sender = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
     assertTrue(sender.waitUntilReady(2, TimeUnit.SECONDS));
   }
@@ -117,12 +133,27 @@ public class DefaultOnlyReadTest extends AbstractMqttTest {
     assertEquals(expectedCharValue, chars.getCharValue());
     assertEquals(expectedStringValue, chars.getStringValue());
 
+    assertEquals(expectedIntValue, integers.getIntValueTransformed());
+    assertEquals(expectedShortValue, integers.getShortValueTransformed());
+    assertEquals(expectedLongValue, integers.getLongValueTransformed());
+    assertEquals(expectedFloatValue, floats.getFloatValueTransformed(), TestUtils.DELTA);
+    assertEquals(expectedDoubleValue, floats.getDoubleValueTransformed(), TestUtils.DELTA);
+    assertEquals(expectedCharValue, chars.getCharValueTransformed());
+    assertEquals(expectedStringValue, chars.getStringValueTransformed());
+
     assertEquals(expectedIntValue, allBoxed.getIntValue().intValue());
     assertEquals(expectedShortValue, allBoxed.getShortValue().shortValue());
     assertEquals(expectedLongValue, allBoxed.getLongValue().longValue());
     assertEquals(expectedFloatValue, allBoxed.getFloatValue(), TestUtils.DELTA);
     assertEquals(expectedDoubleValue, allBoxed.getDoubleValue(), TestUtils.DELTA);
     assertEquals(expectedCharValue, allBoxed.getCharValue().charValue());
+
+    assertEquals(expectedIntValue, allBoxed.getIntValueTransformed().intValue());
+    assertEquals(expectedShortValue, allBoxed.getShortValueTransformed().shortValue());
+    assertEquals(expectedLongValue, allBoxed.getLongValueTransformed().longValue());
+    assertEquals(expectedFloatValue, allBoxed.getFloatValueTransformed(), TestUtils.DELTA);
+    assertEquals(expectedDoubleValue, allBoxed.getDoubleValueTransformed(), TestUtils.DELTA);
+    assertEquals(expectedCharValue, allBoxed.getCharValueTransformed().charValue());
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
index 05b217f705d6152adf14f6d1f45f4f394a992a76..3044bb53504018a2586552d60a3c21d0b192f0e2 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/DefaultOnlyWriteTest.java
@@ -26,6 +26,14 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   private static final String TOPIC_NATIVE_CHAR = "native/char";
   private static final String TOPIC_NATIVE_STRING = "native/string";
 
+  private static final String TOPIC_NATIVE_INT_TRANSFORMED = "native/int/t";
+  private static final String TOPIC_NATIVE_SHORT_TRANSFORMED = "native/short/t";
+  private static final String TOPIC_NATIVE_LONG_TRANSFORMED = "native/long/t";
+  private static final String TOPIC_NATIVE_FLOAT_TRANSFORMED = "native/float/t";
+  private static final String TOPIC_NATIVE_DOUBLE_TRANSFORMED = "native/double/t";
+  private static final String TOPIC_NATIVE_CHAR_TRANSFORMED = "native/char/t";
+  private static final String TOPIC_NATIVE_STRING_TRANSFORMED = "native/string/t";
+
   private static final String TOPIC_BOXED_INTEGER = "boxed/Integer";
   private static final String TOPIC_BOXED_SHORT = "boxed/Short";
   private static final String TOPIC_BOXED_LONG = "boxed/Long";
@@ -33,6 +41,13 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   private static final String TOPIC_BOXED_DOUBLE = "boxed/Double";
   private static final String TOPIC_BOXED_CHARACTER = "boxed/Character";
 
+  private static final String TOPIC_BOXED_INTEGER_TRANSFORMED = "boxed/Integer/t";
+  private static final String TOPIC_BOXED_SHORT_TRANSFORMED = "boxed/Short/t";
+  private static final String TOPIC_BOXED_LONG_TRANSFORMED = "boxed/Long/t";
+  private static final String TOPIC_BOXED_FLOAT_TRANSFORMED = "boxed/Float/t";
+  private static final String TOPIC_BOXED_DOUBLE_TRANSFORMED = "boxed/Double/t";
+  private static final String TOPIC_BOXED_CHARACTER_TRANSFORMED = "boxed/Character/t";
+
   private A model;
   private NativeTypesSyn nativeIntegers;
   private NativeTypesSyn nativeFloats;
@@ -41,7 +56,8 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
   private BoxedTypesSyn boxedFloats;
   private BoxedTypesSyn boxedChars;
   private MqttHandler receiver;
-  private ReceiverData data;
+  private ReceiverData dataNormal;
+  private ReceiverData dataTransformed;
 
   @Override
   protected void createModel() {
@@ -79,6 +95,14 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     nativeChars.addNativeCharDependency(nativeChars);
     nativeChars.addNativeStringDependency(nativeChars);
 
+    nativeIntegers.addNativeIntTransformedDependency(nativeIntegers);
+    nativeIntegers.addNativeShortTransformedDependency(nativeIntegers);
+    nativeIntegers.addNativeLongTransformedDependency(nativeIntegers);
+    nativeFloats.addNativeFloatTransformedDependency(nativeFloats);
+    nativeFloats.addNativeDoubleTransformedDependency(nativeFloats);
+    nativeChars.addNativeCharTransformedDependency(nativeChars);
+    nativeChars.addNativeStringTransformedDependency(nativeChars);
+
     boxedIntegers.addBoxedIntDependency(boxedIntegers);
     boxedIntegers.addBoxedShortDependency(boxedIntegers);
     boxedIntegers.addBoxedLongDependency(boxedIntegers);
@@ -86,60 +110,15 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     boxedFloats.addBoxedDoubleDependency(boxedFloats);
     boxedChars.addBoxedCharDependency(boxedChars);
 
-    data = new ReceiverData();
+    boxedIntegers.addBoxedIntTransformedDependency(boxedIntegers);
+    boxedIntegers.addBoxedShortTransformedDependency(boxedIntegers);
+    boxedIntegers.addBoxedLongTransformedDependency(boxedIntegers);
+    boxedFloats.addBoxedFloatTransformedDependency(boxedFloats);
+    boxedFloats.addBoxedDoubleTransformedDependency(boxedFloats);
+    boxedChars.addBoxedCharTransformedDependency(boxedChars);
 
-    receiver.newConnection(TOPIC_NATIVE_INT, bytes -> {
-      data.numberOfNativeIntValues += 1;
-      data.lastNativeIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
-    });
-    receiver.newConnection(TOPIC_NATIVE_SHORT, bytes -> {
-      data.numberOfNativeShortValues += 1;
-      data.lastNativeShortValue = java.nio.ByteBuffer.wrap(bytes).getShort();
-    });
-    receiver.newConnection(TOPIC_NATIVE_LONG, bytes -> {
-      data.numberOfNativeLongValues += 1;
-      data.lastNativeLongValue = java.nio.ByteBuffer.wrap(bytes).getLong();
-    });
-    receiver.newConnection(TOPIC_NATIVE_FLOAT, bytes -> {
-      data.numberOfNativeFloatValues += 1;
-      data.lastNativeFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat();
-    });
-    receiver.newConnection(TOPIC_NATIVE_DOUBLE, bytes -> {
-      data.numberOfNativeDoubleValues += 1;
-      data.lastNativeDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble();
-    });
-    receiver.newConnection(TOPIC_NATIVE_CHAR, bytes -> {
-      data.numberOfNativeCharValues += 1;
-      data.lastNativeCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
-    });
-    receiver.newConnection(TOPIC_NATIVE_STRING, bytes -> {
-      data.numberOfNativeStringValues += 1;
-      data.lastNativeStringValue = new String(bytes);
-    });
-    receiver.newConnection(TOPIC_BOXED_INTEGER, bytes -> {
-      data.numberOfBoxedIntValues += 1;
-      data.lastBoxedIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
-    });
-    receiver.newConnection(TOPIC_BOXED_SHORT, bytes -> {
-      data.numberOfBoxedShortValues += 1;
-      data.lastBoxedShortValue = java.nio.ByteBuffer.wrap(bytes).getShort();
-    });
-    receiver.newConnection(TOPIC_BOXED_LONG, bytes -> {
-      data.numberOfBoxedLongValues += 1;
-      data.lastBoxedLongValue = java.nio.ByteBuffer.wrap(bytes).getLong();
-    });
-    receiver.newConnection(TOPIC_BOXED_FLOAT, bytes -> {
-      data.numberOfBoxedFloatValues += 1;
-      data.lastBoxedFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat();
-    });
-    receiver.newConnection(TOPIC_BOXED_DOUBLE, bytes -> {
-      data.numberOfBoxedDoubleValues += 1;
-      data.lastBoxedDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble();
-    });
-    receiver.newConnection(TOPIC_BOXED_CHARACTER, bytes -> {
-      data.numberOfBoxedCharValues += 1;
-      data.lastBoxedCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
-    });
+    dataNormal = createReceiver(false);
+    dataTransformed = createReceiver(true);
 
     nativeIntegers.connectIntValue(mqttUri(TOPIC_NATIVE_INT), writeCurrentValue);
     nativeIntegers.connectShortValue(mqttUri(TOPIC_NATIVE_SHORT), writeCurrentValue);
@@ -148,53 +127,121 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
     nativeFloats.connectDoubleValue(mqttUri(TOPIC_NATIVE_DOUBLE), writeCurrentValue);
     nativeChars.connectCharValue(mqttUri(TOPIC_NATIVE_CHAR), writeCurrentValue);
     nativeChars.connectStringValue(mqttUri(TOPIC_NATIVE_STRING), writeCurrentValue);
+
+    nativeIntegers.connectIntValueTransformed(mqttUri(TOPIC_NATIVE_INT_TRANSFORMED), writeCurrentValue);
+    nativeIntegers.connectShortValueTransformed(mqttUri(TOPIC_NATIVE_SHORT_TRANSFORMED), writeCurrentValue);
+    nativeIntegers.connectLongValueTransformed(mqttUri(TOPIC_NATIVE_LONG_TRANSFORMED), writeCurrentValue);
+    nativeFloats.connectFloatValueTransformed(mqttUri(TOPIC_NATIVE_FLOAT_TRANSFORMED), writeCurrentValue);
+    nativeFloats.connectDoubleValueTransformed(mqttUri(TOPIC_NATIVE_DOUBLE_TRANSFORMED), writeCurrentValue);
+    nativeChars.connectCharValueTransformed(mqttUri(TOPIC_NATIVE_CHAR_TRANSFORMED), writeCurrentValue);
+    nativeChars.connectStringValueTransformed(mqttUri(TOPIC_NATIVE_STRING_TRANSFORMED), writeCurrentValue);
+
     boxedIntegers.connectIntValue(mqttUri(TOPIC_BOXED_INTEGER), writeCurrentValue);
     boxedIntegers.connectShortValue(mqttUri(TOPIC_BOXED_SHORT), writeCurrentValue);
     boxedIntegers.connectLongValue(mqttUri(TOPIC_BOXED_LONG), writeCurrentValue);
     boxedFloats.connectFloatValue(mqttUri(TOPIC_BOXED_FLOAT), writeCurrentValue);
     boxedFloats.connectDoubleValue(mqttUri(TOPIC_BOXED_DOUBLE), writeCurrentValue);
     boxedChars.connectCharValue(mqttUri(TOPIC_BOXED_CHARACTER), writeCurrentValue);
+
+    boxedIntegers.connectIntValueTransformed(mqttUri(TOPIC_BOXED_INTEGER_TRANSFORMED), writeCurrentValue);
+    boxedIntegers.connectShortValueTransformed(mqttUri(TOPIC_BOXED_SHORT_TRANSFORMED), writeCurrentValue);
+    boxedIntegers.connectLongValueTransformed(mqttUri(TOPIC_BOXED_LONG_TRANSFORMED), writeCurrentValue);
+    boxedFloats.connectFloatValueTransformed(mqttUri(TOPIC_BOXED_FLOAT_TRANSFORMED), writeCurrentValue);
+    boxedFloats.connectDoubleValueTransformed(mqttUri(TOPIC_BOXED_DOUBLE_TRANSFORMED), writeCurrentValue);
+    boxedChars.connectCharValueTransformed(mqttUri(TOPIC_BOXED_CHARACTER_TRANSFORMED), writeCurrentValue);
+  }
+
+  private ReceiverData createReceiver(boolean transformed) {
+    ReceiverData result = new ReceiverData();
+
+    receiver.newConnection(transformed ? TOPIC_NATIVE_INT_TRANSFORMED : TOPIC_NATIVE_INT, bytes -> {
+      result.numberOfNativeIntValues += 1;
+      result.lastNativeIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_SHORT_TRANSFORMED : TOPIC_NATIVE_SHORT, bytes -> {
+      result.numberOfNativeShortValues += 1;
+      result.lastNativeShortValue = java.nio.ByteBuffer.wrap(bytes).getShort();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_LONG_TRANSFORMED : TOPIC_NATIVE_LONG, bytes -> {
+      result.numberOfNativeLongValues += 1;
+      result.lastNativeLongValue = java.nio.ByteBuffer.wrap(bytes).getLong();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_FLOAT_TRANSFORMED : TOPIC_NATIVE_FLOAT, bytes -> {
+      result.numberOfNativeFloatValues += 1;
+      result.lastNativeFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_DOUBLE_TRANSFORMED : TOPIC_NATIVE_DOUBLE, bytes -> {
+      result.numberOfNativeDoubleValues += 1;
+      result.lastNativeDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_CHAR_TRANSFORMED : TOPIC_NATIVE_CHAR, bytes -> {
+      result.numberOfNativeCharValues += 1;
+      result.lastNativeCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
+    });
+    receiver.newConnection(transformed ? TOPIC_NATIVE_STRING_TRANSFORMED : TOPIC_NATIVE_STRING, bytes -> {
+      result.numberOfNativeStringValues += 1;
+      result.lastNativeStringValue = new String(bytes);
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_INTEGER_TRANSFORMED : TOPIC_BOXED_INTEGER, bytes -> {
+      result.numberOfBoxedIntValues += 1;
+      result.lastBoxedIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_SHORT_TRANSFORMED : TOPIC_BOXED_SHORT, bytes -> {
+      result.numberOfBoxedShortValues += 1;
+      result.lastBoxedShortValue = java.nio.ByteBuffer.wrap(bytes).getShort();
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_LONG_TRANSFORMED : TOPIC_BOXED_LONG, bytes -> {
+      result.numberOfBoxedLongValues += 1;
+      result.lastBoxedLongValue = java.nio.ByteBuffer.wrap(bytes).getLong();
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_FLOAT_TRANSFORMED : TOPIC_BOXED_FLOAT, bytes -> {
+      result.numberOfBoxedFloatValues += 1;
+      result.lastBoxedFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat();
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_DOUBLE_TRANSFORMED : TOPIC_BOXED_DOUBLE, bytes -> {
+      result.numberOfBoxedDoubleValues += 1;
+      result.lastBoxedDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble();
+    });
+    receiver.newConnection(transformed ? TOPIC_BOXED_CHARACTER_TRANSFORMED : TOPIC_BOXED_CHARACTER, bytes -> {
+      result.numberOfBoxedCharValues += 1;
+      result.lastBoxedCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
+    });
+    return result;
   }
 
   @Override
   protected void communicateSendInitialValue() throws InterruptedException {
     // check initial value
-    TestUtils.waitForMqtt();
     checkData(1, 1, 1.1, 'a', "ab");
 
     // set new value
     setData("2", "2.2", "cd");
 
     // check new value
-    TestUtils.waitForMqtt();
     checkData(2, 2, 2.2, 'c', "cd");
 
     // set new value
     setData("3", "3.2", "ee");
 
     // check new value
-    TestUtils.waitForMqtt();
     checkData(3, 3, 3.2, 'e', "ee");
   }
 
   @Override
   protected void communicateOnlyUpdatedValue() throws InterruptedException {
     // check initial value (will be default values)
-    TestUtils.waitForMqtt();
     checkData(0, null, null, null, null);
 
     // set new value
     setData("2", "2.2", "cd");
 
     // check new value
-    TestUtils.waitForMqtt();
     checkData(1, 2, 2.2, 'c', "cd");
 
     // set new value
     setData("3", "3.2", "ee");
 
     // check new value
-    TestUtils.waitForMqtt();
     checkData(2, 3, 3.2, 'e', "ee");
   }
 
@@ -220,58 +267,61 @@ public class DefaultOnlyWriteTest extends AbstractMqttTest {
 
   private void checkData(int expectedNumberOfValues,
                          Integer expectedInt, Double expectedDouble,
-                         Character expectedChar, String expectedString) {
-    assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues);
-    assertEquals(expectedNumberOfValues, data.numberOfNativeStringValues);
-
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedIntValues);
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedShortValues);
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedLongValues);
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedFloatValues);
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedDoubleValues);
-    assertEquals(expectedNumberOfValues, data.numberOfBoxedCharValues);
-
-    if (expectedInt != null) {
-      assertEquals(expectedInt.intValue(), data.lastNativeIntValue);
-      assertEquals(expectedInt.shortValue(), data.lastNativeShortValue);
-      assertEquals(expectedInt.longValue(), data.lastNativeLongValue);
-      assertEquals(expectedInt.intValue(), data.lastBoxedIntValue.intValue());
-      assertEquals(expectedInt.shortValue(), data.lastBoxedShortValue.shortValue());
-      assertEquals(expectedInt.longValue(), data.lastBoxedLongValue.longValue());
-    } else {
-      assertEquals(0, data.lastNativeIntValue);
-      assertEquals(0, data.lastNativeShortValue);
-      assertEquals(0, data.lastNativeLongValue);
-      assertNull(data.lastBoxedIntValue);
-      assertNull(data.lastBoxedShortValue);
-      assertNull(data.lastBoxedLongValue);
-    }
-
-    if (expectedDouble != null) {
-      assertEquals(expectedDouble.floatValue(), data.lastNativeFloatValue, TestUtils.DELTA);
-      assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA);
-      assertEquals(expectedDouble.floatValue(), data.lastBoxedFloatValue, TestUtils.DELTA);
-      assertEquals(expectedDouble, data.lastBoxedDoubleValue, TestUtils.DELTA);
-    } else {
-      assertEquals(0f, data.lastNativeFloatValue, TestUtils.DELTA);
-      assertEquals(0d, data.lastNativeDoubleValue, TestUtils.DELTA);
-      assertNull(data.lastBoxedFloatValue);
-      assertNull(data.lastBoxedDoubleValue);
-    }
-
-    if (expectedChar != null) {
-      assertEquals(expectedChar.charValue(), data.lastNativeCharValue);
-      assertEquals(expectedChar, data.lastBoxedCharValue);
-    } else {
-      assertEquals('\0', data.lastNativeCharValue);
-      assertNull(data.lastBoxedCharValue);
+                         Character expectedChar, String expectedString) throws InterruptedException {
+    TestUtils.waitForMqtt();
+    for (ReceiverData data : new ReceiverData[]{dataNormal, dataTransformed}) {
+      assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues);
+      assertEquals(expectedNumberOfValues, data.numberOfNativeStringValues);
+
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedIntValues);
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedShortValues);
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedLongValues);
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedFloatValues);
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedDoubleValues);
+      assertEquals(expectedNumberOfValues, data.numberOfBoxedCharValues);
+
+      if (expectedInt != null) {
+        assertEquals(expectedInt.intValue(), data.lastNativeIntValue);
+        assertEquals(expectedInt.shortValue(), data.lastNativeShortValue);
+        assertEquals(expectedInt.longValue(), data.lastNativeLongValue);
+        assertEquals(expectedInt.intValue(), data.lastBoxedIntValue.intValue());
+        assertEquals(expectedInt.shortValue(), data.lastBoxedShortValue.shortValue());
+        assertEquals(expectedInt.longValue(), data.lastBoxedLongValue.longValue());
+      } else {
+        assertEquals(0, data.lastNativeIntValue);
+        assertEquals(0, data.lastNativeShortValue);
+        assertEquals(0, data.lastNativeLongValue);
+        assertNull(data.lastBoxedIntValue);
+        assertNull(data.lastBoxedShortValue);
+        assertNull(data.lastBoxedLongValue);
+      }
+
+      if (expectedDouble != null) {
+        assertEquals(expectedDouble.floatValue(), data.lastNativeFloatValue, TestUtils.DELTA);
+        assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA);
+        assertEquals(expectedDouble.floatValue(), data.lastBoxedFloatValue, TestUtils.DELTA);
+        assertEquals(expectedDouble, data.lastBoxedDoubleValue, TestUtils.DELTA);
+      } else {
+        assertEquals(0f, data.lastNativeFloatValue, TestUtils.DELTA);
+        assertEquals(0d, data.lastNativeDoubleValue, TestUtils.DELTA);
+        assertNull(data.lastBoxedFloatValue);
+        assertNull(data.lastBoxedDoubleValue);
+      }
+
+      if (expectedChar != null) {
+        assertEquals(expectedChar.charValue(), data.lastNativeCharValue);
+        assertEquals(expectedChar, data.lastBoxedCharValue);
+      } else {
+        assertEquals('\0', data.lastNativeCharValue);
+        assertNull(data.lastBoxedCharValue);
+      }
+      assertEquals(expectedString, data.lastNativeStringValue);
     }
-    assertEquals(expectedString, data.lastNativeStringValue);
   }
 
   private static class ReceiverData {
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
index 4c323595ae5e339a0bfeeb0e357a206b33682542..00123e5dbec5cc1a26e3c7d85620b64160943a12 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/IncrementalDependencyTest.java
@@ -17,7 +17,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  *
  * @author rschoene - Initial contribution
  */
-@Tag("NewTest")
 public class IncrementalDependencyTest extends AbstractMqttTest {
 
   private static final String TOPIC_IN = "in/a";
@@ -42,6 +41,7 @@ public class IncrementalDependencyTest extends AbstractMqttTest {
     b2 = new B();
     model.addB(b1);
     model.addB(b2);
+    model.ragconnectCheckIncremental();
   }
 
   @Override
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..84ad0b9e90305e4acbe3a582f403310330c7c9e3
--- /dev/null
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/MappingTest.java
@@ -0,0 +1,218 @@
+package org.jastadd.ragconnect.tests;
+
+import mapping.ast.A;
+import mapping.ast.BoxedTypes;
+import mapping.ast.MqttHandler;
+import mapping.ast.NativeTypes;
+import org.junit.jupiter.api.Tag;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case "mapping".
+ *
+ * @author rschoene - Initial contribution
+ */
+@Tag("NewTest")
+public class MappingTest extends AbstractMqttTest {
+
+  private static final String TOPIC_INPUT = "input";
+  private static final String TOPIC_WRITE_NATIVE_INT = "native/int";
+  private static final String TOPIC_WRITE_NATIVE_SHORT = "native/short";
+  private static final String TOPIC_WRITE_NATIVE_LONG = "native/long";
+  private static final String TOPIC_WRITE_NATIVE_FLOAT = "native/float";
+  private static final String TOPIC_WRITE_NATIVE_DOUBLE = "native/double";
+  private static final String TOPIC_WRITE_NATIVE_CHAR = "native/char";
+
+  private A model;
+  private NativeTypes natives;
+  private BoxedTypes boxes;
+  private MqttHandler handler;
+  private ReceiverData data;
+
+  @Override
+  protected void createModel() {
+    model = new A();
+    natives = new NativeTypes();
+    natives.setDriver("1");
+    boxes = new BoxedTypes();
+    model.setNativeTypes(natives);
+    model.setBoxedTypes(boxes);
+  }
+
+  @Override
+  protected void setupReceiverAndConnect(boolean writeCurrentValue) throws IOException {
+    model.ragconnectSetupMqttWaitUntilReady(2, TimeUnit.SECONDS);
+
+    handler = new MqttHandler().dontSendWelcomeMessage().setHost(TestUtils.getMqttHost());
+    assertTrue(handler.waitUntilReady(2, TimeUnit.SECONDS));
+
+    natives.addNativeIntDependency(natives);
+    natives.addNativeShortDependency(natives);
+    natives.addNativeLongDependency(natives);
+    natives.addNativeFloatDependency(natives);
+    natives.addNativeDoubleDependency(natives);
+    natives.addNativeCharDependency(natives);
+
+    data = new ReceiverData();
+    handler.newConnection(TOPIC_WRITE_NATIVE_INT, bytes -> {
+      data.numberOfNativeIntValues += 1;
+      data.lastNativeIntValue = java.nio.ByteBuffer.wrap(bytes).getInt();
+    });
+    handler.newConnection(TOPIC_WRITE_NATIVE_SHORT, bytes -> {
+      data.numberOfNativeShortValues += 1;
+      data.lastNativeShortValue = java.nio.ByteBuffer.wrap(bytes).getShort();
+    });
+    handler.newConnection(TOPIC_WRITE_NATIVE_LONG, bytes -> {
+      data.numberOfNativeLongValues += 1;
+      data.lastNativeLongValue = java.nio.ByteBuffer.wrap(bytes).getLong();
+    });
+    handler.newConnection(TOPIC_WRITE_NATIVE_FLOAT, bytes -> {
+      data.numberOfNativeFloatValues += 1;
+      data.lastNativeFloatValue = java.nio.ByteBuffer.wrap(bytes).getFloat();
+    });
+    handler.newConnection(TOPIC_WRITE_NATIVE_DOUBLE, bytes -> {
+      data.numberOfNativeDoubleValues += 1;
+      data.lastNativeDoubleValue = java.nio.ByteBuffer.wrap(bytes).getDouble();
+    });
+    handler.newConnection(TOPIC_WRITE_NATIVE_CHAR, bytes -> {
+      data.numberOfNativeCharValues += 1;
+      data.lastNativeCharValue = java.nio.ByteBuffer.wrap(bytes).getChar();
+    });
+
+    natives.connectWriteIntValue(mqttUri(TOPIC_WRITE_NATIVE_INT), writeCurrentValue);
+    natives.connectWriteShortValue(mqttUri(TOPIC_WRITE_NATIVE_SHORT), writeCurrentValue);
+    natives.connectWriteLongValue(mqttUri(TOPIC_WRITE_NATIVE_LONG), writeCurrentValue);
+    natives.connectWriteFloatValue(mqttUri(TOPIC_WRITE_NATIVE_FLOAT), writeCurrentValue);
+    natives.connectWriteDoubleValue(mqttUri(TOPIC_WRITE_NATIVE_DOUBLE), writeCurrentValue);
+    natives.connectWriteCharValue(mqttUri(TOPIC_WRITE_NATIVE_CHAR), writeCurrentValue);
+
+    natives.connectIntValue(mqttUri(TOPIC_INPUT));
+    natives.connectShortValue(mqttUri(TOPIC_INPUT));
+    natives.connectLongValue(mqttUri(TOPIC_INPUT));
+    natives.connectFloatValue(mqttUri(TOPIC_INPUT));
+    natives.connectDoubleValue(mqttUri(TOPIC_INPUT));
+    natives.connectCharValue(mqttUri(TOPIC_INPUT));
+
+    boxes.connectIntValue(mqttUri(TOPIC_INPUT));
+    boxes.connectShortValue(mqttUri(TOPIC_INPUT));
+    boxes.connectLongValue(mqttUri(TOPIC_INPUT));
+    boxes.connectFloatValue(mqttUri(TOPIC_INPUT));
+    boxes.connectDoubleValue(mqttUri(TOPIC_INPUT));
+    boxes.connectCharValue(mqttUri(TOPIC_INPUT));
+  }
+
+  @Override
+  protected void communicateSendInitialValue() throws InterruptedException {
+    checkSendData(1, 1, (short) 1, 1, 1.01f, 1.01d, (char) 1);
+    // no check for initial received data (no input set yet)
+
+    // send first value
+    sendAndSetData("21", "31");
+    checkSendData(2, 21, (short) 21, 21, 21.01f, 21.01d, (char) 21);
+    checkReceiveData(31, (short) 31, 31, 31.01f, 31.01d, (char) 31);
+
+    // send same value
+    sendAndSetData("21", "31");
+    checkSendData(2, 21, (short) 21, 21, 21.01f, 21.01d, (char) 21);
+    checkReceiveData(31, (short) 31, 31, 31.01f, 31.01d, (char) 31);
+
+    // send new value
+    sendAndSetData("22", "32");
+    checkSendData(3, 22, (short) 22, 22, 22.01f, 22.01d, (char) 22);
+    checkReceiveData(32, (short) 32, 32, 32.01f, 32.01d, (char) 32);
+  }
+
+  @Override
+  protected void communicateOnlyUpdatedValue() throws InterruptedException {
+    checkSendData(0, 0, (short) 0, 0, 0f, 0d, (char) 0);
+    // no check for initial received data (no input set yet)
+
+    // send first value
+    sendAndSetData("41", "51");
+    checkSendData(1, 41, (short) 41, 41, 41.01f, 41.01d, (char) 41);
+    checkReceiveData(51, (short) 51, 51, 51.01f, 51.01d, (char) 51);
+
+    // send same value
+    sendAndSetData("41", "51");
+    checkSendData(1, 41, (short) 41, 41, 41.01f, 41.01d, (char) 41);
+    checkReceiveData(51, (short) 51, 51, 51.01f, 51.01d, (char) 51);
+
+    // send new value
+    sendAndSetData("42", "52");
+    checkSendData(2, 42, (short) 42, 42, 42.01f, 42.01d, (char) 42);
+    checkReceiveData(52, (short) 52, 52, 52.01f, 52.01d, (char) 52);
+  }
+
+  @Override
+  protected void closeConnections() {
+    if (handler != null) {
+      handler.close();
+    }
+    if (model != null) {
+      model.ragconnectCloseConnections();
+    }
+  }
+
+  private void sendAndSetData(String driver, String input) {
+    natives.setDriver(driver);
+    handler.publish(TOPIC_INPUT, input.getBytes());
+  }
+
+  private void checkSendData(int expectedNumberOfValues, int expectedInt, short expectedShort, long expectedLong, float expectedFloat, double expectedDouble, char expectedChar) throws InterruptedException {
+    TestUtils.waitForMqtt();
+    assertEquals(expectedNumberOfValues, data.numberOfNativeIntValues);
+    assertEquals(expectedNumberOfValues, data.numberOfNativeShortValues);
+    assertEquals(expectedNumberOfValues, data.numberOfNativeLongValues);
+    assertEquals(expectedNumberOfValues, data.numberOfNativeFloatValues);
+    assertEquals(expectedNumberOfValues, data.numberOfNativeDoubleValues);
+    assertEquals(expectedNumberOfValues, data.numberOfNativeCharValues);
+
+    if (expectedNumberOfValues == 0) {
+      return;
+    }
+    assertEquals(expectedInt, data.lastNativeIntValue);
+    assertEquals(expectedShort, data.lastNativeShortValue);
+    assertEquals(expectedLong, data.lastNativeLongValue);
+    assertEquals(expectedFloat, data.lastNativeFloatValue, TestUtils.DELTA);
+    assertEquals(expectedDouble, data.lastNativeDoubleValue, TestUtils.DELTA);
+    assertEquals(expectedChar, data.lastNativeCharValue);
+  }
+
+  private void checkReceiveData(int expectedInt, short expectedShort, long expectedLong, float expectedFloat, double expectedDouble, char expectedChar) {
+    assertEquals(expectedInt, natives.getIntValue());
+    assertEquals(expectedShort, natives.getShortValue());
+    assertEquals(expectedLong, natives.getLongValue());
+    assertEquals(expectedFloat, natives.getFloatValue(), TestUtils.DELTA);
+    assertEquals(expectedDouble, natives.getDoubleValue(), TestUtils.DELTA);
+    assertEquals(expectedChar, natives.getCharValue());
+
+    assertEquals(expectedInt, boxes.getIntValue());
+    assertEquals(expectedShort, boxes.getShortValue());
+    assertEquals(expectedLong, boxes.getLongValue());
+    assertEquals(expectedFloat, boxes.getFloatValue(), TestUtils.DELTA);
+    assertEquals(expectedDouble, boxes.getDoubleValue(), TestUtils.DELTA);
+    assertEquals(expectedChar, boxes.getCharValue());
+  }
+
+  private static class ReceiverData {
+    int lastNativeIntValue;
+    int numberOfNativeIntValues = 0;
+    short lastNativeShortValue;
+    int numberOfNativeShortValues = 0;
+    long lastNativeLongValue;
+    int numberOfNativeLongValues = 0;
+    float lastNativeFloatValue;
+    int numberOfNativeFloatValues = 0;
+    double lastNativeDoubleValue;
+    int numberOfNativeDoubleValues = 0;
+    char lastNativeCharValue;
+    int numberOfNativeCharValues = 0;
+  }
+
+}
diff --git a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
index dbfca5452a1f6058dfe699efce4e3d682b9e19ec..6e66c761ffe4b5e6045d567cc1bdf9a14dea4de0 100644
--- a/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
+++ b/ragconnect.tests/src/test/java/org/jastadd/ragconnect/tests/TutorialTest.java
@@ -2,7 +2,6 @@ package org.jastadd.ragconnect.tests;
 
 import tutorial.ast.A;
 import tutorial.ast.B;
-import tutorial.ast.MqttHandler;
 
 import java.io.IOException;
 
@@ -15,7 +14,6 @@ import static org.jastadd.ragconnect.tests.TestUtils.mqttUri;
  */
 public class TutorialTest extends AbstractMqttTest {
 
-  private MqttHandler handler;
   private A a;
   private B b1;
   private B b2;
@@ -58,9 +56,6 @@ public class TutorialTest extends AbstractMqttTest {
 
   @Override
   protected void closeConnections() {
-    if (handler != null) {
-      handler.close();
-    }
     if (a != null) {
       a.ragconnectCloseConnections();
     }