diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java
index 79cc9ee12917ec4384333b595f7656cf172f0981..323080fe8db4c612786e84cc7b145e1e6987df1f 100644
--- a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java
+++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java
@@ -5,20 +5,37 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.StandardOpenOption;
+import javax.annotation.Nonnull;
 import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
 
-class FileBasedModelJoinWriter implements ModelJoinWriter<Boolean> {
+/**
+ * The {@code FileBasedModelJoinWriter} outputs a {@link ModelJoinExpression model join description}
+ * onto the File System.
+ *
+ * @author Rico Bergmann
+ */
+class FileBasedModelJoinWriter implements ModelJoinWritingService<Boolean> {
 
   private final File outputFile;
   private final StringBasedModelJoinWriter toStringWriter;
 
-  FileBasedModelJoinWriter(File outputFile) {
+  /**
+   * Full constructor.
+   *
+   * @param outputFile the file to write to
+   */
+  FileBasedModelJoinWriter(@Nonnull File outputFile) {
     this.outputFile = outputFile;
     this.toStringWriter = StringBasedModelJoinWriter.withNewlines();
   }
 
+  /**
+   * Writes the given description to the {@code outputFile}.
+   *
+   * @return whether the file was successfully written
+   */
   @Override
-  public Boolean write(ModelJoinExpression modelJoin) {
+  public Boolean write(@Nonnull ModelJoinExpression modelJoin) {
     try (BufferedWriter writer = Files.newBufferedWriter(
         outputFile.toPath(),
         StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriter.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriter.java
index d39eee5ca3901d0b6906dd87a62bc2988b859888..96d3434153904743c2f634cb890be8ba823e32a8 100644
--- a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriter.java
+++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriter.java
@@ -1,9 +1,42 @@
 package org.rosi_project.model_sync.model_join.representation.writer;
 
+import java.io.File;
+import javax.annotation.Nonnull;
 import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
 
-public interface ModelJoinWriter<T> {
+/**
+ * The {@code ModelJoinWriter} acts as an entry point to write and or transform {@link
+ * ModelJoinExpression model join descriptions} to various representations.
+ *
+ * @author Rico Bergmann
+ */
+public class ModelJoinWriter {
 
-  T write(ModelJoinExpression modelJoin);
+  private static final StringBasedModelJoinWriter stringWriter = StringBasedModelJoinWriter
+      .createDefault();
+
+  /**
+   * Converts a whole model join description to a single {@code String}.
+   * <p>
+   * This {@code String} is primarily intended to be human-readable and does not focus in being easy
+   * to reuse for further purposes.
+   */
+  public static String writeToString(@Nonnull ModelJoinExpression modelJoin) {
+    return stringWriter.write(modelJoin);
+  }
+
+  /**
+   * Outputs a whole model join description to a single file.
+   * <p>
+   * The resulting file is intended to be both human-readable as well as to be parsed by further
+   * applications.
+   *
+   * @return whether the expression was written successfully.
+   */
+  public static boolean writeToFile(
+      @Nonnull File outputFile,
+      @Nonnull ModelJoinExpression modelJoin) {
+    return new FileBasedModelJoinWriter(outputFile).write(modelJoin);
+  }
 
 }
diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java
deleted file mode 100644
index 0fe9dff02df00c925616403303f8dccb5fd75174..0000000000000000000000000000000000000000
--- a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.rosi_project.model_sync.model_join.representation.writer;
-
-import java.io.File;
-import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
-
-public class ModelJoinWriterFactory {
-
-  public static String writeToString(ModelJoinExpression modelJoin) {
-    return StringBasedModelJoinWriter.createDefault().write(modelJoin);
-  }
-
-  public static boolean writeToFile(File outputFile, ModelJoinExpression modelJoin) {
-    return new FileBasedModelJoinWriter(outputFile).write(modelJoin);
-  }
-
-}
diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWritingService.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWritingService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f89772ac9e62bef6ad2952892b60862cfcd6510
--- /dev/null
+++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWritingService.java
@@ -0,0 +1,20 @@
+package org.rosi_project.model_sync.model_join.representation.writer;
+
+import javax.annotation.Nonnull;
+import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression;
+
+/**
+ * A {@code ModelJoinWritingService} transforms a {@link ModelJoinExpression model join description}
+ * into some different representation.
+ *
+ * @author Rico Bergmann
+ */
+public interface ModelJoinWritingService<T> {
+
+  /**
+   * Performs the transformation. The result of this method depends on the actual details of the
+   * writing service and should be documented there.
+   */
+  T write(@Nonnull ModelJoinExpression modelJoin);
+
+}
diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java
index 15870e0275f2a39e95390e77f51882f2c309d2cb..8281979371398a408c082622e8d6d10fa37de98d 100644
--- a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java
+++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java
@@ -18,17 +18,23 @@ import org.rosi_project.model_sync.model_join.representation.grammar.ThetaJoinEx
 import org.rosi_project.model_sync.model_join.representation.util.Functional;
 import org.rosi_project.model_sync.model_join.representation.util.Nothing;
 
-class StringBasedModelJoinWriter implements ModelJoinWriter<String> {
+/**
+ * The {@code StringBasedModelJoinWriter} transforms a {@link ModelJoinExpression model join description}
+ * to a single {@code String}.
+ *
+ * @author Rico Bergmann
+ */
+class StringBasedModelJoinWriter implements ModelJoinWritingService<String> {
 
   private static final String NEWLINE_DELIM = "\n";
   private static final String EMPTY_DELIM = " ";
   private static final String DEFAULT_INDENTATION = "  ";
 
-  public static StringBasedModelJoinWriter createDefault() {
+  static StringBasedModelJoinWriter createDefault() {
     return new StringBasedModelJoinWriter(false);
   }
 
-  public static StringBasedModelJoinWriter withNewlines() {
+  static StringBasedModelJoinWriter withNewlines() {
     return new StringBasedModelJoinWriter(true);
   }
 
diff --git a/src/main/java/org/rosi_project/model_sync/util/NeedsCleanup.java b/src/main/java/org/rosi_project/model_sync/util/NeedsCleanup.java
deleted file mode 100644
index 60eea42bb5583c75f1c0a79f72095dd5d99a8a7b..0000000000000000000000000000000000000000
--- a/src/main/java/org/rosi_project/model_sync/util/NeedsCleanup.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.rosi_project.model_sync.util;
-
-public @interface NeedsCleanup {
-
-}
diff --git a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java
index 4a00cb911e8d353c283070350f6c26dd96f0f310..12ab7c144319e4a5a33174852cf402f2e22e644f 100644
--- a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java
+++ b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java
@@ -56,9 +56,9 @@ class TestedModels {
     static final String asString = "theta join imdb.Film with library.VideoCassette as jointarget.Movie\n"
         + "where library.VideoCassette.cast->forAll (p | imdb.Film.figures->playedBy->exists (a | p.\n"
         + "firstname.concat(\" \") .concat(p.lastName) == a.name)) {\n"
-        + "keep attributes imdb.Film.year\n"
-        + "keep outgoing imdb.Film.votes as type jointarget.Vote {\n"
-        + "keep attributes imdb.Vote.score\n"
+        + " keep attributes imdb.Film.year\n"
+        + " keep outgoing imdb.Film.votes as type jointarget.Vote {\n"
+        + " keep attributes imdb.Vote.score\n"
         + "}\n"
         + "keep supertype library.AudioVisualItem as type jointarget.MediaItem {\n"
         + " keep attributes library.AudioVisualItem.minutesLength\n"