diff --git a/src/main/jastadd/Analysis.jrag b/src/main/jastadd/Analysis.jrag
index 139bd082748e4407dbdcbf851f54a8147fba7949..c73a8ca09e31cf61666897f50a4c07e1caea0310 100644
--- a/src/main/jastadd/Analysis.jrag
+++ b/src/main/jastadd/Analysis.jrag
@@ -170,10 +170,14 @@ aspect ComponentAnalysis {
   //--- isList ---
   syn boolean Component.isList() = false;
   eq ListComponent.isList() = true;
+  eq NTAListComponent.isList() = true;
+  eq ManyRelationComponent.isList() = true;
 
   //--- isOpt ---
   syn boolean Component.isOpt() = false;
   eq OptComponent.isOpt() = true;
+  eq NTAOptComponent.isOpt() = true;
+  eq OptionalRelationComponent.isOpt() = true;
 
   //--- isNullable ---
   syn boolean Component.isNullable() = false;
@@ -248,10 +252,6 @@ aspect Constructors {
 
 aspect Utils {
 
-  //--- isMany ---
-  syn boolean RelationComponent.isMany() = false;
-  eq ManyRelationComponent.isMany() = true;
-
   //--- toString ---
   public String SimpleTypeUse.toString() {
     return getID();
diff --git a/src/main/jastadd/backend/API.jadd b/src/main/jastadd/backend/API.jadd
index 481bc2dbbea2bb55c7d0fd6f0bf350e739fba416..9476a471da1954e02347d4c216fb2c45df47bc89 100644
--- a/src/main/jastadd/backend/API.jadd
+++ b/src/main/jastadd/backend/API.jadd
@@ -1,16 +1,75 @@
 aspect BackendAPI {
   public void Relation.generateAPI(StringBuilder sb) {
-    sb.append(ind(1) + "// " + prettyPrint() + "\n");
-    getDirection().generateAPI(sb);
+    sb.append(ind(1) + "// api for " + prettyPrint() + "\n");
+    if (getLeft().isNavigable()) {
+      sb.append(ind(1) + "// left direction for " + prettyPrint() + "\n");
+      getLeft().generateAPI(sb);
+    }
+    if (getRight().isNavigable()) {
+      sb.append(ind(1) + "// right direction for " + prettyPrint() + "\n");
+      getRight().generateAPI(sb);
+    }
     sb.append("\n");
   }
-  public abstract void Direction.generateAPI(StringBuilder sb);
 
+  public void RelationComponent.generateAPI(StringBuilder sb) {
+    if (otherSide().isNavigable()) {
+      if (multiplicityOne() || multiplicityOpt()) {
+        if (otherSide().multiplicityOne() || otherSide().multiplicityOpt()) {
+          generateBiOneOne(sb);
+        } else if (otherSide().multiplicityMany()) {
+          generateBiOneMany(sb);
+        }
+      } else if (multiplicityMany()) {
+        if (otherSide().multiplicityOne() || otherSide().multiplicityOpt()) {
+          generateBiManyOne(sb);
+        } else if (otherSide().multiplicityMany()) {
+          generateBiManyMany(sb);
+        }
+      }
+    } else {
+      if (multiplicityOne() || multiplicityOpt()) {
+        generateDirectedZeroOneAPI(sb);
+      } else if (multiplicityMany()) {
+        generateDirectedManyAPI(sb);
+      }
+    }
+  }
+
+  public void RelationComponent.generateExtraOptAPI(StringBuilder sb) {
+    // has
+    sb.append(ind(1) + "public boolean " + getTypeUse().decl());
+    sb.append(".has" + nameCapitalized() + "() {\n");
+    sb.append(ind(2) + "return ");
+    if (useJastAddNames) {
+      sb.append("get" + nameCapitalized());
+    } else {
+      sb.append(name());
+    }
+    sb.append("() != null;\n");
+    sb.append(ind(1) + "}\n");
+
+    // clear
+    sb.append(ind(1) + "public void " + getTypeUse().decl());
+    sb.append(".clear" + nameCapitalized() + "() {\n");
+    sb.append(ind(2) + "set" + nameCapitalized() + "(null);\n");
+    sb.append(ind(1) + "}\n");
+  }
 
   inh Relation Direction.relation();
+  inh Relation RelationComponent.relation();
   eq Relation.getChild().relation() = this;
   eq Program.getChild().relation() = null;
 
+  inh boolean RelationComponent.isNavigable();
+  eq Relation.getLeft().isNavigable() = getDirection().isNavigableLeftToRight();
+  eq Relation.getRight().isNavigable() = getDirection().isNavigableRightToLeft();
+
+  syn boolean Direction.isNavigableRightToLeft() = true;
+  eq RightDirection.isNavigableRightToLeft() = false;
+  syn boolean Direction.isNavigableLeftToRight() = true;
+  eq LeftDirection.isNavigableLeftToRight() = false;
+
   public String RelationComponent.nameCapitalized() {
     return name().substring(0,1).toUpperCase() + name().substring(1);
   }
diff --git a/src/main/jastadd/backend/BidirectionalAPI.jadd b/src/main/jastadd/backend/BidirectionalAPI.jadd
index 75f28ab40df71d2690208408e6cb314d9107d886..205668357ada568344130746cc1d8a798cab6369 100644
--- a/src/main/jastadd/backend/BidirectionalAPI.jadd
+++ b/src/main/jastadd/backend/BidirectionalAPI.jadd
@@ -1,52 +1,13 @@
 aspect BackendBidirectionalAPI {
-  public void Bidirectional.generateAPI(StringBuilder sb) {
-    RelationComponent l = relation().getLeft();
-    RelationComponent r = relation().getRight();
 
-    if (l.multiplicityOne()) {
-      if (r.multiplicityOne()) {
-        l.generateBiOneOne(sb, false);
-        r.generateBiOneOne(sb, false);
-      } else if (r.multiplicityOpt()) {
-        l.generateBiOneOne(sb, false);
-        r.generateBiOneOne(sb, true);
-      } else if (r.multiplicityMany()) {
-        l.generateBiOneMany(sb, false);
-        r.generateBiManyOne(sb, l);
-      }
-    } else if (l.multiplicityOpt()) {
-      if (r.multiplicityOne()) {
-        l.generateBiOneOne(sb, true);
-        r.generateBiOneOne(sb, false);
-      } else if (r.multiplicityOpt()) {
-        l.generateBiOneOne(sb, true);
-        r.generateBiOneOne(sb, true);
-      } else if (r.multiplicityMany()) {
-        l.generateBiOneMany(sb, true);
-        r.generateBiManyOne(sb, l);
-      }
-    } else if (l.multiplicityMany()) {
-      if (r.multiplicityOne()) {
-        l.generateBiManyOne(sb, r);
-        r.generateBiOneMany(sb, false);
-      } else if (r.multiplicityOpt()) {
-        l.generateBiManyOne(sb, r);
-        r.generateBiOneMany(sb, true);
-      } else if (r.multiplicityMany()) {
-        l.generateBiManyMany(sb, r);
-        r.generateBiManyMany(sb, l);
-      }
-    }
-  }
-
-  public void RelationComponent.generateBiOneOne(StringBuilder sb, boolean isOpt) {
+  public void RelationComponent.generateBiOneOne(StringBuilder sb) {
     // Get
     generateGetOne(sb);
 
     // Set
     sb.append(ind(1) + "public " + getTypeUse().decl() + " " + getTypeUse().decl());
     sb.append(".set" + nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
-    if (!isOpt) {
+    if (!isOpt()) {
       sb.append(ind(2) + "assertNotNull(o);\n");
     }
     // unset the old opposite
@@ -63,7 +24,7 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
     if (resolverHelper | serializer) {
       sb.append(ind(2) + "if (o == null || !o.is$Unresolved()) {\n");
-      if (isOpt) {
+      if (isOpt()) {
         sb.append(ind(3) + "if (o != null) {\n");
         sb.append(ind(4) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
         sb.append(ind(3) + "}\n");
@@ -72,7 +33,7 @@ aspect BackendBidirectionalAPI {
       }
       sb.append(ind(2) + "}\n");
     } else {
-      if (isOpt) {
+      if (isOpt()) {
         sb.append(ind(2) + "if (o != null) {\n");
         sb.append(ind(3) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
         sb.append(ind(2) + "}\n");
@@ -84,12 +45,12 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(2) + "return this;\n");
     sb.append(ind(1) + "}\n");
 
-    if (isOpt) {
+    if (isOpt()) {
       generateExtraOptAPI(sb);
     }
   }
 
-  public void RelationComponent.generateBiManyMany(StringBuilder sb, RelationComponent opposite) {
+  public void RelationComponent.generateBiManyMany(StringBuilder sb) {
     // Get
     sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + getTypeUse().decl() + ".");
     if (useJastAddNames) {
@@ -115,12 +76,12 @@ aspect BackendBidirectionalAPI {
             sb.append(ind(5) + "changed = true;\n");
             sb.append(ind(5) + ofTypeDecl() + " resolvedElement = resolve" + nameCapitalized() + "ByToken(element.as$Unresolved().getUnresolved$Token(), i);\n");
             sb.append(ind(5) + "if (resolvedElement != null && element.as$Unresolved().getUnresolved$ResolveOpposite()) {\n");
-              sb.append(ind(6) + ASTNode.listClass + "<" + getTypeUse().decl() + "> otherList = resolvedElement." + opposite.getImplAttributeField() + ";\n");
+              sb.append(ind(6) + ASTNode.listClass + "<" + getTypeUse().decl() + "> otherList = resolvedElement." + otherSide().getImplAttributeField() + ";\n");
               sb.append(ind(6) + "if (otherList == null) {\n");
                 sb.append(ind(7) + "otherList = new " + listClass + "<>();\n");
               sb.append(ind(6) + "}\n");
               sb.append(ind(6) + "otherList.add(this);\n");
-              sb.append(ind(6) + "resolvedElement.set" + opposite.getImplAttributeName() + "(otherList);\n");
+              sb.append(ind(6) + "resolvedElement.set" + otherSide().getImplAttributeName() + "(otherList);\n");
             sb.append(ind(5) + "}\n");
             sb.append(ind(5) + "l.set(i, resolvedElement);\n");
           sb.append(ind(4) + "}\n");
@@ -195,7 +156,7 @@ aspect BackendBidirectionalAPI {
   }
 
 
-  public void RelationComponent.generateBiManyOne(StringBuilder sb, RelationComponent opposite) {
+  public void RelationComponent.generateBiManyOne(StringBuilder sb) {
     // Get
     sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + getTypeUse().decl() + ".");
     if (useJastAddNames) {
@@ -221,7 +182,7 @@ aspect BackendBidirectionalAPI {
             sb.append(ind(5) + "changed = true;\n");
             sb.append(ind(5) + ofTypeDecl() + " resolvedElement = resolve" + nameCapitalized() + "ByToken(element.as$Unresolved().getUnresolved$Token(), i);\n");
             sb.append(ind(5) + "if (element.as$Unresolved().getUnresolved$ResolveOpposite()) {\n");
-              sb.append(ind(6) + getTypeUse().decl() + " oldTarget = resolvedElement." + opposite.getImplAttributeField() + ";\n");
+              sb.append(ind(6) + getTypeUse().decl() + " oldTarget = resolvedElement." + otherSide().getImplAttributeField() + ";\n");
               sb.append(ind(6) + "if (oldTarget != null && oldTarget != this) {\n");
                 sb.append(ind(7) + "oldTarget." + getImplAttributeField() + ".remove(resolvedElement);\n");
               sb.append(ind(6) + "}\n");
@@ -229,7 +190,7 @@ aspect BackendBidirectionalAPI {
                 sb.append(ind(7) + "l.remove(i);\n");
                 sb.append(ind(7) + "i--;\n");
               sb.append(ind(6) + "} else {\n");
-                sb.append(ind(7) + "resolvedElement.set" + opposite.getImplAttributeName() + "(this);\n");
+                sb.append(ind(7) + "resolvedElement.set" + otherSide().getImplAttributeName() + "(this);\n");
                 sb.append(ind(7) + "l.set(i, resolvedElement);\n");
               sb.append(ind(6) + "}\n");
             sb.append(ind(5) + "} else {\n");
@@ -306,14 +267,14 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(1) + "}\n");
   }
 
-  public void RelationComponent.generateBiOneMany(StringBuilder sb, boolean isOpt) {
+  public void RelationComponent.generateBiOneMany(StringBuilder sb) {
     // Get
     generateGetOne(sb);
 
     // Set
     sb.append(ind(1) + "public " + getTypeUse().decl() + " " + getTypeUse().decl() + ".set" + nameCapitalized()
       + "(" + ofTypeDecl() + " o) {\n");
-    if (!isOpt) {
+    if (!isOpt()) {
       sb.append(ind(2) + "assertNotNull(o);\n");
     }
     sb.append(ind(2) + "if (" + getImplAttributeField() + " != null) {\n");
@@ -325,8 +286,8 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(2) + "}\n");
     sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
 
-    int ind = isOpt ? 3 : 2;
-    if (isOpt) {
+    int ind = isOpt() ? 3 : 2;
+    if (isOpt()) {
       sb.append(ind(2) + "if (o != null) {\n");
     }
     sb.append(ind(ind) + ASTNode.listClass + "<" + getTypeUse().decl() + "> list = o."
@@ -336,13 +297,13 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(ind) + "}\n");
     sb.append(ind(ind) + "list.add(this);\n");
     sb.append(ind(ind) + "o.set" + otherSide().getImplAttributeName() + "(list);\n");
-    if (isOpt) {
+    if (isOpt()) {
       sb.append(ind(2) + "}\n");
     }
     sb.append(ind(2) + "return this;\n");
     sb.append(ind(1) + "}\n");
 
-    if (isOpt) {
+    if (isOpt()) {
       generateExtraOptAPI(sb);
     }
   }
diff --git a/src/main/jastadd/backend/DirectedAPI.jadd b/src/main/jastadd/backend/DirectedAPI.jadd
index 000c47614ec48abbf110f18090a1d08ec55eb758..9bece4a42da28790c391ce82c22c2fcbcf510e5e 100644
--- a/src/main/jastadd/backend/DirectedAPI.jadd
+++ b/src/main/jastadd/backend/DirectedAPI.jadd
@@ -1,36 +1,25 @@
 aspect BackendDirectedAPI {
-  public void RightDirection.generateAPI(StringBuilder sb) {
-    relation().getLeft().generateDirectedAPI(sb);
-  }
-  public void LeftDirection.generateAPI(StringBuilder sb) {
-    relation().getRight().generateDirectedAPI(sb);
-  }
 
-  public abstract void RelationComponent.generateDirectedAPI(StringBuilder sb);
-  public void OneRelationComponent.generateDirectedAPI(StringBuilder sb) {
-    generateDirectedZeroOneAPI(sb, false);
-  }
-  public void OptionalRelationComponent.generateDirectedAPI(StringBuilder sb) {
-    generateDirectedZeroOneAPI(sb, true);
-
-    generateExtraOptAPI(sb);
-  }
-  public void RelationComponent.generateDirectedZeroOneAPI(StringBuilder sb, boolean optional) {
+  public void RelationComponent.generateDirectedZeroOneAPI(StringBuilder sb) {
     // Get
     generateGetOne(sb);
 
     // Set
     sb.append(ind(1) + "public " + getTypeUse().decl() + " " + getTypeUse().decl());
     sb.append(".set" + nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
-    if (!optional) {
+    if (!isOpt()) {
       sb.append(ind(2) + "assertNotNull(o);\n");
     }
     sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
     sb.append(ind(2) + "return this;\n");
     sb.append(ind(1) + "}\n");
+
+    if (isOpt()) {
+      generateExtraOptAPI(sb);
+    }
   }
 
-  public void ManyRelationComponent.generateDirectedAPI(StringBuilder sb) {
+  public void RelationComponent.generateDirectedManyAPI(StringBuilder sb) {
     // Get
     sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + getTypeUse().decl() + ".");
     if (useJastAddNames) {
@@ -130,24 +119,4 @@ aspect BackendDirectedAPI {
     sb.append(ind(2) + "return get" + getImplAttributeName() + "();\n");
     sb.append(ind(1) + "}\n");
   }
-
-  public void RelationComponent.generateExtraOptAPI(StringBuilder sb) {
-    // has
-    sb.append(ind(1) + "public boolean " + getTypeUse().decl());
-    sb.append(".has" + nameCapitalized() + "() {\n");
-    sb.append(ind(2) + "return ");
-    if (useJastAddNames) {
-      sb.append("get" + nameCapitalized());
-    } else {
-      sb.append(name());
-    }
-    sb.append("() != null;\n");
-    sb.append(ind(1) + "}\n");
-
-    // clear
-    sb.append(ind(1) + "public void " + getTypeUse().decl());
-    sb.append(".clear" + nameCapitalized() + "() {\n");
-    sb.append(ind(2) + "set" + nameCapitalized() + "(null);\n");
-    sb.append(ind(1) + "}\n");
-  }
 }
diff --git a/src/main/jastadd/backend/NameResolution.jadd b/src/main/jastadd/backend/NameResolution.jadd
index 1d373e9e4b4c224bf5c65227182e0c2493197737..58194664316ad4d6354424daf9287705c07c0b66 100644
--- a/src/main/jastadd/backend/NameResolution.jadd
+++ b/src/main/jastadd/backend/NameResolution.jadd
@@ -250,7 +250,7 @@ aspect NameResolutionHelper {
       } else {
         sb.append(relationComponent.name());
       }
-      sb.append(relationComponent.isMany() && useJastAddNames ? "List" : "").append("();\n");
+      sb.append(relationComponent.isList() && useJastAddNames ? "List" : "").append("();\n");
     }
       sb.append(ind(2) + "super.resolveAll();\n");
     sb.append(ind(1) + "}\n");