diff --git a/pom.xml b/pom.xml
index 667ccecefb1c3cc07e4dfad8157e8a3474baa31f..8f91f97a0f0d3eb37d323e59a314f30a1f2be199 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,6 +121,12 @@
 			<groupId>commons-lang</groupId>
 			<artifactId>commons-lang</artifactId>
 		</dependency>
+		<!-- While commons-logging is not a required dependency, AJC fails without it. -->
+		<dependency>
+			<groupId>commons-logging</groupId>
+			<artifactId>commons-logging</artifactId>
+			<scope>provided</scope>
+		</dependency>
 		<!-- Spring -->
 		<dependency>
 			<groupId>org.springframework</groupId>
@@ -186,9 +192,14 @@
 			<artifactId>log4j</artifactId>
 		</dependency>
 		<dependency>
-			<groupId>de.thm.couchdb4j</groupId>
-			<artifactId>couchdb4j</artifactId>
-			<version>0.8-SNAPSHOT</version>
+			<groupId>org.ektorp</groupId>
+			<artifactId>org.ektorp</artifactId>
+			<version>1.4.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.ektorp</groupId>
+			<artifactId>org.ektorp.spring</artifactId>
+			<version>1.4.4</version>
 		</dependency>
 		<dependency>
 			<groupId>javax.servlet</groupId>
diff --git a/src/main/java/de/thm/arsnova/cache/CacheBuster.java b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
index 913c36b24ae1d60efca90d156a85a2bbcc197fb3..c998bd4f30ddd4c69b9f6a0eaef4149e93e8fcb0 100644
--- a/src/main/java/de/thm/arsnova/cache/CacheBuster.java
+++ b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
@@ -30,11 +30,11 @@ public class CacheBuster implements ICacheBuster, NovaEventVisitor {
 
 	@CacheEvict(value = "statistics", allEntries = true)
 	@Override
-	public void visit(NewInterposedQuestionEvent event) { }
+	public void visit(NewCommentEvent event) { }
 
 	@CacheEvict(value = "statistics", allEntries = true)
 	@Override
-	public void visit(DeleteInterposedQuestionEvent event) { }
+	public void visit(DeleteCommentEvent event) { }
 
 	@Override
 	public void visit(NewQuestionEvent event) { }
@@ -51,7 +51,7 @@ public class CacheBuster implements ICacheBuster, NovaEventVisitor {
 	@Override
 	public void visit(LockQuestionsEvent lockQuestionsEvent) { }
 
-	@CacheEvict(value = "answers", key = "#event.Question")
+	@CacheEvict(value = "answers", key = "#event.content")
 	@Override
 	public void visit(NewAnswerEvent event) { }
 
diff --git a/src/main/java/de/thm/arsnova/config/AppConfig.java b/src/main/java/de/thm/arsnova/config/AppConfig.java
index 00b5d0c0fa9f4b4af754a1eea92ad47c7242e49e..43cb0b5d024176dd0dc08337ee25629a30ed8c7c 100644
--- a/src/main/java/de/thm/arsnova/config/AppConfig.java
+++ b/src/main/java/de/thm/arsnova/config/AppConfig.java
@@ -18,16 +18,27 @@
 package de.thm.arsnova.config;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import de.thm.arsnova.ImageUtils;
 import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.client.ConnectorClientImpl;
+import de.thm.arsnova.entities.*;
+import de.thm.arsnova.entities.serialization.CouchDbDocumentModule;
+import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
+import de.thm.arsnova.entities.serialization.View;
+import de.thm.arsnova.persistance.*;
+import de.thm.arsnova.persistance.couchdb.*;
+import de.thm.arsnova.persistance.couchdb.InitializingCouchDbConnector;
 import de.thm.arsnova.socket.ARSnovaSocket;
 import de.thm.arsnova.socket.ARSnovaSocketIOServer;
 import de.thm.arsnova.web.CacheControlInterceptorHandler;
 import de.thm.arsnova.web.CorsFilter;
 import de.thm.arsnova.web.DeprecatedApiInterceptorHandler;
 import de.thm.arsnova.web.ResponseInterceptorHandler;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.impl.StdCouchDbInstance;
+import org.ektorp.spring.HttpClientFactoryBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.config.PropertiesFactoryBean;
@@ -60,6 +71,7 @@ import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 import org.springframework.web.servlet.view.InternalResourceViewResolver;
 
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -114,8 +126,8 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 	@Override
 	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
 		converters.add(stringMessageConverter());
-		converters.add(defaultJsonMessageConverter());
 		converters.add(apiV2JsonMessageConverter());
+		converters.add(defaultJsonMessageConverter());
 		//converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
 	}
 
@@ -163,6 +175,8 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 	@Bean
 	public StringHttpMessageConverter stringMessageConverter() {
 		StringHttpMessageConverter messageConverter = new StringHttpMessageConverter();
+		messageConverter.setDefaultCharset(Charset.forName("UTF-8"));
+		messageConverter.setWriteAcceptCharset(false);
 		List<MediaType> mediaTypes = new ArrayList<>();
 		mediaTypes.add(MediaType.TEXT_PLAIN);
 		messageConverter.setSupportedMediaTypes(mediaTypes);
@@ -178,7 +192,9 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 				.defaultViewInclusion(false)
 				.indentOutput(apiIndent)
 				.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
-		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
+		ObjectMapper mapper = builder.build();
+		mapper.setConfig(mapper.getSerializationConfig().withView(View.Public.class));
+		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
 		List<MediaType> mediaTypes = new ArrayList<>();
 		mediaTypes.add(API_V3_MEDIA_TYPE);
 		mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
@@ -195,8 +211,11 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 				.defaultViewInclusion(false)
 				.indentOutput(apiIndent)
 				.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
-				.featuresToEnable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
-		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
+				.featuresToEnable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
+				.modules(new CouchDbDocumentModule());
+		ObjectMapper mapper = builder.build();
+		mapper.setConfig(mapper.getSerializationConfig().withView(View.Public.class));
+		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(mapper);
 		List<MediaType> mediaTypes = new ArrayList<>();
 		mediaTypes.add(API_V2_MEDIA_TYPE);
 		mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
diff --git a/src/main/java/de/thm/arsnova/config/PersistanceConfig.java b/src/main/java/de/thm/arsnova/config/PersistanceConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..663e9ab8c5d30eb215736603e8c25d4170d55669
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/config/PersistanceConfig.java
@@ -0,0 +1,103 @@
+package de.thm.arsnova.config;
+
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.entities.LogEntry;
+import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.entities.MotdList;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.VisitedSession;
+import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
+import de.thm.arsnova.persistance.*;
+import de.thm.arsnova.persistance.couchdb.*;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.impl.StdCouchDbInstance;
+import org.ektorp.spring.HttpClientFactoryBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+
+@Configuration
+@Profile("!test")
+public class PersistanceConfig {
+	@Value("${couchdb.name}") private String couchDbName;
+	@Value("${couchdb.host}") private String couchDbHost;
+	@Value("${couchdb.port}") private int couchDbPort;
+
+	@Bean
+	public CouchDbConnector couchDbConnector() throws Exception {
+		return new InitializingCouchDbConnector(couchDbName, couchDbInstance(), new CouchDbObjectMapperFactory());
+	}
+
+	@Bean
+	public StdCouchDbInstance couchDbInstance() throws Exception {
+		return new StdCouchDbInstance(couchDbHttpClientFactory().getObject());
+	}
+
+	@Bean
+	public HttpClientFactoryBean couchDbHttpClientFactory() throws Exception {
+		final HttpClientFactoryBean factory = new HttpClientFactoryBean();
+		factory.setHost(couchDbHost);
+		factory.setPort(couchDbPort);
+
+		return factory;
+	}
+
+	@Bean
+	public LogEntryRepository logEntryRepository() throws Exception {
+		return new CouchDbLogEntryRepository(LogEntry.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public UserRepository userRepository() throws Exception {
+		return new CouchDbUserRepository(DbUser.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public SessionRepository sessionRepository() throws Exception {
+		return new CouchDbSessionRepository(Session.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public CommentRepository commentRepository() throws Exception {
+		return new CouchDbCommentRepository(Comment.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public ContentRepository contentRepository() throws Exception {
+		return new CouchDbContentRepository(Content.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public AnswerRepository answerRepository() throws Exception {
+		return new CouchDbAnswerRepository(Answer.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public MotdRepository motdRepository() throws Exception {
+		return new CouchDbMotdRepository(Motd.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public MotdListRepository motdListRepository() throws Exception {
+		return new CouchDbMotdListRepository(MotdList.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public VisitedSessionRepository visitedSessionRepository() throws Exception {
+		return new CouchDbVisitedSessionRepository(VisitedSession.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public StatisticsRepository statisticsRepository() throws Exception {
+		return new CouchDbStatisticsRepository(Object.class, couchDbConnector(), false);
+	}
+
+	@Bean
+	public SessionStatisticsRepository sessionStatisticsRepository() throws Exception {
+		return new CouchDbSessionStatisticsRepository(Object.class, couchDbConnector(), false);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java b/src/main/java/de/thm/arsnova/controller/CommentController.java
similarity index 59%
rename from src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
rename to src/main/java/de/thm/arsnova/controller/CommentController.java
index 2de2dc3638733d9ded5968821bbc7ea6a3233510..e38c98630f2406217bcdc5f0c532f86db677b36c 100644
--- a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/CommentController.java
@@ -17,10 +17,10 @@
  */
 package de.thm.arsnova.controller;
 
-import de.thm.arsnova.entities.InterposedReadingCount;
-import de.thm.arsnova.entities.transport.InterposedQuestion;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.transport.Comment;
 import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -41,50 +41,50 @@ import org.springframework.web.bind.annotation.RestController;
 import java.util.List;
 
 /**
- * Handles requests related to audience questions, which are also called interposed or feedback questions.
+ * Handles requests related to comments.
  */
 @RestController
 @RequestMapping("/audiencequestion")
-@Api(value = "/audiencequestion", description = "the Audience Question API")
-public class AudienceQuestionController extends PaginationController {
+@Api(value = "/audiencequestion", description = "the Audience Content API")
+public class CommentController extends PaginationController {
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
-	@ApiOperation(value = "Count all the questions in current session",
+	@ApiOperation(value = "Count all the comments in current session",
 			nickname = "getAudienceQuestionCount")
 	@RequestMapping(value = "/count", method = RequestMethod.GET)
 	@DeprecatedApi
 	@Deprecated
 	public int getInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
-		return questionService.getInterposedCount(sessionkey);
+		return contentService.getInterposedCount(sessionkey);
 	}
 
-	@ApiOperation(value = "count all unread interposed questions",
+	@ApiOperation(value = "count all unread comments",
 			nickname = "getUnreadInterposedCount")
 	@RequestMapping(value = "/readcount", method = RequestMethod.GET)
 	@DeprecatedApi
 	@Deprecated
-	public InterposedReadingCount getUnreadInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam("sessionkey") final String sessionkey, String user) {
-		return questionService.getInterposedReadingCount(sessionkey, user);
+	public CommentReadingCount getUnreadInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam("sessionkey") final String sessionkey, String user) {
+		return contentService.getInterposedReadingCount(sessionkey, user);
 	}
 
-	@ApiOperation(value = "Retrieves all Interposed Questions for a Session",
+	@ApiOperation(value = "Retrieves all Comments for a Session",
 			nickname = "getInterposedQuestions")
 	@RequestMapping(value = "/", method = RequestMethod.GET)
 	@Pagination
-	public List<InterposedQuestion> getInterposedQuestions(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
-		return InterposedQuestion.fromList(questionService.getInterposedQuestions(sessionkey, offset, limit));
+	public List<Comment> getInterposedQuestions(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
+		return Comment.fromList(contentService.getInterposedQuestions(sessionkey, offset, limit));
 	}
 
-	@ApiOperation(value = "Retrieves an InterposedQuestion",
+	@ApiOperation(value = "Retrieves an Comment",
 			nickname = "getInterposedQuestion")
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
-	public InterposedQuestion getInterposedQuestion(@ApiParam(value = "ID of the question that needs to be deleted", required = true) @PathVariable final String questionId) {
-		return new InterposedQuestion(questionService.readInterposedQuestion(questionId));
+	public Comment getInterposedQuestion(@ApiParam(value = "ID of the Comment that needs to be deleted", required = true) @PathVariable final String questionId) {
+		return new Comment(contentService.readInterposedQuestion(questionId));
 	}
 
-	@ApiOperation(value = "Creates a new Interposed Question for a Session and returns the InterposedQuestion's data",
+	@ApiOperation(value = "Creates a new Comment for a Session and returns the Comment's data",
 			nickname = "postInterposedQuestion")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
@@ -93,19 +93,19 @@ public class AudienceQuestionController extends PaginationController {
 	@ResponseStatus(HttpStatus.CREATED)
 	public void postInterposedQuestion(
 			@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey,
-			@ApiParam(value = "the body from the new question", required = true) @RequestBody final de.thm.arsnova.entities.InterposedQuestion question
+			@ApiParam(value = "the body from the new comment", required = true) @RequestBody final de.thm.arsnova.entities.Comment comment
 			) {
-		if (questionService.saveQuestion(question)) {
+		if (contentService.saveQuestion(comment)) {
 			return;
 		}
 
 		throw new BadRequestException();
 	}
 
-	@ApiOperation(value = "Deletes an InterposedQuestion",
+	@ApiOperation(value = "Deletes a Comment",
 			nickname = "deleteInterposedQuestion")
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.DELETE)
-	public void deleteInterposedQuestion(@ApiParam(value = "ID of the question that needs to be deleted", required = true) @PathVariable final String questionId) {
-		questionService.deleteInterposedQuestion(questionId);
+	public void deleteInterposedQuestion(@ApiParam(value = "ID of the comment that needs to be deleted", required = true) @PathVariable final String questionId) {
+		contentService.deleteInterposedQuestion(questionId);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/ContentController.java
similarity index 76%
rename from src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
rename to src/main/java/de/thm/arsnova/controller/ContentController.java
index 85ee7c9c57380a1bd5dd757c4d97c5c440c527fd..9695d365164c78e6bff74e820cd1d12da6c4dd3d 100644
--- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/ContentController.java
@@ -19,12 +19,12 @@ package de.thm.arsnova.controller;
 
 import de.thm.arsnova.PaginationListDecorator;
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -52,9 +52,9 @@ import java.util.List;
 @RestController
 @RequestMapping("/lecturerquestion")
 @Api(value = "/lecturerquestion", description = "Operations for Lecture Questions")
-public class LecturerQuestionController extends PaginationController {
+public class ContentController extends PaginationController {
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	@ApiOperation(value = "Get question with provided question Id",
 			nickname = "getQuestion")
@@ -62,56 +62,56 @@ public class LecturerQuestionController extends PaginationController {
 		@ApiResponse(code = 404, message = HTML_STATUS_404)
 	})
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
-	public Question getQuestion(@PathVariable final String questionId) {
-		final Question question = questionService.getQuestion(questionId);
-		if (question != null) {
-			return question;
+	public Content getQuestion(@PathVariable final String questionId) {
+		final Content content = contentService.getQuestion(questionId);
+		if (content != null) {
+			return content;
 		}
 
 		throw new NotFoundException();
 	}
 
-	@ApiOperation(value = "Post provided question",
+	@ApiOperation(value = "Post provided content",
 			nickname = "postQuestion")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/", method = RequestMethod.POST)
 	@ResponseStatus(HttpStatus.CREATED)
-	public Question postQuestion(@RequestBody final Question question) {
-		if (questionService.saveQuestion(question) != null) {
-			return question;
+	public Content postQuestion(@RequestBody final Content content) {
+		if (contentService.saveQuestion(content) != null) {
+			return content;
 		}
 		throw new BadRequestException();
 	}
 
-	@ApiOperation(value = "Post provided questions", nickname = "bulkPostQuestions")
+	@ApiOperation(value = "Post provided contents", nickname = "bulkPostQuestions")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/bulk", method = RequestMethod.POST)
 	@ResponseStatus(HttpStatus.CREATED)
-	public List<Question> bulkPostQuestions(@RequestBody final List<Question> questions) {
-		for (final Question question : questions) {
-			if (questionService.saveQuestion(question) == null) {
+	public List<Content> bulkPostQuestions(@RequestBody final List<Content> contents) {
+		for (final Content content : contents) {
+			if (contentService.saveQuestion(content) == null) {
 				throw new BadRequestException();
 			}
 		}
-		return questions;
+		return contents;
 	}
 
-	@ApiOperation(value = "Update the question, identified by provided id, with the provided question in the Request Body",
+	@ApiOperation(value = "Update the content, identified by provided id, with the provided content in the Request Body",
 			nickname = "updateQuestion")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.PUT)
-	public Question updateQuestion(
+	public Content updateQuestion(
 			@PathVariable final String questionId,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		try {
-			return questionService.update(question);
+			return contentService.update(content);
 		} catch (final Exception e) {
 			throw new BadRequestException();
 		}
@@ -126,9 +126,9 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 
 		if (fcImage) {
-			return questionService.getQuestionFcImage(questionId);
+			return contentService.getQuestionFcImage(questionId);
 		} else {
-			return questionService.getQuestionImage(questionId);
+			return contentService.getQuestionImage(questionId);
 		}
 	}
 
@@ -139,9 +139,9 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 
 		if (time == 0) {
-			questionService.startNewPiRound(questionId, null);
+			contentService.startNewPiRound(questionId, null);
 		} else {
-			questionService.startNewPiRoundDelayed(questionId, time);
+			contentService.startNewPiRoundDelayed(questionId, time);
 		}
 	}
 
@@ -151,7 +151,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void cancelPiRound(
 			@PathVariable final String questionId
 			) {
-		questionService.cancelPiRoundChange(questionId);
+		contentService.cancelPiRoundChange(questionId);
 	}
 
 	@RequestMapping(value = "/{questionId}/resetpiroundstate", method = RequestMethod.POST)
@@ -160,7 +160,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void resetPiQuestion(
 			@PathVariable final String questionId
 			) {
-		questionService.resetPiRoundState(questionId);
+		contentService.resetPiRoundState(questionId);
 	}
 
 	@ApiOperation(value = "Set voting admission on question, identified by provided id",
@@ -176,7 +176,7 @@ public class LecturerQuestionController extends PaginationController {
 			disable = disableVote;
 		}
 
-		questionService.setVotingAdmission(questionId, disable);
+		contentService.setVotingAdmission(questionId, disable);
 	}
 
 	@ApiOperation(value = "Set voting admission for all questions",
@@ -189,35 +189,35 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
 			) {
 		boolean disable = false;
-		List<Question> questions;
+		List<Content> contents;
 
 		if (disableVote != null) {
 			disable = disableVote;
 		}
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
-			questionService.setVotingAdmissions(sessionkey, disable, questions);
+			contents = contentService.getLectureQuestions(sessionkey);
+			contentService.setVotingAdmissions(sessionkey, disable, contents);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
-			questionService.setVotingAdmissions(sessionkey, disable, questions);
+			contents = contentService.getPreparationQuestions(sessionkey);
+			contentService.setVotingAdmissions(sessionkey, disable, contents);
 		} else {
-			questionService.setVotingAdmissionForAllQuestions(sessionkey, disable);
+			contentService.setVotingAdmissionForAllQuestions(sessionkey, disable);
 		}
 	}
 
-	@ApiOperation(value = "Publish a question, identified by provided id and question in Request Body.",
+	@ApiOperation(value = "Publish a content, identified by provided id and content in Request Body.",
 			nickname = "publishQuestion")
 	@RequestMapping(value = "/{questionId}/publish", method = RequestMethod.POST)
 	public void publishQuestion(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean publish,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (publish != null) {
-			question.setActive(publish);
+			content.setActive(publish);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
 	@ApiOperation(value = "Publish all questions",
@@ -230,52 +230,52 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
 			) {
 		boolean p = publish == null || publish;
-		List<Question> questions;
+		List<Content> contents;
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
-			questionService.publishQuestions(sessionkey, p, questions);
+			contents = contentService.getLectureQuestions(sessionkey);
+			contentService.publishQuestions(sessionkey, p, contents);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
-			questionService.publishQuestions(sessionkey, p, questions);
+			contents = contentService.getPreparationQuestions(sessionkey);
+			contentService.publishQuestions(sessionkey, p, contents);
 		} else {
-			questionService.publishAll(sessionkey, p);
+			contentService.publishAll(sessionkey, p);
 		}
 	}
 
-	@ApiOperation(value = "Publish statistics from question with provided id",
+	@ApiOperation(value = "Publish statistics from content with provided id",
 			nickname = "publishStatistics")
 	@RequestMapping(value = "/{questionId}/publishstatistics", method = RequestMethod.POST)
 	public void publishStatistics(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean showStatistics,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (showStatistics != null) {
-			question.setShowStatistic(showStatistics);
+			content.setShowStatistic(showStatistics);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
-	@ApiOperation(value = "Publish correct answer from question with provided id",
+	@ApiOperation(value = "Publish correct answer from content with provided id",
 			nickname = "publishCorrectAnswer")
 	@RequestMapping(value = "/{questionId}/publishcorrectanswer", method = RequestMethod.POST)
 	public void publishCorrectAnswer(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean showCorrectAnswer,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (showCorrectAnswer != null) {
-			question.setShowAnswer(showCorrectAnswer);
+			content.setShowAnswer(showCorrectAnswer);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
 	@ApiOperation(value = "Get skill questions",
 			nickname = "getSkillQuestions")
 	@RequestMapping(value = "/", method = RequestMethod.GET)
 	@Pagination
-	public List<Question> getSkillQuestions(
+	public List<Content> getSkillQuestions(
 			@RequestParam final String sessionkey,
 			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
 			@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@@ -283,24 +283,24 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "requestImageData", defaultValue = "false") final boolean requestImageData,
 			final HttpServletResponse response
 			) {
-		List<Question> questions;
+		List<Content> contents;
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
+			contents = contentService.getLectureQuestions(sessionkey);
 		} else if (flashcardsOnly) {
-			questions = questionService.getFlashcards(sessionkey);
+			contents = contentService.getFlashcards(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
+			contents = contentService.getPreparationQuestions(sessionkey);
 		} else {
-			questions = questionService.getSkillQuestions(sessionkey);
+			contents = contentService.getSkillQuestions(sessionkey);
 		}
-		if (questions == null || questions.isEmpty()) {
+		if (contents == null || contents.isEmpty()) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
 			return null;
 		} else if (!requestImageData) {
-			questions = questionService.replaceImageData(questions);
+			contents = contentService.replaceImageData(contents);
 		}
 
-		return new PaginationListDecorator<>(questions, offset, limit);
+		return new PaginationListDecorator<>(contents, offset, limit);
 	}
 
 	@ApiOperation(value = "Delete skill questions",
@@ -314,13 +314,13 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 		if (lectureQuestionsOnly) {
-			questionService.deleteLectureQuestions(sessionkey);
+			contentService.deleteLectureQuestions(sessionkey);
 		} else if (flashcardsOnly) {
-			questionService.deleteFlashcards(sessionkey);
+			contentService.deleteFlashcards(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questionService.deletePreparationQuestions(sessionkey);
+			contentService.deletePreparationQuestions(sessionkey);
 		} else {
-			questionService.deleteAllQuestions(sessionkey);
+			contentService.deleteAllQuestions(sessionkey);
 		}
 	}
 
@@ -336,13 +336,13 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
 			) {
 		if (lectureQuestionsOnly) {
-			return questionService.getLectureQuestionCount(sessionkey);
+			return contentService.getLectureQuestionCount(sessionkey);
 		} else if (flashcardsOnly) {
-			return questionService.getFlashcardCount(sessionkey);
+			return contentService.getFlashcardCount(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			return questionService.getPreparationQuestionCount(sessionkey);
+			return contentService.getPreparationQuestionCount(sessionkey);
 		} else {
-			return questionService.getSkillQuestionCount(sessionkey);
+			return contentService.getSkillQuestionCount(sessionkey);
 		}
 	}
 
@@ -352,7 +352,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void deleteAnswersAndQuestion(
 			@PathVariable final String questionId
 			) {
-		questionService.deleteQuestion(questionId);
+		contentService.deleteQuestion(questionId);
 	}
 
 	@ApiOperation(value = "Get unanswered skill question ID by provided session ID",
@@ -367,11 +367,11 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 		List<String> answers;
 		if (lectureQuestionsOnly) {
-			answers = questionService.getUnAnsweredLectureQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredLectureQuestionIds(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			answers = questionService.getUnAnsweredPreparationQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredPreparationQuestionIds(sessionkey);
 		} else {
-			answers = questionService.getUnAnsweredQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredQuestionIds(sessionkey);
 		}
 		if (answers == null || answers.isEmpty()) {
 			throw new NoContentException();
@@ -384,7 +384,7 @@ public class LecturerQuestionController extends PaginationController {
 	 * returns a JSON document which represents the given answer of a question.
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answer should be
+	 *            CouchDB Content ID for which the given answer should be
 	 *            retrieved
 	 * @return JSON Document of {@link Answer} or {@link NotFoundException}
 	 * @throws NotFoundException
@@ -402,7 +402,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String questionId,
 			final HttpServletResponse response
 			) {
-		final Answer answer = questionService.getMyAnswer(questionId);
+		final Answer answer = contentService.getMyAnswer(questionId);
 		if (answer == null) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
 			return null;
@@ -418,7 +418,7 @@ public class LecturerQuestionController extends PaginationController {
 	 * properties are set
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answers should be
+	 *            CouchDB Content ID for which the given answers should be
 	 *            retrieved
 	 * @throws NotFoundException
 	 *             if wrong session, wrong question or no answers was given
@@ -436,16 +436,16 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 		List<Answer> answers;
 		if (allAnswers) {
-			answers = questionService.getAllAnswers(questionId, -1, -1);
+			answers = contentService.getAllAnswers(questionId, -1, -1);
 		} else if (null == piRound) {
-			answers = questionService.getAnswers(questionId, offset, limit);
+			answers = contentService.getAnswers(questionId, offset, limit);
 		} else {
 			if (piRound < 1 || piRound > 2) {
 				response.setStatus(HttpStatus.BAD_REQUEST.value());
 
 				return null;
 			}
-			answers = questionService.getAnswers(questionId, piRound, offset, limit);
+			answers = contentService.getAnswers(questionId, piRound, offset, limit);
 		}
 		if (answers == null) {
 			return new ArrayList<>();
@@ -461,7 +461,7 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestBody final de.thm.arsnova.entities.transport.Answer answer,
 			final HttpServletResponse response
 			) {
-		return questionService.saveAnswer(questionId, answer);
+		return contentService.saveAnswer(questionId, answer);
 	}
 
 	@ApiOperation(value = "Update answer, provided in Request Body, identified by question ID and answer ID",
@@ -473,7 +473,7 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestBody final Answer answer,
 			final HttpServletResponse response
 			) {
-		return questionService.updateAnswer(answer);
+		return contentService.updateAnswer(answer);
 	}
 
 	@ApiOperation(value = "Get Image, identified by question ID and answer ID",
@@ -485,7 +485,7 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 
-		return questionService.getImage(questionId, answerId);
+		return contentService.getImage(questionId, answerId);
 	}
 
 	@ApiOperation(value = "Delete answer, identified by question ID and answer ID",
@@ -496,7 +496,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String answerId,
 			final HttpServletResponse response
 			) {
-		questionService.deleteAnswer(questionId, answerId);
+		contentService.deleteAnswer(questionId, answerId);
 	}
 
 	@ApiOperation(value = "Delete answers from a question, identified by question ID",
@@ -506,7 +506,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String questionId,
 			final HttpServletResponse response
 			) {
-		questionService.deleteAnswers(questionId);
+		contentService.deleteAnswers(questionId);
 	}
 
 	@ApiOperation(value = "Delete all answers and questions from a session, identified by sessionkey",
@@ -519,18 +519,18 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 		if (lectureQuestionsOnly) {
-			questionService.deleteAllLectureAnswers(sessionkey);
+			contentService.deleteAllLectureAnswers(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questionService.deleteAllPreparationAnswers(sessionkey);
+			contentService.deleteAllPreparationAnswers(sessionkey);
 		} else {
-			questionService.deleteAllQuestionsAnswers(sessionkey);
+			contentService.deleteAllQuestionsAnswers(sessionkey);
 		}
 	}
 
 	/**
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answers should be
+	 *            CouchDB Content ID for which the given answers should be
 	 *            retrieved
 	 * @return count of answers for given question id
 	 * @throws NotFoundException
@@ -544,7 +544,7 @@ public class LecturerQuestionController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/{questionId}/answercount", method = RequestMethod.GET)
 	public int getAnswerCount(@PathVariable final String questionId) {
-		return questionService.getAnswerCount(questionId);
+		return contentService.getAnswerCount(questionId);
 	}
 
 	@ApiOperation(value = "Get the amount of answers for a question, identified by the question ID",
@@ -552,8 +552,8 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/allroundanswercount", method = RequestMethod.GET)
 	public List<Integer> getAllAnswerCount(@PathVariable final String questionId) {
 		return Arrays.asList(
-			questionService.getAnswerCount(questionId, 1),
-			questionService.getAnswerCount(questionId, 2)
+			contentService.getAnswerCount(questionId, 1),
+			contentService.getAnswerCount(questionId, 2)
 		);
 	}
 
@@ -561,7 +561,7 @@ public class LecturerQuestionController extends PaginationController {
 			nickname = "getTotalAnswerCountByQuestion")
 	@RequestMapping(value = "/{questionId}/totalanswercount", method = RequestMethod.GET)
 	public int getTotalAnswerCountByQuestion(@PathVariable final String questionId) {
-		return questionService.getTotalAnswerCountByQuestion(questionId);
+		return contentService.getTotalAnswerCountByQuestion(questionId);
 	}
 
 	@ApiOperation(value = "Get the amount of answers and abstention answers by a question, identified by the question ID",
@@ -569,8 +569,8 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/answerandabstentioncount", method = RequestMethod.GET)
 	public List<Integer> getAnswerAndAbstentionCount(@PathVariable final String questionId) {
 		return Arrays.asList(
-			questionService.getAnswerCount(questionId),
-			questionService.getAbstentionAnswerCount(questionId)
+			contentService.getAnswerCount(questionId),
+			contentService.getAbstentionAnswerCount(questionId)
 		);
 	}
 
@@ -579,7 +579,7 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/freetextanswer/", method = RequestMethod.GET)
 	@Pagination
 	public List<Answer> getFreetextAnswers(@PathVariable final String questionId) {
-		return questionService.getFreetextAnswers(questionId, offset, limit);
+		return contentService.getFreetextAnswers(questionId, offset, limit);
 	}
 
 	@ApiOperation(value = "Get my answers of an session, identified by the sessionkey",
@@ -588,7 +588,7 @@ public class LecturerQuestionController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/myanswers", method = RequestMethod.GET)
 	public List<Answer> getMyAnswers(@RequestParam final String sessionkey) {
-		return questionService.getMyAnswers(sessionkey);
+		return contentService.getMyAnswers(sessionkey);
 	}
 
 	@ApiOperation(value = "Get the total amount of answers of an session, identified by the sessionkey",
@@ -602,11 +602,11 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
 			) {
 		if (lectureQuestionsOnly) {
-			return questionService.countLectureQuestionAnswers(sessionkey);
+			return contentService.countLectureQuestionAnswers(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			return questionService.countPreparationQuestionAnswers(sessionkey);
+			return contentService.countPreparationQuestionAnswers(sessionkey);
 		} else {
-			return questionService.getTotalAnswerCount(sessionkey);
+			return contentService.getTotalAnswerCount(sessionkey);
 		}
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/LegacyController.java b/src/main/java/de/thm/arsnova/controller/LegacyController.java
index c9f809c63b73415f788860754cec0716d9c2be90..a1d3a9de2a3ff25df4bba88b735f4e46853a8a94 100644
--- a/src/main/java/de/thm/arsnova/controller/LegacyController.java
+++ b/src/main/java/de/thm/arsnova/controller/LegacyController.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.controller;
 
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -33,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
 public class LegacyController extends AbstractController {
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	/* specific routes */
 
@@ -95,7 +95,7 @@ public class LegacyController extends AbstractController {
 	@RequestMapping(value = "/session/{sessionKey}/interposed", method = RequestMethod.DELETE)
 	@ResponseBody
 	public void deleteAllInterposedQuestions(@PathVariable final String sessionKey) {
-		questionService.deleteAllInterposedQuestions(sessionKey);
+		contentService.deleteAllInterposedQuestions(sessionKey);
 	}
 
 	@DeprecatedApi
diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java
index 1c1c6599bd3873f1a4519561d905a69faac235f3..9b0dcd48a023fa8903103653c755eae633174dfc 100644
--- a/src/main/java/de/thm/arsnova/controller/SessionController.java
+++ b/src/main/java/de/thm/arsnova/controller/SessionController.java
@@ -298,7 +298,7 @@ public class SessionController extends PaginationController {
 	public List<ImportExportSession> getExport(
 			@ApiParam(value = "sessionkey", required = true) @RequestParam(value = "sessionkey", defaultValue = "") final List<String> sessionkey,
 			@ApiParam(value = "wether statistics shall be exported", required = true) @RequestParam(value = "withAnswerStatistics", defaultValue = "false") final Boolean withAnswerStatistics,
-			@ApiParam(value = "wether interposed questions shall be exported", required = true) @RequestParam(value = "withFeedbackQuestions", defaultValue = "false") final Boolean withFeedbackQuestions,
+			@ApiParam(value = "wether comments shall be exported", required = true) @RequestParam(value = "withFeedbackQuestions", defaultValue = "false") final Boolean withFeedbackQuestions,
 			final HttpServletResponse response
 		) {
 		List<ImportExportSession> sessions = new ArrayList<>();
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
deleted file mode 100644
index 763a2dacf2b862d752ea8a3762593aa7ac7c1e8e..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ /dev/null
@@ -1,2886 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.dao;
-
-import com.fourspaces.couchdb.Database;
-import com.fourspaces.couchdb.Document;
-import com.fourspaces.couchdb.Results;
-import com.fourspaces.couchdb.RowResult;
-import com.fourspaces.couchdb.View;
-import com.fourspaces.couchdb.ViewResults;
-import com.google.common.collect.Lists;
-import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.domain.CourseScore;
-import de.thm.arsnova.entities.*;
-import de.thm.arsnova.entities.transport.AnswerQueueElement;
-import de.thm.arsnova.entities.transport.ImportExportSession;
-import de.thm.arsnova.entities.transport.ImportExportSession.ImportExportQuestion;
-import de.thm.arsnova.events.NewAnswerEvent;
-import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.services.ISessionService;
-import net.sf.ezmorph.Morpher;
-import net.sf.ezmorph.MorpherRegistry;
-import net.sf.ezmorph.bean.BeanMorpher;
-import net.sf.json.JSONArray;
-import net.sf.json.JSONObject;
-import net.sf.json.util.JSONUtils;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.aop.framework.AopContext;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.CachePut;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.cache.annotation.Caching;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
-import org.springframework.context.annotation.Profile;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Isolation;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.io.IOException;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * Database implementation based on CouchDB.
- *
- * Note to developers:
- *
- * This class makes use of Spring Framework's caching annotations. When you are about to add new functionality,
- * you should also think about the possibility of caching. Ideally, your methods should be dependent on domain
- * objects like Session or Question, which can be used as cache keys. Relying on plain String objects as a key, e.g.
- * by passing only a Session's keyword, will make your cache annotations less readable. You will also need to think
- * about cases where your cache needs to be updated and evicted.
- *
- * In order to use cached methods from within this object, you have to use the getDatabaseDao() method instead of
- * using the "this" pointer. This is because caching is only available if the method is called through a Spring Proxy,
- * which is not the case when using "this".
- *
- * @see <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html">Spring Framework's Cache Abstraction</a>
- * @see <a href="https://github.com/thm-projects/arsnova-backend/wiki/Caching">Caching in ARSnova explained</a>
- */
-@Profile("!test")
-@Service("databaseDao")
-public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware {
-
-	private static final int BULK_PARTITION_SIZE = 500;
-
-	@Autowired
-	private ISessionService sessionService;
-
-	private String databaseHost;
-	private int databasePort;
-	private String databaseName;
-	private Database database;
-
-	private ApplicationEventPublisher publisher;
-
-	private final Queue<AbstractMap.SimpleEntry<Document, AnswerQueueElement>> answerQueue = new ConcurrentLinkedQueue<>();
-
-	private static final Logger logger = LoggerFactory.getLogger(CouchDBDao.class);
-
-	@Value("${couchdb.host}")
-	public void setDatabaseHost(final String newDatabaseHost) {
-		databaseHost = newDatabaseHost;
-	}
-
-	@Value("${couchdb.port}")
-	public void setDatabasePort(final String newDatabasePort) {
-		databasePort = Integer.parseInt(newDatabasePort);
-	}
-
-	@Value("${couchdb.name}")
-	public void setDatabaseName(final String newDatabaseName) {
-		databaseName = newDatabaseName;
-	}
-
-	public void setSessionService(final ISessionService service) {
-		sessionService = service;
-	}
-
-	/**
-	 * <strike>
-	 * Allows access to the proxy object. It has to be used instead of <code>this</code> for local calls to public
-	 * methods for caching purposes. This is an ugly but necessary temporary workaround until a better solution is
-	 * implemented (e.g. use of AspectJ's weaving).
-	 * @return the proxy for CouchDBDao
-	 * </strike>
-	 */
-	private @NonNull IDatabaseDao getDatabaseDao() {
-		return this;
-	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
-
-	@Override
-	public void log(String event, Map<String, Object> payload, LogEntry.LogLevel level) {
-		final Document d = new Document();
-		d.put("timestamp", System.currentTimeMillis());
-		d.put("type", "log");
-		d.put("event", event);
-		d.put("level", level.ordinal());
-		d.put("payload", payload);
-		try {
-			database.saveDocument(d);
-		} catch (final IOException e) {
-			logger.error("Logging of '{}' event to database failed.", event, e);
-		}
-	}
-
-	@Override
-	public void log(String event, Map<String, Object> payload) {
-		log(event, payload, LogEntry.LogLevel.INFO);
-	}
-
-	@Override
-	public void log(String event, LogEntry.LogLevel level, Object... rawPayload) {
-		if (rawPayload.length % 2 != 0) {
-			throw new IllegalArgumentException("");
-		}
-		Map<String, Object> payload = new HashMap<>();
-		for (int i = 0; i < rawPayload.length; i += 2) {
-			payload.put((String) rawPayload[i], rawPayload[i + 1]);
-		}
-		log(event, payload, level);
-	}
-
-	@Override
-	public void log(String event, Object... rawPayload) {
-		log(event, LogEntry.LogLevel.INFO, rawPayload);
-	}
-
-	@Override
-	public List<Session> getMySessions(final User user, final int start, final int limit) {
-		return this.getDatabaseDao().getSessionsForUsername(user.getUsername(), start, limit);
-	}
-
-	@Override
-	public List<Session> getSessionsForUsername(String username, final int start, final int limit) {
-		final View view = new View("session/partial_by_sessiontype_creator_name");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setStartKeyArray("", username);
-		view.setEndKeyArray("", username, "{}");
-
-		final Results<Session> results = getDatabase().queryView(view, Session.class);
-
-		final List<Session> result = new ArrayList<>();
-		for (final RowResult<Session> row : results.getRows()) {
-			final Session session = row.getValue();
-			session.setCreator(row.getKey().getString(1));
-			session.setName(row.getKey().getString(2));
-			session.set_id(row.getId());
-			result.add(session);
-		}
-		return result;
-	}
-
-	@Override
-	public List<Session> getPublicPoolSessions() {
-		final View view = new View("session/partial_by_ppsubject_name_for_publicpool");
-
-		final ViewResults sessions = getDatabase().view(view);
-
-		final List<Session> result = new ArrayList<>();
-
-		for (final Document d : sessions.getResults()) {
-			final Session session = (Session) JSONObject.toBean(
-					d.getJSONObject().getJSONObject("value"),
-					Session.class
-					);
-			session.set_id(d.getId());
-			result.add(session);
-		}
-		return result;
-	}
-
-	@Override
-	public List<SessionInfo> getPublicPoolSessionsInfo() {
-		final List<Session> sessions = this.getPublicPoolSessions();
-		return getInfosForSessions(sessions);
-	}
-
-	@Override
-	public List<Session> getMyPublicPoolSessions(final User user) {
-		final View view = new View("session/partial_by_sessiontype_creator_name");
-		view.setStartKeyArray("public_pool", user.getUsername());
-		view.setEndKeyArray("public_pool", user.getUsername(), "{}");
-
-		final ViewResults sessions = getDatabase().view(view);
-
-		final List<Session> result = new ArrayList<>();
-		for (final Document d : sessions.getResults()) {
-			final Session session = (Session) JSONObject.toBean(
-					d.getJSONObject().getJSONObject("value"),
-					Session.class
-					);
-			session.setCreator(d.getJSONObject().getJSONArray("key").getString(1));
-			session.setName(d.getJSONObject().getJSONArray("key").getString(2));
-			session.set_id(d.getId());
-			result.add(session);
-		}
-		return result;
-	}
-
-	@Override
-	public List<SessionInfo> getMyPublicPoolSessionsInfo(final User user) {
-		final List<Session> sessions = this.getMyPublicPoolSessions(user);
-		if (sessions.isEmpty()) {
-			return new ArrayList<>();
-		}
-		return getInfosForSessions(sessions);
-	}
-
-	@Override
-	public List<SessionInfo> getMySessionsInfo(final User user, final int start, final int limit) {
-		final List<Session> sessions = this.getMySessions(user, start, limit);
-		if (sessions.isEmpty()) {
-			return new ArrayList<>();
-		}
-		return getInfosForSessions(sessions);
-	}
-
-	private List<SessionInfo> getInfosForSessions(final List<Session> sessions) {
-		/* TODO: migrate to new view */
-		final ExtendedView questionCountView = new ExtendedView("content/by_sessionid");
-		final ExtendedView answerCountView = new ExtendedView("answer/by_sessionid");
-		final ExtendedView interposedCountView = new ExtendedView("comment/by_sessionid");
-		final ExtendedView unreadInterposedCountView = new ExtendedView("comment/by_sessionid_read");
-
-		interposedCountView.setSessionIdKeys(sessions);
-		interposedCountView.setGroup(true);
-		questionCountView.setSessionIdKeys(sessions);
-		questionCountView.setGroup(true);
-		answerCountView.setSessionIdKeys(sessions);
-		answerCountView.setGroup(true);
-		List<String> unreadInterposedQueryKeys = new ArrayList<>();
-		for (Session s : sessions) {
-			unreadInterposedQueryKeys.add("[\"" + s.get_id() + "\",false]");
-		}
-		unreadInterposedCountView.setKeys(unreadInterposedQueryKeys);
-		unreadInterposedCountView.setGroup(true);
-		return getSessionInfoData(sessions, questionCountView, answerCountView, interposedCountView, unreadInterposedCountView);
-	}
-
-	private List<SessionInfo> getInfosForVisitedSessions(final List<Session> sessions, final User user) {
-		final ExtendedView answeredQuestionsView = new ExtendedView("answer/by_user_sessionid");
-		final ExtendedView questionIdsView = new ExtendedView("content/by_sessionid");
-		questionIdsView.setSessionIdKeys(sessions);
-		List<String> answeredQuestionQueryKeys = new ArrayList<>();
-		for (Session s : sessions) {
-			answeredQuestionQueryKeys.add("[\"" + user.getUsername() + "\",\"" + s.get_id() + "\"]");
-		}
-		answeredQuestionsView.setKeys(answeredQuestionQueryKeys);
-		return getVisitedSessionInfoData(sessions, answeredQuestionsView, questionIdsView);
-	}
-
-	private List<SessionInfo> getVisitedSessionInfoData(List<Session> sessions,
-			ExtendedView answeredQuestionsView, ExtendedView questionIdsView) {
-		final Map<String, Set<String>> answeredQuestionsMap = new HashMap<>();
-		final Map<String, Set<String>> questionIdMap = new HashMap<>();
-		final ViewResults answeredQuestionsViewResults = getDatabase().view(answeredQuestionsView);
-		final ViewResults questionIdsViewResults = getDatabase().view(questionIdsView);
-
-		// Maps a session ID to a set of question IDs of answered questions of that session
-		for (final Document d : answeredQuestionsViewResults.getResults()) {
-			final String sessionId = d.getJSONArray("key").getString(1);
-			final String questionId = d.getString("value");
-			Set<String> questionIdsInSession = answeredQuestionsMap.get(sessionId);
-			if (questionIdsInSession == null) {
-				questionIdsInSession = new HashSet<>();
-			}
-			questionIdsInSession.add(questionId);
-			answeredQuestionsMap.put(sessionId, questionIdsInSession);
-		}
-
-		// Maps a session ID to a set of question IDs of that session
-		for (final Document d : questionIdsViewResults.getResults()) {
-			final String sessionId = d.getString("key");
-			final String questionId = d.getId();
-			Set<String> questionIdsInSession = questionIdMap.get(sessionId);
-			if (questionIdsInSession == null) {
-				questionIdsInSession = new HashSet<>();
-			}
-			questionIdsInSession.add(questionId);
-			questionIdMap.put(sessionId, questionIdsInSession);
-		}
-
-		// For each session, count the question IDs that are not yet answered
-		Map<String, Integer> unansweredQuestionsCountMap = new HashMap<>();
-		for (final Session s : sessions) {
-			if (!questionIdMap.containsKey(s.get_id())) {
-				continue;
-			}
-			// Note: create a copy of the first set so that we don't modify the contents in the original set
-			Set<String> questionIdsInSession = new HashSet<>(questionIdMap.get(s.get_id()));
-			Set<String> answeredQuestionIdsInSession = answeredQuestionsMap.get(s.get_id());
-			if (answeredQuestionIdsInSession == null) {
-				answeredQuestionIdsInSession = new HashSet<>();
-			}
-			questionIdsInSession.removeAll(answeredQuestionIdsInSession);
-			unansweredQuestionsCountMap.put(s.get_id(), questionIdsInSession.size());
-		}
-
-		List<SessionInfo> sessionInfos = new ArrayList<>();
-		for (Session session : sessions) {
-			int numUnanswered = 0;
-
-			if (unansweredQuestionsCountMap.containsKey(session.get_id())) {
-				numUnanswered = unansweredQuestionsCountMap.get(session.get_id());
-			}
-			SessionInfo info = new SessionInfo(session);
-			info.setNumUnanswered(numUnanswered);
-			sessionInfos.add(info);
-		}
-		return sessionInfos;
-	}
-
-	private List<SessionInfo> getSessionInfoData(final List<Session> sessions,
-			final ExtendedView questionCountView,
-			final ExtendedView answerCountView,
-			final ExtendedView interposedCountView,
-			final ExtendedView unredInterposedCountView) {
-		final ViewResults questionCountViewResults = getDatabase().view(questionCountView);
-		final ViewResults answerCountViewResults = getDatabase().view(answerCountView);
-		final ViewResults interposedCountViewResults = getDatabase().view(interposedCountView);
-		final ViewResults unredInterposedCountViewResults = getDatabase().view(unredInterposedCountView);
-
-		Map<String, Integer> questionCountMap = new HashMap<>();
-		for (final Document d : questionCountViewResults.getResults()) {
-			questionCountMap.put(d.getString("key"), d.getInt("value"));
-		}
-		Map<String, Integer> answerCountMap = new HashMap<>();
-		for (final Document d : answerCountViewResults.getResults()) {
-			answerCountMap.put(d.getString("key"), d.getInt("value"));
-		}
-		Map<String, Integer> interposedCountMap = new HashMap<>();
-		for (final Document d : interposedCountViewResults.getResults()) {
-			interposedCountMap.put(d.getString("key"), d.getInt("value"));
-		}
-		Map<String, Integer> unredInterposedCountMap = new HashMap<>();
-		for (final Document d : unredInterposedCountViewResults.getResults()) {
-			unredInterposedCountMap.put(d.getJSONArray("key").getString(0), d.getInt("value"));
-		}
-
-		List<SessionInfo> sessionInfos = new ArrayList<>();
-		for (Session session : sessions) {
-			int numQuestions = 0;
-			int numAnswers = 0;
-			int numInterposed = 0;
-			int numUnredInterposed = 0;
-			if (questionCountMap.containsKey(session.get_id())) {
-				numQuestions = questionCountMap.get(session.get_id());
-			}
-			if (answerCountMap.containsKey(session.get_id())) {
-				numAnswers = answerCountMap.get(session.get_id());
-			}
-			if (interposedCountMap.containsKey(session.get_id())) {
-				numInterposed = interposedCountMap.get(session.get_id());
-			}
-			if (unredInterposedCountMap.containsKey(session.get_id())) {
-				numUnredInterposed = unredInterposedCountMap.get(session.get_id());
-			}
-
-			SessionInfo info = new SessionInfo(session);
-			info.setNumQuestions(numQuestions);
-			info.setNumAnswers(numAnswers);
-			info.setNumInterposed(numInterposed);
-			info.setNumUnredInterposed(numUnredInterposed);
-			sessionInfos.add(info);
-		}
-		return sessionInfos;
-	}
-
-	@Cacheable("skillquestions")
-	@Override
-	public List<Question> getSkillQuestionsForUsers(final Session session) {
-		final List<Question> questions = new ArrayList<>();
-		final String viewName = "content/doc_by_sessionid_variant_active";
-		final View view1 = new View(viewName);
-		final View view2 = new View(viewName);
-		final View view3 = new View(viewName);
-		view1.setStartKey(session.get_id(), "lecture", true);
-		view1.setEndKey(session.get_id(), "lecture", true, "{}");
-		view2.setStartKey(session.get_id(), "preparation", true);
-		view2.setEndKey(session.get_id(), "preparation", true, "{}");
-		view3.setStartKey(session.get_id(), "flashcard", true);
-		view3.setEndKey(session.get_id(), "flashcard", true, "{}");
-		questions.addAll(getQuestions(view1, session));
-		questions.addAll(getQuestions(view2, session));
-		questions.addAll(getQuestions(view3, session));
-
-		return questions;
-	}
-
-	@Cacheable("skillquestions")
-	@Override
-	public List<Question> getSkillQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKey(session.get_id());
-		view.setEndKey(session.get_id(), "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public int getSkillQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKey(session.get_id());
-		view.setEndKey(session.get_id(), "{}");
-
-		return getQuestionCount(view);
-	}
-
-	@Override
-	@Cacheable("sessions")
-	public Session getSessionFromKeyword(final String keyword) {
-		final View view = new View("session/by_keyword");
-		view.setIncludeDocs(true);
-		view.setKey(keyword);
-		final ViewResults results = getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			throw new NotFoundException();
-		}
-		return (Session) JSONObject.toBean(
-				results.getJSONArray("rows").optJSONObject(0).optJSONObject("doc"),
-				Session.class
-				);
-	}
-
-	@Override
-	@Cacheable("sessions")
-	public Session getSessionFromId(final String sessionId) {
-		try {
-			final Document doc = getDatabase().getDocument(sessionId);
-			if (!"session".equals(doc.getString("type"))) {
-				return null;
-			}
-
-			return (Session) JSONObject.toBean(
-					doc.getJSONObject(),
-					Session.class
-					);
-		} catch (IOException e) {
-			return null;
-		}
-	}
-
-	@Override
-	public Session saveSession(final User user, final Session session) {
-		session.setKeyword(sessionService.generateKeyword());
-		session.setActive(true);
-		session.setFeedbackLock(false);
-
-		final Document sessionDocument = new Document();
-		sessionDocument.put("type", "session");
-		sessionDocument.put("name", session.getName());
-		sessionDocument.put("shortName", session.getShortName());
-		sessionDocument.put("keyword", session.getKeyword());
-		sessionDocument.put("creator", user.getUsername());
-		sessionDocument.put("active", session.isActive());
-		sessionDocument.put("courseType", session.getCourseType());
-		sessionDocument.put("courseId", session.getCourseId());
-		sessionDocument.put("creationTime", session.getCreationTime());
-		sessionDocument.put("learningProgressOptions", JSONObject.fromObject(session.getLearningProgressOptions()));
-		sessionDocument.put("ppAuthorName", session.getPpAuthorName());
-		sessionDocument.put("ppAuthorMail", session.getPpAuthorMail());
-		sessionDocument.put("ppUniversity", session.getPpUniversity());
-		sessionDocument.put("ppLogo", session.getPpLogo());
-		sessionDocument.put("ppSubject", session.getPpSubject());
-		sessionDocument.put("ppLicense", session.getPpLicense());
-		sessionDocument.put("ppDescription", session.getPpDescription());
-		sessionDocument.put("ppFaculty", session.getPpFaculty());
-		sessionDocument.put("ppLevel", session.getPpLevel());
-		sessionDocument.put("sessionType", session.getSessionType());
-		sessionDocument.put("features", JSONObject.fromObject(session.getFeatures()));
-		sessionDocument.put("feedbackLock", session.getFeedbackLock());
-		try {
-			database.saveDocument(sessionDocument);
-			session.set_id(sessionDocument.getId());
-		} catch (final IOException e) {
-			return null;
-		}
-
-		return session.get_id() != null ? session : null;
-	}
-
-	@Override
-	public boolean sessionKeyAvailable(final String keyword) {
-		final View view = new View("session/by_keyword");
-		view.setKey(keyword);
-		final ViewResults results = getDatabase().view(view);
-
-		return !results.containsKey(keyword);
-	}
-
-	private String getSessionKeyword(final String internalSessionId) throws IOException {
-		final Document document = getDatabase().getDocument(internalSessionId);
-		if (document.has("keyword")) {
-			return (String) document.get("keyword");
-		}
-		logger.error("No session found for internal id {}.", internalSessionId);
-		return null;
-	}
-
-	private Database getDatabase() {
-		if (database == null) {
-			try {
-				final com.fourspaces.couchdb.Session session = new com.fourspaces.couchdb.Session(
-						databaseHost,
-						databasePort
-						);
-				database = session.getDatabase(databaseName);
-			} catch (final Exception e) {
-				logger.error("Cannot connect to CouchDB database '{}' on host '{}' using port {}.",
-						databaseName, databaseHost, databasePort, e);
-			}
-		}
-
-		return database;
-	}
-
-	@Caching(evict = {@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session", condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", key = "#session", condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", key = "#session", condition = "#question.getQuestionVariant().equals('flashcard')") },
-			put = {@CachePut(value = "questions", key = "#question._id")})
-	@Override
-	public Question saveQuestion(final Session session, final Question question) {
-		final Document q = toQuestionDocument(session, question);
-		try {
-			database.saveDocument(q);
-			question.set_id(q.getId());
-			question.set_rev(q.getRev());
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not save question {}.", question, e);
-		}
-		return null;
-	}
-
-	private Document toQuestionDocument(final Session session, final Question question) {
-		Document q = new Document();
-
-		question.updateRoundManagementState();
-		q.put("type", "skill_question");
-		q.put("questionType", question.getQuestionType());
-		q.put("ignoreCaseSensitive", question.isIgnoreCaseSensitive());
-		q.put("ignoreWhitespaces", question.isIgnoreWhitespaces());
-		q.put("ignorePunctuation", question.isIgnorePunctuation());
-		q.put("fixedAnswer", question.isFixedAnswer());
-		q.put("strictMode", question.isStrictMode());
-		q.put("rating", question.getRating());
-		q.put("correctAnswer", question.getCorrectAnswer());
-		q.put("questionVariant", question.getQuestionVariant());
-		q.put("sessionId", session.get_id());
-		q.put("subject", question.getSubject());
-		q.put("text", question.getText());
-		q.put("active", question.isActive());
-		q.put("votingDisabled", question.isVotingDisabled());
-		q.put("number", 0); // TODO: This number is now unused. A clean up is necessary.
-		q.put("releasedFor", question.getReleasedFor());
-		q.put("possibleAnswers", question.getPossibleAnswers());
-		q.put("noCorrect", question.isNoCorrect());
-		q.put("piRound", question.getPiRound());
-		q.put("piRoundStartTime", question.getPiRoundStartTime());
-		q.put("piRoundEndTime", question.getPiRoundEndTime());
-		q.put("piRoundFinished", question.isPiRoundFinished());
-		q.put("piRoundActive", question.isPiRoundActive());
-		q.put("showStatistic", question.isShowStatistic());
-		q.put("showAnswer", question.isShowAnswer());
-		q.put("abstention", question.isAbstention());
-		q.put("image", question.getImage());
-		q.put("fcImage", question.getFcImage());
-		q.put("gridSize", question.getGridSize());
-		q.put("offsetX", question.getOffsetX());
-		q.put("offsetY", question.getOffsetY());
-		q.put("zoomLvl", question.getZoomLvl());
-		q.put("gridOffsetX", question.getGridOffsetX());
-		q.put("gridOffsetY", question.getGridOffsetY());
-		q.put("gridZoomLvl", question.getGridZoomLvl());
-		q.put("gridSizeX", question.getGridSizeX());
-		q.put("gridSizeY", question.getGridSizeY());
-		q.put("gridIsHidden", question.getGridIsHidden());
-		q.put("imgRotation", question.getImgRotation());
-		q.put("toggleFieldsLeft", question.getToggleFieldsLeft());
-		q.put("numClickableFields", question.getNumClickableFields());
-		q.put("thresholdCorrectAnswers", question.getThresholdCorrectAnswers());
-		q.put("cvIsColored", question.getCvIsColored());
-		q.put("gridLineColor", question.getGridLineColor());
-		q.put("numberOfDots", question.getNumberOfDots());
-		q.put("gridType", question.getGridType());
-		q.put("scaleFactor", question.getScaleFactor());
-		q.put("gridScaleFactor", question.getGridScaleFactor());
-		q.put("imageQuestion", question.isImageQuestion());
-		q.put("textAnswerEnabled", question.isTextAnswerEnabled());
-		q.put("timestamp", question.getTimestamp());
-		q.put("hint", question.getHint());
-		q.put("solution", question.getSolution());
-		return q;
-	}
-
-	/* TODO: Only evict cache entry for the question's session. This requires some refactoring. */
-	@Caching(evict = {@CacheEvict(value = "skillquestions", allEntries = true),
-			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('flashcard')") },
-			put = {@CachePut(value = "questions", key = "#question._id")})
-	@Override
-	public Question updateQuestion(final Question question) {
-		try {
-			final Document q = database.getDocument(question.get_id());
-
-			question.updateRoundManagementState();
-			q.put("subject", question.getSubject());
-			q.put("text", question.getText());
-			q.put("active", question.isActive());
-			q.put("votingDisabled", question.isVotingDisabled());
-			q.put("releasedFor", question.getReleasedFor());
-			q.put("possibleAnswers", question.getPossibleAnswers());
-			q.put("noCorrect", question.isNoCorrect());
-			q.put("piRound", question.getPiRound());
-			q.put("piRoundStartTime", question.getPiRoundStartTime());
-			q.put("piRoundEndTime", question.getPiRoundEndTime());
-			q.put("piRoundFinished", question.isPiRoundFinished());
-			q.put("piRoundActive", question.isPiRoundActive());
-			q.put("showStatistic", question.isShowStatistic());
-			q.put("ignoreCaseSensitive", question.isIgnoreCaseSensitive());
-			q.put("ignoreWhitespaces", question.isIgnoreWhitespaces());
-			q.put("ignorePunctuation", question.isIgnorePunctuation());
-			q.put("fixedAnswer", question.isFixedAnswer());
-			q.put("strictMode", question.isStrictMode());
-			q.put("rating", question.getRating());
-			q.put("correctAnswer", question.getCorrectAnswer());
-			q.put("showAnswer", question.isShowAnswer());
-			q.put("abstention", question.isAbstention());
-			q.put("image", question.getImage());
-			q.put("fcImage", question.getFcImage());
-			q.put("gridSize", question.getGridSize());
-			q.put("offsetX", question.getOffsetX());
-			q.put("offsetY", question.getOffsetY());
-			q.put("zoomLvl", question.getZoomLvl());
-			q.put("gridOffsetX", question.getGridOffsetX());
-			q.put("gridOffsetY", question.getGridOffsetY());
-			q.put("gridZoomLvl", question.getGridZoomLvl());
-			q.put("gridSizeX", question.getGridSizeX());
-			q.put("gridSizeY", question.getGridSizeY());
-			q.put("gridIsHidden", question.getGridIsHidden());
-			q.put("imgRotation", question.getImgRotation());
-			q.put("toggleFieldsLeft", question.getToggleFieldsLeft());
-			q.put("numClickableFields", question.getNumClickableFields());
-			q.put("thresholdCorrectAnswers", question.getThresholdCorrectAnswers());
-			q.put("cvIsColored", question.getCvIsColored());
-			q.put("gridLineColor", question.getGridLineColor());
-			q.put("numberOfDots", question.getNumberOfDots());
-			q.put("gridType", question.getGridType());
-			q.put("scaleFactor", question.getScaleFactor());
-			q.put("gridScaleFactor", question.getGridScaleFactor());
-			q.put("imageQuestion", question.isImageQuestion());
-			q.put("hint", question.getHint());
-			q.put("solution", question.getSolution());
-
-			database.saveDocument(q);
-			question.set_rev(q.getRev());
-
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not update question {}.", question, e);
-		}
-
-		return null;
-	}
-
-	@Override
-	public InterposedQuestion saveQuestion(final Session session, final InterposedQuestion question, User user) {
-		final Document q = new Document();
-		q.put("type", "interposed_question");
-		q.put("sessionId", session.get_id());
-		q.put("subject", question.getSubject());
-		q.put("text", question.getText());
-		if (question.getTimestamp() != 0) {
-			q.put("timestamp", question.getTimestamp());
-		} else {
-			q.put("timestamp", System.currentTimeMillis());
-		}
-		q.put("read", false);
-		q.put("creator", user.getUsername());
-		try {
-			database.saveDocument(q);
-			question.set_id(q.getId());
-			question.set_rev(q.getRev());
-
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not save interposed question {}.", question, e);
-		}
-
-		return null;
-	}
-
-	@Cacheable("questions")
-	@Override
-	public Question getQuestion(final String id) {
-		try {
-			final Document q = getDatabase().getDocument(id);
-			if (q == null) {
-				return null;
-			}
-			final Question question = (Question) JSONObject.toBean(q.getJSONObject(), Question.class);
-			final JSONArray possibleAnswers = q.getJSONObject().getJSONArray("possibleAnswers");
-			@SuppressWarnings("unchecked")
-			final Collection<PossibleAnswer> answers = JSONArray.toCollection(possibleAnswers, PossibleAnswer.class);
-
-			question.updateRoundManagementState();
-			question.setPossibleAnswers(new ArrayList<>(answers));
-			question.setSessionKeyword(getSessionKeyword(question.getSessionId()));
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not get question {}.", id, e);
-		}
-		return null;
-	}
-
-	@Override
-	public LoggedIn registerAsOnlineUser(final User user, final Session session) {
-		try {
-			final View view = new View("logged_in/all");
-			view.setKey(user.getUsername());
-			final ViewResults results = getDatabase().view(view);
-
-			LoggedIn loggedIn = new LoggedIn();
-			if (results.getJSONArray("rows").optJSONObject(0) != null) {
-				final JSONObject json = results.getJSONArray("rows").optJSONObject(0).optJSONObject("value");
-				loggedIn = (LoggedIn) JSONObject.toBean(json, LoggedIn.class);
-				final JSONArray vs = json.optJSONArray("visitedSessions");
-				if (vs != null) {
-					@SuppressWarnings("unchecked")
-					final Collection<VisitedSession> visitedSessions = JSONArray.toCollection(vs, VisitedSession.class);
-					loggedIn.setVisitedSessions(new ArrayList<>(visitedSessions));
-				}
-
-				/* Do not clutter CouchDB. Only update once every 3 hours per session. */
-				if (loggedIn.getSessionId().equals(session.get_id()) && loggedIn.getTimestamp() > System.currentTimeMillis() - 3 * 3600000) {
-					return loggedIn;
-				}
-			}
-
-			loggedIn.setUser(user.getUsername());
-			loggedIn.setSessionId(session.get_id());
-			loggedIn.addVisitedSession(session);
-			loggedIn.updateTimestamp();
-
-			final JSONObject json = JSONObject.fromObject(loggedIn);
-			final Document doc = new Document(json);
-			if (doc.getId().isEmpty()) {
-				// If this is a new user without a logged_in document, we have
-				// to remove the following
-				// pre-filled fields. Otherwise, CouchDB will take these empty
-				// fields as genuine
-				// identifiers, and will throw errors afterwards.
-				doc.remove("_id");
-				doc.remove("_rev");
-			}
-			getDatabase().saveDocument(doc);
-
-			final LoggedIn l = (LoggedIn) JSONObject.toBean(doc.getJSONObject(), LoggedIn.class);
-			final JSONArray vs = doc.getJSONObject().optJSONArray("visitedSessions");
-			if (vs != null) {
-				@SuppressWarnings("unchecked")
-				final Collection<VisitedSession> visitedSessions = JSONArray.toCollection(vs, VisitedSession.class);
-				l.setVisitedSessions(new ArrayList<>(visitedSessions));
-			}
-			return l;
-		} catch (final IOException e) {
-			return null;
-		}
-	}
-
-	@Override
-	@CachePut(value = "sessions")
-	public Session updateSessionOwnerActivity(final Session session) {
-		try {
-			/* Do not clutter CouchDB. Only update once every 3 hours. */
-			if (session.getLastOwnerActivity() > System.currentTimeMillis() - 3 * 3600000) {
-				return session;
-			}
-
-			session.setLastOwnerActivity(System.currentTimeMillis());
-			final JSONObject json = JSONObject.fromObject(session);
-			getDatabase().saveDocument(new Document(json));
-			return session;
-		} catch (final IOException e) {
-			logger.error("Failed to update lastOwnerActivity for session {}.", session, e);
-			return session;
-		}
-	}
-
-	@Override
-	public List<String> getQuestionIds(final Session session, final User user) {
-		View view = new View("content/by_sessionid_variant_active");
-		view.setKey(session.get_id());
-		return collectQuestionIds(view);
-	}
-
-	/* TODO: Only evict cache entry for the question's session. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", key = "#question._id"),
-			@CacheEvict(value = "skillquestions", allEntries = true),
-			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('flashcard')") })
-	@Override
-	public int deleteQuestionWithAnswers(final Question question) {
-		try {
-			int count = deleteAnswers(question);
-			deleteDocument(question.get_id());
-			log("delete", "type", "question", "answerCount", count);
-
-			return count;
-		} catch (final IOException e) {
-			logger.error("Could not delete question {}.", question.get_id(), e);
-		}
-
-		return 0;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public int[] deleteAllQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKey(session.get_id(), "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	private int[] deleteAllQuestionDocumentsWithAnswers(final View view) {
-		final ViewResults results = getDatabase().view(view);
-
-		List<Question> questions = new ArrayList<>();
-		for (final Document d : results.getResults()) {
-			final Question q = new Question();
-			q.set_id(d.getId());
-			q.set_rev(d.getString("value"));
-			questions.add(q);
-		}
-
-		int[] count = deleteAllAnswersWithQuestions(questions);
-		log("delete", "type", "question", "questionCount", count[0]);
-		log("delete", "type", "answer", "answerCount", count[1]);
-
-		return count;
-	}
-
-	private void deleteDocument(final String documentId) throws IOException {
-		final Document d = getDatabase().getDocument(documentId);
-		getDatabase().deleteDocument(d);
-	}
-
-	@CacheEvict("answers")
-	@Override
-	public int deleteAnswers(final Question question) {
-		try {
-			final View view = new View("answer/by_questionid");
-			view.setKey(question.get_id());
-			view.setIncludeDocs(true);
-			final ViewResults results = getDatabase().view(view);
-			final List<List<Document>> partitions = Lists.partition(results.getResults(), BULK_PARTITION_SIZE);
-
-			int count = 0;
-			for (List<Document> partition: partitions) {
-				List<Document> answersToDelete = new ArrayList<>();
-				for (final Document a : partition) {
-					final Document d = new Document(a.getJSONObject("doc"));
-					d.put("_deleted", true);
-					answersToDelete.add(d);
-				}
-				if (database.bulkSaveDocuments(answersToDelete.toArray(new Document[answersToDelete.size()]))) {
-					count += partition.size();
-				} else {
-					logger.error("Could not bulk delete answers.");
-				}
-			}
-			log("delete", "type", "answer", "answerCount", count);
-
-			return count;
-		} catch (final IOException e) {
-			logger.error("Could not delete answers for question {}.", question.get_id(), e);
-		}
-
-		return 0;
-	}
-
-	@Override
-	public List<String> getUnAnsweredQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_by_user_sessionid_variant");
-		view.setStartKeyArray(user.getUsername(), session.get_id());
-		view.setEndKeyArray(user.getUsername(), session.get_id(), "{}");
-		return collectUnansweredQuestionIds(getQuestionIds(session, user), view);
-	}
-
-	@Override
-	public Answer getMyAnswer(final User me, final String questionId, final int piRound) {
-
-		final View view = new View("answer/doc_by_questionid_user_piround");
-		if (2 == piRound) {
-			view.setKey(questionId, me.getUsername(), "2");
-		} else {
-			/* needed for legacy questions whose piRound property has not been set */
-			view.setStartKey(questionId, me.getUsername());
-			view.setEndKey(questionId, me.getUsername(), "1");
-		}
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return null;
-		}
-		return (Answer) JSONObject.toBean(
-				results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
-				Answer.class
-				);
-	}
-
-	@SuppressWarnings("unchecked")
-	@Override
-	public <T> T getObjectFromId(final String documentId, final Class<T> klass) {
-		try {
-			final Document doc = getDatabase().getDocument(documentId);
-			if (doc == null) {
-				return null;
-			}
-			// TODO: This needs some more error checking...
-			return (T) JSONObject.toBean(doc.getJSONObject(), klass);
-		} catch (ClassCastException | IOException | net.sf.json.JSONException e) {
-			return null;
-		}
-	}
-
-	@Override
-	public List<Answer> getAnswers(final Question question, final int piRound) {
-		final String questionId = question.get_id();
-		final View view = new View("answer/by_questionid_piround_text_subject");
-		if (2 == piRound) {
-			view.setStartKey(questionId, 2);
-			view.setEndKey(questionId, 2, "{}");
-		} else {
-			/* needed for legacy questions whose piRound property has not been set */
-			view.setStartKeyArray(questionId);
-			view.setEndKeyArray(questionId, 1, "{}");
-		}
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-		final int abstentionCount = getDatabaseDao().getAbstentionAnswerCount(questionId);
-		final List<Answer> answers = new ArrayList<>();
-
-		for (final Document d : results.getResults()) {
-			final Answer a = new Answer();
-			a.setAnswerCount(d.getInt("value"));
-			a.setAbstentionCount(abstentionCount);
-			a.setQuestionId(d.getJSONObject().getJSONArray("key").getString(0));
-			a.setPiRound(piRound);
-			final String answerText = d.getJSONObject().getJSONArray("key").getString(3);
-			a.setAnswerText("null".equals(answerText) ? null : answerText);
-			answers.add(a);
-		}
-		return answers;
-	}
-
-	@Override
-	public List<Answer> getAllAnswers(final Question question) {
-		final String questionId = question.get_id();
-		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKeyArray(questionId);
-		view.setEndKeyArray(questionId, "{}");
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-		final int abstentionCount = getDatabaseDao().getAbstentionAnswerCount(questionId);
-
-		final List<Answer> answers = new ArrayList<>();
-		for (final Document d : results.getResults()) {
-			final Answer a = new Answer();
-			a.setAnswerCount(d.getInt("value"));
-			a.setAbstentionCount(abstentionCount);
-			a.setQuestionId(d.getJSONObject().getJSONArray("key").getString(0));
-			final String answerText = d.getJSONObject().getJSONArray("key").getString(3);
-			final String answerSubject = d.getJSONObject().getJSONArray("key").getString(4);
-			final boolean successfulFreeTextAnswer = d.getJSONObject().getJSONArray("key").getBoolean(5);
-			a.setAnswerText("null".equals(answerText) ? null : answerText);
-			a.setAnswerSubject("null".equals(answerSubject) ? null : answerSubject);
-			a.setSuccessfulFreeTextAnswer(successfulFreeTextAnswer);
-			answers.add(a);
-		}
-		return answers;
-	}
-
-	@Cacheable("answers")
-	@Override
-	public List<Answer> getAnswers(final Question question) {
-		return this.getAnswers(question, question.getPiRound());
-	}
-
-	@Override
-	public int getAbstentionAnswerCount(final String questionId) {
-		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKeyArray(questionId);
-		view.setEndKeyArray(questionId, "{}");
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	@Override
-	public int getAnswerCount(final Question question, final int piRound) {
-		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKey(question.get_id(), piRound);
-		view.setEndKey(question.get_id(), piRound, "{}");
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return 0;
-		}
-
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	@Override
-	public int getTotalAnswerCountByQuestion(final Question question) {
-		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKeyArray(question.get_id());
-		view.setEndKeyArray(question.get_id(), "{}");
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-
-		if (results.getResults().isEmpty()) {
-			return 0;
-		}
-
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	private boolean isEmptyResults(final ViewResults results) {
-		return results == null || results.getResults().isEmpty() || results.getJSONArray("rows").isEmpty();
-	}
-
-	@Override
-	public List<Answer> getFreetextAnswers(final String questionId, final int start, final int limit) {
-		final List<Answer> answers = new ArrayList<>();
-		final View view = new View("answer/doc_by_questionid_timestamp");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setDescending(true);
-		view.setStartKeyArray(questionId, "{}");
-		view.setEndKeyArray(questionId);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return answers;
-		}
-		for (final Document d : results.getResults()) {
-			final Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
-			a.setQuestionId(questionId);
-			answers.add(a);
-		}
-		return answers;
-	}
-
-	@Override
-	public List<Answer> getMyAnswers(final User me, final Session s) {
-		final View view = new View("answer/doc_by_user_sessionid");
-		view.setKey(me.getUsername(), s.get_id());
-		final ViewResults results = getDatabase().view(view);
-		final List<Answer> answers = new ArrayList<>();
-		if (results == null || results.getResults() == null || results.getResults().isEmpty()) {
-			return answers;
-		}
-		for (final Document d : results.getResults()) {
-			final Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
-			a.set_id(d.getId());
-			a.set_rev(d.getRev());
-			a.setUser(me.getUsername());
-			a.setSessionId(s.get_id());
-			answers.add(a);
-		}
-		return answers;
-	}
-
-	@Override
-	public int getTotalAnswerCount(final String sessionKey) {
-		final Session s = getDatabaseDao().getSessionFromKeyword(sessionKey);
-		if (s == null) {
-			throw new NotFoundException();
-		}
-
-		final View view = new View("answer/by_sessionid_variant");
-		view.setKey(s.get_id());
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	@Override
-	public int getInterposedCount(final String sessionKey) {
-		final Session s = getDatabaseDao().getSessionFromKeyword(sessionKey);
-		if (s == null) {
-			throw new NotFoundException();
-		}
-
-		final View view = new View("comment/by_sessionid");
-		view.setKey(s.get_id());
-		view.setGroup(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.isEmpty() || results.getResults().isEmpty()) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	@Override
-	public InterposedReadingCount getInterposedReadingCount(final Session session) {
-		final View view = new View("comment/by_sessionid_read");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKeyArray(session.get_id(), "{}");
-		view.setGroup(true);
-		return getInterposedReadingCount(view);
-	}
-
-	@Override
-	public InterposedReadingCount getInterposedReadingCount(final Session session, final User user) {
-		final View view = new View("comment/by_sessionid_creator_read");
-		view.setStartKeyArray(session.get_id(), user.getUsername());
-		view.setEndKeyArray(session.get_id(), user.getUsername(), "{}");
-		view.setGroup(true);
-		return getInterposedReadingCount(view);
-	}
-
-	private InterposedReadingCount getInterposedReadingCount(final View view) {
-		final ViewResults results = getDatabase().view(view);
-		if (results.isEmpty() || results.getResults().isEmpty()) {
-			return new InterposedReadingCount();
-		}
-		// A complete result looks like this. Note that the second row is optional, and that the first one may be
-		// 'unread' or 'read', i.e., results may be switched around or only one result may be present.
-		// count = {"rows":[
-		// {"key":["cecebabb21b096e592d81f9c1322b877","Guestc9350cf4a3","read"],"value":1},
-		// {"key":["cecebabb21b096e592d81f9c1322b877","Guestc9350cf4a3","unread"],"value":1}
-		// ]}
-		int read = 0, unread = 0;
-		boolean isRead = false;
-		final JSONObject fst = results.getJSONArray("rows").getJSONObject(0);
-		final JSONObject snd = results.getJSONArray("rows").optJSONObject(1);
-
-		final JSONArray fstkey = fst.getJSONArray("key");
-		if (fstkey.size() == 2) {
-			isRead = fstkey.getBoolean(1);
-		} else if (fstkey.size() == 3) {
-			isRead = fstkey.getBoolean(2);
-		}
-		if (isRead) {
-			read = fst.optInt("value");
-		} else {
-			unread = fst.optInt("value");
-		}
-
-		if (snd != null) {
-			final JSONArray sndkey = snd.getJSONArray("key");
-			if (sndkey.size() == 2) {
-				isRead = sndkey.getBoolean(1);
-			} else {
-				isRead = sndkey.getBoolean(2);
-			}
-			if (isRead) {
-				read = snd.optInt("value");
-			} else {
-				unread = snd.optInt("value");
-			}
-		}
-		return new InterposedReadingCount(read, unread);
-	}
-
-	@Override
-	public List<InterposedQuestion> getInterposedQuestions(final Session session, final int start, final int limit) {
-		final View view = new View("comment/doc_by_sessionid_timestamp");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setDescending(true);
-		view.setStartKeyArray(session.get_id(), "{}");
-		view.setEndKeyArray(session.get_id());
-		final ViewResults questions = getDatabase().view(view);
-		if (questions == null || questions.isEmpty()) {
-			return null;
-		}
-		return createInterposedList(session, questions);
-	}
-
-	@Override
-	public List<InterposedQuestion> getInterposedQuestions(final Session session, final User user, final int start, final int limit) {
-		final View view = new View("comment/doc_by_sessionid_creator_timestamp");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setDescending(true);
-		view.setStartKeyArray(session.get_id(), user.getUsername(), "{}");
-		view.setEndKeyArray(session.get_id(), user.getUsername());
-		final ViewResults questions = getDatabase().view(view);
-		if (questions == null || questions.isEmpty()) {
-			return null;
-		}
-		return createInterposedList(session, questions);
-	}
-
-	private List<InterposedQuestion> createInterposedList(
-			final Session session, final ViewResults questions) {
-		final List<InterposedQuestion> result = new ArrayList<>();
-		for (final Document document : questions.getResults()) {
-			final InterposedQuestion question = (InterposedQuestion) JSONObject.toBean(
-					document.getJSONObject().getJSONObject("value"),
-					InterposedQuestion.class
-					);
-			question.setSessionId(session.getKeyword());
-			question.set_id(document.getId());
-			result.add(question);
-		}
-		return result;
-	}
-
-	@Cacheable("statistics")
-	@Override
-	public Statistics getStatistics() {
-		final Statistics stats = new Statistics();
-		try {
-			final View statsView = new View("statistics/statistics");
-			final View creatorView = new View("statistics/unique_session_creators");
-			final View studentUserView = new View("statistics/active_student_users");
-			statsView.setGroup(true);
-			creatorView.setGroup(true);
-			studentUserView.setGroup(true);
-
-			final ViewResults statsResults = getDatabase().view(statsView);
-			final ViewResults creatorResults = getDatabase().view(creatorView);
-			final ViewResults studentUserResults = getDatabase().view(studentUserView);
-
-			if (!isEmptyResults(statsResults)) {
-				final JSONArray rows = statsResults.getJSONArray("rows");
-				for (int i = 0; i < rows.size(); i++) {
-					final JSONObject row = rows.getJSONObject(i);
-					final int value = row.getInt("value");
-					switch (row.getString("key")) {
-					case "openSessions":
-						stats.setOpenSessions(stats.getOpenSessions() + value);
-						break;
-					case "closedSessions":
-						stats.setClosedSessions(stats.getClosedSessions() + value);
-						break;
-					case "deletedSessions":
-						/* Deleted sessions are not exposed separately for now. */
-						stats.setClosedSessions(stats.getClosedSessions() + value);
-						break;
-					case "answers":
-						stats.setAnswers(stats.getAnswers() + value);
-						break;
-					case "lectureQuestions":
-						stats.setLectureQuestions(stats.getLectureQuestions() + value);
-						break;
-					case "preparationQuestions":
-						stats.setPreparationQuestions(stats.getPreparationQuestions() + value);
-						break;
-					case "interposedQuestions":
-						stats.setInterposedQuestions(stats.getInterposedQuestions() + value);
-						break;
-					case "conceptQuestions":
-						stats.setConceptQuestions(stats.getConceptQuestions() + value);
-						break;
-					case "flashcards":
-						stats.setFlashcards(stats.getFlashcards() + value);
-						break;
-					}
-				}
-			}
-			if (!isEmptyResults(creatorResults)) {
-				final JSONArray rows = creatorResults.getJSONArray("rows");
-				Set<String> creators = new HashSet<>();
-				for (int i = 0; i < rows.size(); i++) {
-					final JSONObject row = rows.getJSONObject(i);
-					creators.add(row.getString("key"));
-				}
-				stats.setCreators(creators.size());
-			}
-			if (!isEmptyResults(studentUserResults)) {
-				final JSONArray rows = studentUserResults.getJSONArray("rows");
-				Set<String> students = new HashSet<>();
-				for (int i = 0; i < rows.size(); i++) {
-					final JSONObject row = rows.getJSONObject(i);
-					students.add(row.getString("key"));
-				}
-				stats.setActiveStudents(students.size());
-			}
-			return stats;
-		} catch (final Exception e) {
-			logger.error("Could not retrieve session count.", e);
-		}
-		return stats;
-	}
-
-	@Override
-	public InterposedQuestion getInterposedQuestion(final String questionId) {
-		try {
-			final Document document = getDatabase().getDocument(questionId);
-			final InterposedQuestion question = (InterposedQuestion) JSONObject.toBean(document.getJSONObject(),
-					InterposedQuestion.class);
-			question.setSessionId(getSessionKeyword(question.getSessionId()));
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not load interposed question {}.", questionId, e);
-		}
-		return null;
-	}
-
-	@Override
-	public void markInterposedQuestionAsRead(final InterposedQuestion question) {
-		try {
-			question.setRead(true);
-			final Document document = getDatabase().getDocument(question.get_id());
-			document.put("read", question.isRead());
-			getDatabase().saveDocument(document);
-		} catch (final IOException e) {
-			logger.error("Could not mark interposed question as read {}.", question.get_id(), e);
-		}
-	}
-
-	@Override
-	public List<Session> getMyVisitedSessions(final User user, final int start, final int limit) {
-		final View view = new View("logged_in/visited_sessions_by_user");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setKey(user.getUsername());
-		final ViewResults sessions = getDatabase().view(view);
-		final List<Session> allSessions = new ArrayList<>();
-		for (final Document d : sessions.getResults()) {
-			// Not all users have visited sessions
-			if (d.getJSONObject().optJSONArray("value") != null) {
-				@SuppressWarnings("unchecked")
-				final Collection<Session> visitedSessions =	 JSONArray.toCollection(
-					d.getJSONObject().getJSONArray("value"),
-					Session.class
-				);
-				allSessions.addAll(visitedSessions);
-			}
-		}
-		// Filter sessions that don't exist anymore, also filter my own sessions
-		final List<Session> result = new ArrayList<>();
-		final List<Session> filteredSessions = new ArrayList<>();
-		for (final Session s : allSessions) {
-			try {
-				final Session session = getDatabaseDao().getSessionFromKeyword(s.getKeyword());
-				if (session != null && !session.isCreator(user)) {
-					result.add(session);
-				} else {
-					filteredSessions.add(s);
-				}
-			} catch (final NotFoundException e) {
-				filteredSessions.add(s);
-			}
-		}
-		if (filteredSessions.isEmpty()) {
-			return result;
-		}
-		// Update document to remove sessions that don't exist anymore
-		try {
-			List<VisitedSession> visitedSessions = new ArrayList<>();
-			for (final Session s : result) {
-				visitedSessions.add(new VisitedSession(s));
-			}
-			final LoggedIn loggedIn = new LoggedIn();
-			final Document loggedInDocument = getDatabase().getDocument(sessions.getResults().get(0).getString("id"));
-			loggedIn.setSessionId(loggedInDocument.getString("sessionId"));
-			loggedIn.setUser(user.getUsername());
-			loggedIn.setTimestamp(loggedInDocument.getLong("timestamp"));
-			loggedIn.setType(loggedInDocument.getString("type"));
-			loggedIn.setVisitedSessions(visitedSessions);
-			loggedIn.set_id(loggedInDocument.getId());
-			loggedIn.set_rev(loggedInDocument.getRev());
-
-			final JSONObject json = JSONObject.fromObject(loggedIn);
-			final Document doc = new Document(json);
-			getDatabase().saveDocument(doc);
-		} catch (IOException e) {
-			logger.error("Could not clean up logged_in document of {}.", user.getUsername(), e);
-		}
-		return result;
-	}
-
-	@Override
-	public List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit) {
-		final View view = new View("logged_in/visited_sessions_by_user");
-		if (start > 0) {
-			view.setSkip(start);
-		}
-		if (limit > 0) {
-			view.setLimit(limit);
-		}
-		view.setKey(username);
-		final ViewResults sessions = getDatabase().view(view);
-		final List<Session> allSessions = new ArrayList<>();
-		for (final Document d : sessions.getResults()) {
-			// Not all users have visited sessions
-			if (d.getJSONObject().optJSONArray("value") != null) {
-				@SuppressWarnings("unchecked")
-				final Collection<Session> visitedSessions =	 JSONArray.toCollection(
-						d.getJSONObject().getJSONArray("value"),
-						Session.class
-				);
-				allSessions.addAll(visitedSessions);
-			}
-		}
-		// Filter sessions that don't exist anymore, also filter my own sessions
-		final List<Session> result = new ArrayList<>();
-		final List<Session> filteredSessions = new ArrayList<>();
-		for (final Session s : allSessions) {
-			try {
-				final Session session = getDatabaseDao().getSessionFromKeyword(s.getKeyword());
-				if (session != null && !(session.getCreator().equals(username))) {
-					result.add(session);
-				} else {
-					filteredSessions.add(s);
-				}
-			} catch (final NotFoundException e) {
-				filteredSessions.add(s);
-			}
-		}
-		if (filteredSessions.isEmpty()) {
-			return result;
-		}
-		// Update document to remove sessions that don't exist anymore
-		try {
-			List<VisitedSession> visitedSessions = new ArrayList<>();
-			for (final Session s : result) {
-				visitedSessions.add(new VisitedSession(s));
-			}
-			final LoggedIn loggedIn = new LoggedIn();
-			final Document loggedInDocument = getDatabase().getDocument(sessions.getResults().get(0).getString("id"));
-			loggedIn.setSessionId(loggedInDocument.getString("sessionId"));
-			loggedIn.setUser(username);
-			loggedIn.setTimestamp(loggedInDocument.getLong("timestamp"));
-			loggedIn.setType(loggedInDocument.getString("type"));
-			loggedIn.setVisitedSessions(visitedSessions);
-			loggedIn.set_id(loggedInDocument.getId());
-			loggedIn.set_rev(loggedInDocument.getRev());
-
-			final JSONObject json = JSONObject.fromObject(loggedIn);
-			final Document doc = new Document(json);
-			getDatabase().saveDocument(doc);
-		} catch (IOException e) {
-			logger.error("Could not clean up logged_in document of {}.", username, e);
-		}
-		return result;
-	}
-
-	@Override
-	public List<SessionInfo> getMyVisitedSessionsInfo(final User user, final int start, final int limit) {
-		List<Session> sessions = this.getMyVisitedSessions(user, start, limit);
-		if (sessions.isEmpty()) {
-			return new ArrayList<>();
-		}
-		return this.getInfosForVisitedSessions(sessions, user);
-	}
-
-	@CacheEvict(value = "answers", key = "#question")
-	@Override
-	public Answer saveAnswer(final Answer answer, final User user, final Question question, final Session session) {
-		final Document a = new Document();
-		a.put("type", "skill_question_answer");
-		a.put("sessionId", answer.getSessionId());
-		a.put("questionId", answer.getQuestionId());
-		a.put("answerSubject", answer.getAnswerSubject());
-		a.put("questionVariant", answer.getQuestionVariant());
-		a.put("questionValue", answer.getQuestionValue());
-		a.put("answerText", answer.getAnswerText());
-		a.put("answerTextRaw", answer.getAnswerTextRaw());
-		a.put("successfulFreeTextAnswer", answer.isSuccessfulFreeTextAnswer());
-		a.put("timestamp", answer.getTimestamp());
-		a.put("user", user.getUsername());
-		a.put("piRound", answer.getPiRound());
-		a.put("abstention", answer.isAbstention());
-		a.put("answerImage", answer.getAnswerImage());
-		a.put("answerThumbnailImage", answer.getAnswerThumbnailImage());
-		AnswerQueueElement answerQueueElement = new AnswerQueueElement(session, question, answer, user);
-		this.answerQueue.offer(new AbstractMap.SimpleEntry<>(a, answerQueueElement));
-		return answer;
-	}
-
-	@Scheduled(fixedDelay = 5000)
-	public void flushAnswerQueue() {
-		final Map<Document, Answer> map = new HashMap<>();
-		final List<Document> answerList = new ArrayList<>();
-		final List<AnswerQueueElement> elements = new ArrayList<>();
-		AbstractMap.SimpleEntry<Document, AnswerQueueElement> entry;
-		while ((entry = this.answerQueue.poll()) != null) {
-			final Document doc = entry.getKey();
-			final Answer answer = entry.getValue().getAnswer();
-			map.put(doc, answer);
-			answerList.add(doc);
-			elements.add(entry.getValue());
-		}
-		if (answerList.isEmpty()) {
-			// no need to send an empty bulk request. ;-)
-			return;
-		}
-		try {
-			getDatabase().bulkSaveDocuments(answerList.toArray(new Document[answerList.size()]));
-			for (Document d : answerList) {
-				final Answer answer = map.get(d);
-				answer.set_id(d.getId());
-				answer.set_rev(d.getRev());
-			}
-			// Send NewAnswerEvents ...
-			for (AnswerQueueElement e : elements) {
-				this.publisher.publishEvent(new NewAnswerEvent(this, e.getSession(), e.getAnswer(), e.getUser(), e.getQuestion()));
-			}
-		} catch (IOException e) {
-			logger.error("Could not bulk save answers from queue.", e);
-		}
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@CacheEvict(value = "answers", allEntries = true)
-	@Override
-	public Answer updateAnswer(final Answer answer) {
-		try {
-			final Document a = database.getDocument(answer.get_id());
-			a.put("answerSubject", answer.getAnswerSubject());
-			a.put("answerText", answer.getAnswerText());
-			a.put("answerTextRaw", answer.getAnswerTextRaw());
-			a.put("successfulFreeTextAnswer", answer.isSuccessfulFreeTextAnswer());
-			a.put("timestamp", answer.getTimestamp());
-			a.put("abstention", answer.isAbstention());
-			a.put("questionValue", answer.getQuestionValue());
-			a.put("answerImage", answer.getAnswerImage());
-			a.put("answerThumbnailImage", answer.getAnswerThumbnailImage());
-			a.put("read", answer.isRead());
-			database.saveDocument(a);
-			answer.set_rev(a.getRev());
-			return answer;
-		} catch (final IOException e) {
-			logger.error("Could not update answer {}.", answer, e);
-		}
-		return null;
-	}
-
-	/* TODO: Only evict cache entry for the answer's session. This requires some refactoring. */
-	@CacheEvict(value = "answers", allEntries = true)
-	@Override
-	public void deleteAnswer(final String answerId) {
-		try {
-			database.deleteDocument(database.getDocument(answerId));
-			log("delete", "type", "answer");
-		} catch (final IOException e) {
-			logger.error("Could not delete answer {}.", answerId, e);
-		}
-	}
-
-	@Override
-	public void deleteInterposedQuestion(final InterposedQuestion question) {
-		try {
-			deleteDocument(question.get_id());
-			log("delete", "type", "comment");
-		} catch (final IOException e) {
-			logger.error("Could not delete interposed question {}.", question.get_id(), e);
-		}
-	}
-
-	@Override
-	public List<Session> getCourseSessions(final List<Course> courses) {
-		final ExtendedView view = new ExtendedView("session/by_courseid");
-		view.setIncludeDocs(true);
-		view.setCourseIdKeys(courses);
-
-		final ViewResults sessions = getDatabase().view(view);
-
-		final List<Session> result = new ArrayList<>();
-		for (final Document d : sessions.getResults()) {
-			final Session session = (Session) JSONObject.toBean(
-					d.getJSONObject().getJSONObject("doc"),
-					Session.class
-					);
-			result.add(session);
-		}
-		return result;
-	}
-
-	/**
-	 * Adds convenience methods to CouchDB4J's view class.
-	 */
-	private static class ExtendedView extends View {
-
-		ExtendedView(final String fullname) {
-			super(fullname);
-		}
-
-		void setCourseIdKeys(final List<Course> courses) {
-			List<String> courseIds = new ArrayList<>();
-			for (Course c : courses) {
-				courseIds.add(c.getId());
-			}
-			setKeys(courseIds);
-		}
-
-		void setSessionIdKeys(final List<Session> sessions) {
-			List<String> sessionIds = new ArrayList<>();
-			for (Session s : sessions) {
-				sessionIds.add(s.get_id());
-			}
-			setKeys(sessionIds);
-		}
-	}
-
-	@Override
-	@CachePut(value = "sessions")
-	public Session updateSession(final Session session) {
-		try {
-			final Document s = database.getDocument(session.get_id());
-			s.put("name", session.getName());
-			s.put("shortName", session.getShortName());
-			s.put("active", session.isActive());
-			s.put("ppAuthorName", session.getPpAuthorName());
-			s.put("ppAuthorMail", session.getPpAuthorMail());
-			s.put("ppUniversity", session.getPpUniversity());
-			s.put("ppLogo", session.getPpLogo());
-			s.put("ppSubject", session.getPpSubject());
-			s.put("ppLicense", session.getPpLicense());
-			s.put("ppDescription", session.getPpDescription());
-			s.put("ppFaculty", session.getPpFaculty());
-			s.put("ppLevel", session.getPpLevel());
-			s.put("learningProgressOptions", JSONObject.fromObject(session.getLearningProgressOptions()));
-			s.put("features", JSONObject.fromObject(session.getFeatures()));
-			s.put("feedbackLock", session.getFeedbackLock());
-			database.saveDocument(s);
-			session.set_rev(s.getRev());
-
-			return session;
-		} catch (final IOException e) {
-			logger.error("Could not update session {}.", session, e);
-		}
-
-		return null;
-	}
-
-	@Override
-	@Caching(evict = { @CacheEvict("sessions"), @CacheEvict(cacheNames = "sessions", key = "#p0.keyword") })
-	public Session changeSessionCreator(Session session, final String newCreator) {
-		try {
-			final Document s = database.getDocument(session.get_id());
-			s.put("creator", newCreator);
-			database.saveDocument(s);
-			session.set_rev(s.getRev());
-		} catch (final IOException e) {
-			logger.error("Could not update creator for session {}.", session, e);
-		}
-
-		return session;
-	}
-
-	@Override
-	@Caching(evict = { @CacheEvict("sessions"), @CacheEvict(cacheNames = "sessions", key = "#p0.keyword") })
-	public int[] deleteSession(final Session session) {
-		int[] count = new int[] {0, 0};
-		try {
-			count = deleteAllQuestionsWithAnswers(session);
-			deleteDocument(session.get_id());
-			logger.debug("Deleted session document {} and related data.", session.get_id());
-			log("delete", "type", "session", "id", session.get_id());
-		} catch (final IOException e) {
-			logger.error("Could not delete session {}.", session, e);
-		}
-
-		return count;
-	}
-
-	@Override
-	public int[] deleteInactiveGuestSessions(long lastActivityBefore) {
-		View view = new View("session/by_lastactivity_for_guests");
-		view.setEndKey(lastActivityBefore);
-		final List<Document> results = this.getDatabase().view(view).getResults();
-		int[] count = new int[3];
-
-		for (Document oldDoc : results) {
-			Session s = new Session();
-			s.set_id(oldDoc.getId());
-			s.set_rev(oldDoc.getJSONObject("value").getString("_rev"));
-			int[] qaCount = deleteSession(s);
-			count[1] += qaCount[0];
-			count[2] += qaCount[1];
-		}
-
-		if (!results.isEmpty()) {
-			logger.info("Deleted {} inactive guest sessions.", results.size());
-			log("cleanup", "type", "session", "sessionCount", results.size(), "questionCount", count[1], "answerCount", count[2]);
-		}
-		count[0] = results.size();
-
-		return count;
-	}
-
-	@Override
-	public int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore) {
-		try {
-			View view = new View("logged_in/by_last_activity_for_guests");
-			view.setEndKey(lastActivityBefore);
-			List<Document> results = this.getDatabase().view(view).getResults();
-
-			int count = 0;
-			List<List<Document>> partitions = Lists.partition(results, BULK_PARTITION_SIZE);
-			for (List<Document> partition: partitions) {
-				final List<Document> newDocs = new ArrayList<>();
-				for (final Document oldDoc : partition) {
-					final Document newDoc = new Document();
-					newDoc.setId(oldDoc.getId());
-					newDoc.setRev(oldDoc.getJSONObject("value").getString("_rev"));
-					newDoc.put("_deleted", true);
-					newDocs.add(newDoc);
-					logger.debug("Marked logged_in document {} for deletion.", oldDoc.getId());
-					/* Use log type 'user' since effectively the user is deleted in case of guests */
-					log("delete", "type", "user", "id", oldDoc.getId());
-				}
-
-				if (!newDocs.isEmpty()) {
-					if (getDatabase().bulkSaveDocuments(newDocs.toArray(new Document[newDocs.size()]))) {
-						count += newDocs.size();
-					} else {
-						logger.error("Could not bulk delete visited session lists.");
-					}
-				}
-			}
-
-			if (count > 0) {
-				logger.info("Deleted {} visited session lists of inactive users.", count);
-				log("cleanup", "type", "visitedsessions", "count", count);
-			}
-
-			return count;
-		} catch (IOException e) {
-			logger.error("Could not delete visited session lists of inactive users.", e);
-		}
-
-		return 0;
-	}
-
-	@Cacheable("lecturequestions")
-	@Override
-	public List<Question> getLectureQuestionsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "lecture", true);
-		view.setEndKeyArray(session.get_id(), "lecture", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getLectureQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "lecture");
-		view.setEndKeyArray(session.get_id(), "lecture", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Cacheable("flashcardquestions")
-	@Override
-	public List<Question> getFlashcardsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "flashcard", true);
-		view.setEndKeyArray(session.get_id(), "flashcard", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getFlashcardsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "flashcard");
-		view.setEndKeyArray(session.get_id(), "flashcard", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Cacheable("preparationquestions")
-	@Override
-	public List<Question> getPreparationQuestionsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "preparation", true);
-		view.setEndKeyArray(session.get_id(), "preparation", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "preparation");
-		view.setEndKeyArray(session.get_id(), "preparation", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getAllSkillQuestions(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKeyArray(session.get_id(), "{}");
-
-		return getQuestions(view, session);
-	}
-
-	private List<Question> getQuestions(final View view, final Session session) {
-		final ViewResults viewResults = getDatabase().view(view);
-		if (viewResults == null || viewResults.isEmpty()) {
-			return null;
-		}
-
-		final List<Question> questions = new ArrayList<>();
-
-		Results<Question> results = getDatabase().queryView(view, Question.class);
-		for (final RowResult<Question> row : results.getRows()) {
-			Question question = row.getValue();
-			question.updateRoundManagementState();
-			question.setSessionKeyword(session.getKeyword());
-			if (!"freetext".equals(question.getQuestionType()) && 0 == question.getPiRound()) {
-				/* needed for legacy questions whose piRound property has not been set */
-				question.setPiRound(1);
-			}
-
-			questions.add(question);
-		}
-		return questions;
-	}
-
-	@Override
-	public int getLectureQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "lecture");
-		view.setEndKeyArray(session.get_id(), "lecture", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	@Override
-	public int getFlashcardCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "flashcard");
-		view.setEndKeyArray(session.get_id(), "flashcard", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	@Override
-	public int getPreparationQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "preparation");
-		view.setEndKeyArray(session.get_id(), "preparation", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	private int getQuestionCount(final View view) {
-		view.setReduce(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	@Override
-	public int countLectureQuestionAnswers(final Session session) {
-		return countQuestionVariantAnswers(session, "lecture");
-	}
-
-	@Override
-	public int countPreparationQuestionAnswers(final Session session) {
-		return countQuestionVariantAnswers(session, "preparation");
-	}
-
-	private int countQuestionVariantAnswers(final Session session, final String variant) {
-		final View view = new View("answer/by_sessionid_variant");
-		view.setKey(session.get_id(), variant);
-		view.setReduce(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("lecturequestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllLectureQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "lecture");
-		view.setEndKey(session.get_id(), "lecture", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("flashcardquestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllFlashcardsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "flashcard");
-		view.setEndKey(session.get_id(), "flashcard", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("preparationquestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllPreparationQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "preparation");
-		view.setEndKey(session.get_id(), "preparation", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	@Override
-	public List<String> getUnAnsweredLectureQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_piround_by_user_sessionid_variant");
-		view.setKey(user.getUsername(), session.get_id(), "lecture");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getLectureQuestionsForUsers(session), view);
-	}
-
-	@Override
-	public List<String> getUnAnsweredPreparationQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_piround_by_user_sessionid_variant");
-		view.setKey(user.getUsername(), session.get_id(), "preparation");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getPreparationQuestionsForUsers(session), view);
-	}
-
-	private List<String> collectUnansweredQuestionIds(
-			final List<String> questions,
-			final View view
-			) {
-		final ViewResults answeredQuestions = getDatabase().view(view);
-
-		final List<String> answered = new ArrayList<>();
-		for (final Document d : answeredQuestions.getResults()) {
-			answered.add(d.getString("value"));
-		}
-
-		final List<String> unanswered = new ArrayList<>();
-		for (final String questionId : questions) {
-			if (!answered.contains(questionId)) {
-				unanswered.add(questionId);
-			}
-		}
-		return unanswered;
-	}
-
-	private List<String> collectUnansweredQuestionIdsByPiRound(
-			final List<Question> questions,
-			final View view
-			) {
-		final ViewResults answeredQuestions = getDatabase().view(view);
-
-		final Map<String, Integer> answered = new HashMap<>();
-		for (final Document d : answeredQuestions.getResults()) {
-			answered.put(d.getJSONArray("value").getString(0), d.getJSONArray("value").getInt(1));
-		}
-
-		final List<String> unanswered = new ArrayList<>();
-
-		for (final Question question : questions) {
-			if (!"slide".equals(question.getQuestionType()) && (!answered.containsKey(question.get_id())
-					|| (answered.containsKey(question.get_id()) && answered.get(question.get_id()) != question.getPiRound()))) {
-				unanswered.add(question.get_id());
-			}
-		}
-
-		return unanswered;
-	}
-
-	private List<String> collectQuestionIds(final View view) {
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return new ArrayList<>();
-		}
-		final List<String> ids = new ArrayList<>();
-		for (final Document d : results.getResults()) {
-			ids.add(d.getId());
-		}
-		return ids;
-	}
-
-	@Override
-	public int deleteAllInterposedQuestions(final Session session) {
-		final View view = new View("comment/by_sessionid");
-		view.setKey(session.get_id());
-		final ViewResults questions = getDatabase().view(view);
-
-		return deleteAllInterposedQuestions(session, questions);
-	}
-
-	@Override
-	public int deleteAllInterposedQuestions(final Session session, final User user) {
-		final View view = new View("comment/by_sessionid_creator_read");
-		view.setStartKeyArray(session.get_id(), user.getUsername());
-		view.setEndKeyArray(session.get_id(), user.getUsername(), "{}");
-		final ViewResults questions = getDatabase().view(view);
-
-		return deleteAllInterposedQuestions(session, questions);
-	}
-
-	private int deleteAllInterposedQuestions(final Session session, final ViewResults questions) {
-		if (questions == null || questions.isEmpty()) {
-			return 0;
-		}
-		List<Document> results = questions.getResults();
-		/* TODO: use bulk delete */
-		for (final Document document : results) {
-			try {
-				deleteDocument(document.getId());
-			} catch (final IOException e) {
-				logger.error("Could not delete all interposed questions {}.", session, e);
-			}
-		}
-
-		/* This does account for failed deletions */
-		log("delete", "type", "comment", "commentCount", results.size());
-
-		return results.size();
-	}
-
-	@Override
-	public List<Question> publishAllQuestions(final Session session, final boolean publish) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKeyArray(session.get_id(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().publishQuestions(session, publish, questions);
-
-		return questions;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void publishQuestions(final Session session, final boolean publish, List<Question> questions) {
-		for (final Question q : questions) {
-			q.setActive(publish);
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk publish all questions.", e);
-		}
-	}
-
-	@Override
-	public List<Question> setVotingAdmissionForAllQuestions(final Session session, final boolean disableVoting) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKeyArray(session.get_id(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().setVotingAdmissions(session, disableVoting, questions);
-
-		return questions;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void setVotingAdmissions(final Session session, final boolean disableVoting, List<Question> questions) {
-		for (final Question q : questions) {
-			if (!"flashcard".equals(q.getQuestionType())) {
-				q.setVotingDisabled(disableVoting);
-			}
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk set voting admission for all questions.", e);
-		}
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@CacheEvict(value = "answers", allEntries = true)
-	@Override
-	public int deleteAllQuestionsAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id());
-		view.setEndKeyArray(session.get_id(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
-
-		return deleteAllAnswersForQuestions(questions);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@CacheEvict(value = "answers", allEntries = true)
-	@Override
-	public int deleteAllPreparationAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "preparation");
-		view.setEndKeyArray(session.get_id(), "preparation", "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
-
-		return deleteAllAnswersForQuestions(questions);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@CacheEvict(value = "answers", allEntries = true)
-	@Override
-	public int deleteAllLectureAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), "lecture");
-		view.setEndKeyArray(session.get_id(), "lecture", "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
-
-		return deleteAllAnswersForQuestions(questions);
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void resetQuestionsRoundState(final Session session, List<Question> questions) {
-		for (final Question q : questions) {
-			q.resetQuestionState();
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk reset all questions round state.", e);
-		}
-	}
-
-	private int deleteAllAnswersForQuestions(List<Question> questions) {
-		List<String> questionIds = new ArrayList<>();
-		for (Question q : questions) {
-			questionIds.add(q.get_id());
-		}
-		final View bulkView = new View("answer/by_questionid");
-		bulkView.setKeys(questionIds);
-		bulkView.setIncludeDocs(true);
-		final List<Document> result = getDatabase().view(bulkView).getResults();
-		final List<Document> allAnswers = new ArrayList<>();
-		for (Document a : result) {
-			final Document d = new Document(a.getJSONObject("doc"));
-			d.put("_deleted", true);
-			allAnswers.add(d);
-		}
-		try {
-			getDatabase().bulkSaveDocuments(allAnswers.toArray(new Document[allAnswers.size()]));
-
-			return allAnswers.size();
-		} catch (IOException e) {
-			logger.error("Could not bulk delete answers.", e);
-		}
-
-		return 0;
-	}
-
-	private int[] deleteAllAnswersWithQuestions(List<Question> questions) {
-		List<String> questionIds = new ArrayList<>();
-		final List<Document> allQuestions = new ArrayList<>();
-		for (Question q : questions) {
-			final Document d = new Document();
-			d.put("_id", q.get_id());
-			d.put("_rev", q.get_rev());
-			d.put("_deleted", true);
-			questionIds.add(q.get_id());
-			allQuestions.add(d);
-		}
-		final View bulkView = new View("answer/by_questionid");
-		bulkView.setKeys(questionIds);
-		bulkView.setIncludeDocs(true);
-		final List<Document> result = getDatabase().view(bulkView).getResults();
-
-		final List<Document> allAnswers = new ArrayList<>();
-		for (Document a : result) {
-			final Document d = new Document(a.getJSONObject("doc"));
-			d.put("_deleted", true);
-			allAnswers.add(d);
-		}
-
-		try {
-			List<Document> deleteList = new ArrayList<>(allAnswers);
-			deleteList.addAll(allQuestions);
-			getDatabase().bulkSaveDocuments(deleteList.toArray(new Document[deleteList.size()]));
-
-			return new int[] {deleteList.size(), result.size()};
-		} catch (IOException e) {
-			logger.error("Could not bulk delete questions and answers.", e);
-		}
-
-		return new int[] {0, 0};
-	}
-
-	@Cacheable("learningprogress")
-	@Override
-	public CourseScore getLearningProgress(final Session session) {
-		final View maximumValueView = new View("learning_progress/maximum_value_of_question");
-		final View answerSumView = new View("learning_progress/question_value_achieved_for_user");
-		maximumValueView.setStartKeyArray(session.get_id());
-		maximumValueView.setEndKeyArray(session.get_id(), "{}");
-		answerSumView.setStartKeyArray(session.get_id());
-		answerSumView.setEndKeyArray(session.get_id(), "{}");
-
-		final List<Document> maximumValueResult = getDatabase().view(maximumValueView).getResults();
-		final List<Document> answerSumResult = getDatabase().view(answerSumView).getResults();
-
-		CourseScore courseScore = new CourseScore();
-
-		// no results found
-		if (maximumValueResult.isEmpty() && answerSumResult.isEmpty()) {
-			return courseScore;
-		}
-
-		// collect mapping (questionId -> max value)
-		for (Document d : maximumValueResult) {
-			String questionId = d.getJSONArray("key").getString(1);
-			JSONObject value = d.getJSONObject("value");
-			int questionScore = value.getInt("value");
-			String questionVariant = value.getString("questionVariant");
-			int piRound = value.getInt("piRound");
-			courseScore.addQuestion(questionId, questionVariant, piRound, questionScore);
-		}
-		// collect mapping (questionId -> (user -> value))
-		for (Document d : answerSumResult) {
-			String username = d.getJSONArray("key").getString(1);
-			JSONObject value = d.getJSONObject("value");
-			String questionId = value.getString("questionId");
-			int userscore = value.getInt("score");
-			int piRound = value.getInt("piRound");
-			courseScore.addAnswer(questionId, piRound, username, userscore);
-		}
-		return courseScore;
-	}
-
-	@Override
-	public DbUser createOrUpdateUser(DbUser user) {
-		try {
-			String id = user.getId();
-			String rev = user.getRev();
-			Document d = new Document();
-
-			if (null != id) {
-				d = database.getDocument(id, rev);
-			}
-
-			d.put("type", "userdetails");
-			d.put("username", user.getUsername());
-			d.put("password", user.getPassword());
-			d.put("activationKey", user.getActivationKey());
-			d.put("passwordResetKey", user.getPasswordResetKey());
-			d.put("passwordResetTime", user.getPasswordResetTime());
-			d.put("creation", user.getCreation());
-			d.put("lastLogin", user.getLastLogin());
-
-			database.saveDocument(d, id);
-			user.setId(d.getId());
-			user.setRev(d.getRev());
-
-			return user;
-		} catch (IOException e) {
-			logger.error("Could not save user {}.", user, e);
-		}
-
-		return null;
-	}
-
-	@Override
-	public DbUser getUser(String username) {
-		View view = new View("user/doc_by_username");
-		view.setKey(username);
-		ViewResults results = this.getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-
-		return (DbUser) JSONObject.toBean(
-			results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
-			DbUser.class
-		);
-	}
-
-	@Override
-	public boolean deleteUser(final DbUser dbUser) {
-		try {
-			this.deleteDocument(dbUser.getId());
-			log("delete", "type", "user", "id", dbUser.getId());
-
-			return true;
-		} catch (IOException e) {
-			logger.error("Could not delete user {}.", dbUser.getId(), e);
-		}
-
-		return false;
-	}
-
-	@Override
-	public int deleteInactiveUsers(long lastActivityBefore) {
-		try {
-			View view = new View("user/by_creation_for_inactive");
-			view.setEndKey(lastActivityBefore);
-			List<Document> results = this.getDatabase().view(view).getResults();
-
-			int count = 0;
-			final List<List<Document>> partitions = Lists.partition(results, BULK_PARTITION_SIZE);
-			for (List<Document> partition: partitions) {
-				final List<Document> newDocs = new ArrayList<>();
-				for (Document oldDoc : partition) {
-					final Document newDoc = new Document();
-					newDoc.setId(oldDoc.getId());
-					newDoc.setRev(oldDoc.getJSONObject("value").getString("_rev"));
-					newDoc.put("_deleted", true);
-					newDocs.add(newDoc);
-					logger.debug("Marked user document {} for deletion.", oldDoc.getId());
-				}
-
-				if (newDocs.size() > 0) {
-					if (getDatabase().bulkSaveDocuments(newDocs.toArray(new Document[newDocs.size()]))) {
-						count += newDocs.size();
-					}
-				}
-			}
-
-			if (count > 0) {
-				logger.info("Deleted {} inactive users.", count);
-				log("cleanup", "type", "user", "count", count);
-			}
-
-			return count;
-		} catch (IOException e) {
-			logger.error("Could not delete inactive users.", e);
-		}
-
-		return 0;
-	}
-
-	@Override
-	public SessionInfo importSession(User user, ImportExportSession importSession) {
-		final Session session = this.saveSession(user, importSession.generateSessionEntity(user));
-		List<Document> questions = new ArrayList<>();
-		// We need to remember which answers belong to which question.
-		// The answers need a questionId, so we first store the questions to get the IDs.
-		// Then we update the answer objects and store them as well.
-		Map<Document, ImportExportQuestion> mapping = new HashMap<>();
-		// Later, generate all answer documents
-		List<Document> answers = new ArrayList<>();
-		// We can then push answers together with interposed questions in one large bulk request
-		List<Document> interposedQuestions = new ArrayList<>();
-		// Motds shouldn't be forgotten, too
-		List<Document> motds = new ArrayList<>();
-		try {
-			// add session id to all questions and generate documents
-			for (ImportExportQuestion question : importSession.getQuestions()) {
-				Document doc = toQuestionDocument(session, question);
-				question.setSessionId(session.get_id());
-				questions.add(doc);
-				mapping.put(doc, question);
-			}
-			database.bulkSaveDocuments(questions.toArray(new Document[questions.size()]));
-
-			// bulk import answers together with interposed questions
-			for (Entry<Document, ImportExportQuestion> entry : mapping.entrySet()) {
-				final Document doc = entry.getKey();
-				final ImportExportQuestion question = entry.getValue();
-				question.set_id(doc.getId());
-				question.set_rev(doc.getRev());
-				for (de.thm.arsnova.entities.transport.Answer answer : question.getAnswers()) {
-					final Answer a = answer.generateAnswerEntity(user, question);
-					final Document answerDoc = new Document();
-					answerDoc.put("type", "skill_question_answer");
-					answerDoc.put("sessionId", a.getSessionId());
-					answerDoc.put("questionId", a.getQuestionId());
-					answerDoc.put("answerSubject", a.getAnswerSubject());
-					answerDoc.put("questionVariant", a.getQuestionVariant());
-					answerDoc.put("questionValue", a.getQuestionValue());
-					answerDoc.put("answerText", a.getAnswerText());
-					answerDoc.put("answerTextRaw", a.getAnswerTextRaw());
-					answerDoc.put("timestamp", a.getTimestamp());
-					answerDoc.put("piRound", a.getPiRound());
-					answerDoc.put("abstention", a.isAbstention());
-					answerDoc.put("successfulFreeTextAnswer", a.isSuccessfulFreeTextAnswer());
-					// we do not store the user's name
-					answerDoc.put("user", "");
-					answers.add(answerDoc);
-				}
-			}
-			for (de.thm.arsnova.entities.transport.InterposedQuestion i : importSession.getFeedbackQuestions()) {
-				final Document q = new Document();
-				q.put("type", "interposed_question");
-				q.put("sessionId", session.get_id());
-				q.put("subject", i.getSubject());
-				q.put("text", i.getText());
-				q.put("timestamp", i.getTimestamp());
-				q.put("read", i.isRead());
-				// we do not store the creator's name
-				q.put("creator", "");
-				interposedQuestions.add(q);
-			}
-			for (Motd m : importSession.getMotds()) {
-				final Document d = new Document();
-				d.put("type", "motd");
-				d.put("motdkey", m.getMotdkey());
-				d.put("title", m.getTitle());
-				d.put("text", m.getText());
-				d.put("audience", m.getAudience());
-				d.put("sessionkey", session.getKeyword());
-				d.put("startdate", String.valueOf(m.getStartdate().getTime()));
-				d.put("enddate", String.valueOf(m.getEnddate().getTime()));
-				motds.add(d);
-			}
-			List<Document> documents = new ArrayList<>(answers);
-			database.bulkSaveDocuments(interposedQuestions.toArray(new Document[interposedQuestions.size()]));
-			database.bulkSaveDocuments(motds.toArray(new Document[motds.size()]));
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (IOException e) {
-			logger.error("Could not import session.", e);
-			// Something went wrong, delete this session since we do not want a partial import
-			this.deleteSession(session);
-			return null;
-		}
-		return this.calculateSessionInfo(importSession, session);
-	}
-
-	@Override
-	public ImportExportSession exportSession(String sessionkey, Boolean withAnswers, Boolean withFeedbackQuestions) {
-		ImportExportSession importExportSession = new ImportExportSession();
-		Session session = getDatabaseDao().getSessionFromKeyword(sessionkey);
-		importExportSession.setSessionFromSessionObject(session);
-		List<Question> questionList = getDatabaseDao().getAllSkillQuestions(session);
-		for (Question question : questionList) {
-			List<de.thm.arsnova.entities.transport.Answer> answerList = new ArrayList<>();
-			if (withAnswers) {
-				for (Answer a : this.getDatabaseDao().getAllAnswers(question)) {
-					de.thm.arsnova.entities.transport.Answer transportAnswer = new de.thm.arsnova.entities.transport.Answer(a);
-					answerList.add(transportAnswer);
-				}
-				// getAllAnswers does not grep for whole answer object so i need to add empty entries for abstentions
-				int i = this.getDatabaseDao().getAbstentionAnswerCount(question.get_id());
-				for (int b = 0; b < i; b++) {
-					de.thm.arsnova.entities.transport.Answer ans = new de.thm.arsnova.entities.transport.Answer();
-					ans.setAnswerSubject("");
-					ans.setAnswerImage("");
-					ans.setAnswerText("");
-					ans.setAbstention(true);
-					answerList.add(ans);
-				}
-			}
-			importExportSession.addQuestionWithAnswers(question, answerList);
-		}
-		if (withFeedbackQuestions) {
-			List<de.thm.arsnova.entities.transport.InterposedQuestion> interposedQuestionList = new ArrayList<>();
-			for (InterposedQuestion i : getDatabaseDao().getInterposedQuestions(session, 0, 0)) {
-				de.thm.arsnova.entities.transport.InterposedQuestion transportInterposedQuestion = new de.thm.arsnova.entities.transport.InterposedQuestion(i);
-				interposedQuestionList.add(transportInterposedQuestion);
-			}
-			importExportSession.setFeedbackQuestions(interposedQuestionList);
-		}
-		if (withAnswers) {
-			importExportSession.setSessionInfo(this.calculateSessionInfo(importExportSession, session));
-		}
-		importExportSession.setMotds(getDatabaseDao().getMotdsForSession(session.getKeyword()));
-		return importExportSession;
-	}
-
-	private SessionInfo calculateSessionInfo(ImportExportSession importExportSession, Session session) {
-		int unreadInterposed = 0;
-		int numUnanswered = 0;
-		int numAnswers = 0;
-		for (de.thm.arsnova.entities.transport.InterposedQuestion i : importExportSession.getFeedbackQuestions()) {
-			if (!i.isRead()) {
-				unreadInterposed++;
-			}
-		}
-		for (ImportExportQuestion question : importExportSession.getQuestions()) {
-			numAnswers += question.getAnswers().size();
-			if (question.getAnswers().isEmpty()) {
-				numUnanswered++;
-			}
-		}
-		final SessionInfo info = new SessionInfo(session);
-		info.setNumQuestions(importExportSession.getQuestions().size());
-		info.setNumUnanswered(numUnanswered);
-		info.setNumAnswers(numAnswers);
-		info.setNumInterposed(importExportSession.getFeedbackQuestions().size());
-		info.setNumUnredInterposed(unreadInterposed);
-		return info;
-	}
-
-	@Override
-	public List<String> getSubjects(Session session, String questionVariant) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), questionVariant);
-		view.setEndKeyArray(session.get_id(), questionVariant, "{}");
-		ViewResults results = this.getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-
-		Set<String> uniqueSubjects = new HashSet<>();
-
-		for (final Document d : results.getResults()) {
-			uniqueSubjects.add(d.getJSONArray("key").getString(3));
-		}
-
-		return new ArrayList<>(uniqueSubjects);
-	}
-
-	/* TODO: remove if this method is no longer used */
-	@Override
-	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.get_id(), questionVariant, 1,	subject);
-		view.setEndKeyArray(session.get_id(), questionVariant, 1, subject, "{}");
-		ViewResults results = this.getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-
-		List<String> qids = new ArrayList<>();
-
-		for (final Document d : results.getResults()) {
-			final String s = d.getId();
-			qids.add(s);
-		}
-
-		return qids;
-	}
-
-	@Override
-	public List<Question> getQuestionsByIds(List<String> ids, final Session session) {
-		View view = new View("_all_docs");
-		view.setKeys(ids);
-		view.setIncludeDocs(true);
-		final List<Document> questiondocs = getDatabase().view(view).getResults();
-		if (questiondocs == null || questiondocs.isEmpty()) {
-
-			return null;
-		}
-		final List<Question> result = new ArrayList<>();
-		final MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry();
-		final Morpher dynaMorpher = new BeanMorpher(PossibleAnswer.class, morpherRegistry);
-		morpherRegistry.registerMorpher(dynaMorpher);
-		for (final Document document : questiondocs) {
-			if (!"".equals(document.optString("error"))) {
-				// Skip documents we could not load. Maybe they were deleted.
-				continue;
-			}
-			final Question question = (Question) JSONObject.toBean(
-					document.getJSONObject().getJSONObject("doc"),
-					Question.class
-					);
-			@SuppressWarnings("unchecked")
-			final Collection<PossibleAnswer> answers = JSONArray.toCollection(
-					document.getJSONObject().getJSONObject("doc").getJSONArray("possibleAnswers"),
-					PossibleAnswer.class
-					);
-			question.setPossibleAnswers(new ArrayList<>(answers));
-			question.setSessionKeyword(session.getKeyword());
-			if (!"freetext".equals(question.getQuestionType()) && 0 == question.getPiRound()) {
-				/* needed for legacy questions whose piRound property has not been set */
-				question.setPiRound(1);
-			}
-
-			if (question.getImage() != null) {
-				question.setImage("true");
-			}
-
-			result.add(question);
-		}
-		return result;
-	}
-
-	@Override
-	public List<Motd> getAdminMotds() {
-		final View view = new View("motd/doc_by_audience_for_global");
-		return getMotds(view);
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motds", key = "'all'")
-	public List<Motd> getMotdsForAll() {
-		final View view = new View("motd/doc_by_audience_for_global");
-		return getMotds(view);
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motds", key = "'loggedIn'")
-	public List<Motd> getMotdsForLoggedIn() {
-		final View view = new View("motd/doc_by_audience_for_global");
-		view.setKey("loggedIn");
-		return getMotds(view);
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motds", key = "'tutors'")
-	public List<Motd> getMotdsForTutors() {
-		final View view1 = new View("motd/doc_by_audience_for_global");
-		final View view2 = new View("motd/doc_by_audience_for_global");
-		view1.setKey("loggedIn");
-		view2.setKey("tutors");
-		final List<Motd> union = new ArrayList<>();
-		union.addAll(getMotds(view1));
-		union.addAll(getMotds(view2));
-
-		return union;
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motds", key = "'students'")
-	public List<Motd> getMotdsForStudents() {
-		final View view1 = new View("motd/doc_by_audience_for_global");
-		final View view2 = new View("motd/doc_by_audience_for_global");
-		view1.setKey("loggedIn");
-		view2.setKey("students");
-		final List<Motd> union = new ArrayList<>();
-		union.addAll(getMotds(view1));
-		union.addAll(getMotds(view2));
-
-		return union;
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motds", key = "('session').concat(#p0)")
-	public List<Motd> getMotdsForSession(final String sessionkey) {
-		final View view = new View("motd/doc_by_sessionkey");
-		view.setKey(sessionkey);
-		return getMotds(view);
-	}
-
-	@Override
-	public List<Motd> getMotds(View view) {
-		final ViewResults motddocs = this.getDatabase().view(view);
-		List<Motd> motdlist = new ArrayList<>();
-		for (final Document d : motddocs.getResults()) {
-			Motd motd = new Motd();
-			motd.set_id(d.getId());
-			motd.set_rev(d.getJSONObject("value").getString("_rev"));
-			motd.setMotdkey(d.getJSONObject("value").getString("motdkey"));
-			Date start = new Date(Long.parseLong(d.getJSONObject("value").getString("startdate")));
-			motd.setStartdate(start);
-			Date end = new Date(Long.parseLong(d.getJSONObject("value").getString("enddate")));
-			motd.setEnddate(end);
-			motd.setTitle(d.getJSONObject("value").getString("title"));
-			motd.setText(d.getJSONObject("value").getString("text"));
-			motd.setAudience(d.getJSONObject("value").getString("audience"));
-			motd.setSessionkey(d.getJSONObject("value").getString("sessionkey"));
-			motdlist.add(motd);
-		}
-		return motdlist;
-	}
-
-	@Override
-	public Motd getMotdByKey(String key) {
-		final View view = new View("motd/by_motdkey");
-		view.setIncludeDocs(true);
-		view.setKey(key);
-		Motd motd = new Motd();
-
-		ViewResults results = this.getDatabase().view(view);
-
-		for (final Document d : results.getResults()) {
-			motd.set_id(d.getId());
-			motd.set_rev(d.getJSONObject("doc").getString("_rev"));
-			motd.setMotdkey(d.getJSONObject("doc").getString("motdkey"));
-			Date start = new Date(Long.parseLong(d.getJSONObject("doc").getString("startdate")));
-			motd.setStartdate(start);
-			Date end = new Date(Long.parseLong(d.getJSONObject("doc").getString("enddate")));
-			motd.setEnddate(end);
-			motd.setTitle(d.getJSONObject("doc").getString("title"));
-			motd.setText(d.getJSONObject("doc").getString("text"));
-			motd.setAudience(d.getJSONObject("doc").getString("audience"));
-			motd.setSessionkey(d.getJSONObject("doc").getString("sessionkey"));
-		}
-
-		return motd;
-	}
-
-	@Override
-	@CacheEvict(cacheNames = "motds", key = "#p0.audience.concat(#p0.sessionkey)")
-	public Motd createOrUpdateMotd(Motd motd) {
-		try {
-			String id = motd.get_id();
-			String rev = motd.get_rev();
-			Document d = new Document();
-
-			if (null != id) {
-				d = database.getDocument(id, rev);
-			} else {
-				motd.setMotdkey(sessionService.generateKeyword());
-				d.put("motdkey", motd.getMotdkey());
-			}
-			d.put("type", "motd");
-			d.put("startdate", String.valueOf(motd.getStartdate().getTime()));
-			d.put("enddate", String.valueOf(motd.getEnddate().getTime()));
-			d.put("title", motd.getTitle());
-			d.put("text", motd.getText());
-			d.put("audience", motd.getAudience());
-			d.put("sessionId", motd.getSessionId());
-			d.put("sessionkey", motd.getSessionkey());
-
-			database.saveDocument(d, id);
-			motd.set_id(d.getId());
-			motd.set_rev(d.getRev());
-
-			return motd;
-		} catch (IOException e) {
-			logger.error("Could not save MotD {}.", motd, e);
-		}
-
-		return null;
-	}
-
-	@Override
-	@CacheEvict(cacheNames = "motds", key = "#p0.audience.concat(#p0.sessionkey)")
-	public void deleteMotd(Motd motd) {
-		try {
-			this.deleteDocument(motd.get_id());
-		} catch (IOException e) {
-			logger.error("Could not delete MotD {}.", motd.get_id(), e);
-		}
-	}
-
-	@Override
-	@Cacheable(cacheNames = "motdlist", key = "#p0")
-	public MotdList getMotdListForUser(final String username) {
-		View view = new View("motdlist/doc_by_username");
-		view.setKey(username);
-
-		ViewResults results = this.getDatabase().view(view);
-
-		MotdList motdlist = new MotdList();
-		for (final Document d : results.getResults()) {
-			motdlist.set_id(d.getId());
-			motdlist.set_rev(d.getJSONObject("value").getString("_rev"));
-			motdlist.setUsername(d.getJSONObject("value").getString("username"));
-			motdlist.setMotdkeys(d.getJSONObject("value").getString("motdkeys"));
-		}
-		return motdlist;
-	}
-
-	@Override
-	@CachePut(cacheNames = "motdlist", key = "#p0.username")
-	public MotdList createOrUpdateMotdList(MotdList motdlist) {
-		try {
-			String id = motdlist.get_id();
-			String rev = motdlist.get_rev();
-			Document d = new Document();
-
-			if (null != id) {
-				d = database.getDocument(id, rev);
-			}
-			d.put("type", "motdlist");
-			d.put("username", motdlist.getUsername());
-			d.put("motdkeys", motdlist.getMotdkeys());
-
-			database.saveDocument(d, id);
-			motdlist.set_id(d.getId());
-			motdlist.set_rev(d.getRev());
-
-			return motdlist;
-		} catch (IOException e) {
-			logger.error("Could not save MotD list {}.", motdlist, e);
-		}
-
-		return null;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
deleted file mode 100644
index bc3a37da2c502e6932c4ff3f461af8fbd510b180..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.dao;
-
-import com.fourspaces.couchdb.View;
-import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.domain.CourseScore;
-import de.thm.arsnova.entities.*;
-import de.thm.arsnova.entities.transport.ImportExportSession;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * All methods the database must support.
- */
-public interface IDatabaseDao {
-	/**
-	 * Logs an event to the database. Arbitrary data can be attached as payload. Database logging should only be used
-	 * if the logged data is later analyzed by the backend itself. Otherwise use the default logging mechanisms.
-	 *
-	 * @param event type of the event
-	 * @param payload arbitrary logging data
-	 * @param level severity of the event
-	 */
-	void log(String event, Map<String, Object> payload, LogEntry.LogLevel level);
-
-	/**
-	 * Logs an event of informational severity to the database. Arbitrary data can be attached as payload. Database
-	 * logging should only be used if the logged data is later analyzed by the backend itself. Otherwise use the default
-	 * logging mechanisms.
-	 *
-	 * @param event type of the event
-	 * @param payload arbitrary logging data
-	 */
-	void log(String event, Map<String, Object> payload);
-
-	/**
-	 * Logs an event to the database. Arbitrary data can be attached as payload. Database logging should only be used
-	 * if the logged data is later analyzed by the backend itself. Otherwise use the default logging mechanisms.
-	 *
-	 * @param event type of the event
-	 * @param level severity of the event
-	 * @param rawPayload key/value pairs of arbitrary logging data
-	 */
-	void log(String event, LogEntry.LogLevel level, Object... rawPayload);
-
-	/**
-	 * Logs an event of informational severity to the database. Arbitrary data can be attached as payload. Database
-	 * logging should only be used if the logged data is later analyzed by the backend itself. Otherwise use the default
-	 * logging mechanisms.
-	 *
-	 * @param event type of the event
-	 * @param rawPayload key/value pairs of arbitrary logging data
-	 */
-	void log(String event, Object... rawPayload);
-
-	Session getSessionFromKeyword(String keyword);
-
-	List<Session> getMySessions(User user, final int start, final int limit);
-
-	List<Session> getSessionsForUsername(String username, final int start, final int limit);
-
-	List<Session> getPublicPoolSessions();
-
-	List<Session> getMyPublicPoolSessions(User user);
-
-	Session saveSession(User user, Session session);
-
-	boolean sessionKeyAvailable(String keyword);
-
-	Question saveQuestion(Session session, Question question);
-
-	InterposedQuestion saveQuestion(Session session, InterposedQuestion question, User user);
-
-	Question getQuestion(String id);
-
-	List<Question> getSkillQuestionsForUsers(Session session);
-
-	List<Question> getSkillQuestionsForTeachers(Session session);
-
-	int getSkillQuestionCount(Session session);
-
-	LoggedIn registerAsOnlineUser(User u, Session s);
-
-	Session updateSessionOwnerActivity(Session session);
-
-	List<String> getQuestionIds(Session session, User user);
-
-	int deleteQuestionWithAnswers(Question question);
-
-	int[] deleteAllQuestionsWithAnswers(Session session);
-
-	List<String> getUnAnsweredQuestionIds(Session session, User user);
-
-	Answer getMyAnswer(User me, String questionId, int piRound);
-
-	List<Answer> getAnswers(Question question, int piRound);
-
-	List<Answer> getAnswers(Question question);
-
-	List<Answer> getAllAnswers(Question question);
-
-	int getAnswerCount(Question question, int piRound);
-
-	int getTotalAnswerCountByQuestion(Question question);
-
-	int getAbstentionAnswerCount(String questionId);
-
-	List<Answer> getFreetextAnswers(String questionId, final int start, final int limit);
-
-	List<Answer> getMyAnswers(User me, Session session);
-
-	int getTotalAnswerCount(String sessionKey);
-
-	int getInterposedCount(String sessionKey);
-
-	InterposedReadingCount getInterposedReadingCount(Session session);
-
-	InterposedReadingCount getInterposedReadingCount(Session session, User user);
-
-	List<InterposedQuestion> getInterposedQuestions(Session session, final int start, final int limit);
-
-	List<InterposedQuestion> getInterposedQuestions(Session session, User user, final int start, final int limit);
-
-	InterposedQuestion getInterposedQuestion(String questionId);
-
-	void markInterposedQuestionAsRead(InterposedQuestion question);
-
-	List<Session> getMyVisitedSessions(User user, final int start, final int limit);
-
-	List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit);
-
-	Question updateQuestion(Question question);
-
-	int deleteAnswers(Question question);
-
-	Answer saveAnswer(Answer answer, User user, Question question, Session session);
-
-	Answer updateAnswer(Answer answer);
-
-	Session getSessionFromId(String sessionId);
-
-	void deleteAnswer(String answerId);
-
-	void deleteInterposedQuestion(InterposedQuestion question);
-
-	List<Session> getCourseSessions(List<Course> courses);
-
-	Session updateSession(Session session);
-
-	Session changeSessionCreator(Session session, String newCreator);
-
-	/**
-	 * Deletes a session and related data.
-	 *
-	 * @param session the session for deletion
-	 */
-	int[] deleteSession(Session session);
-
-	int[] deleteInactiveGuestSessions(long lastActivityBefore);
-
-	int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore);
-
-	List<Question> getLectureQuestionsForUsers(Session session);
-
-	List<Question> getLectureQuestionsForTeachers(Session session);
-
-	List<Question> getFlashcardsForUsers(Session session);
-
-	List<Question> getFlashcardsForTeachers(Session session);
-
-	List<Question> getPreparationQuestionsForUsers(Session session);
-
-	List<Question> getPreparationQuestionsForTeachers(Session session);
-
-	List<Question> getAllSkillQuestions(Session session);
-
-	int getLectureQuestionCount(Session session);
-
-	int getFlashcardCount(Session session);
-
-	int getPreparationQuestionCount(Session session);
-
-	int countLectureQuestionAnswers(Session session);
-
-	int countPreparationQuestionAnswers(Session session);
-
-	int[] deleteAllLectureQuestionsWithAnswers(Session session);
-
-	int[] deleteAllFlashcardsWithAnswers(Session session);
-
-	int[] deleteAllPreparationQuestionsWithAnswers(Session session);
-
-	List<String> getUnAnsweredLectureQuestionIds(Session session, User user);
-
-	List<String> getUnAnsweredPreparationQuestionIds(Session session, User user);
-
-	int deleteAllInterposedQuestions(Session session);
-
-	int deleteAllInterposedQuestions(Session session, User user);
-
-	void publishQuestions(Session session, boolean publish, List<Question> questions);
-
-	List<Question> publishAllQuestions(Session session, boolean publish);
-
-	int deleteAllQuestionsAnswers(Session session);
-
-	DbUser createOrUpdateUser(DbUser user);
-
-	DbUser getUser(String username);
-
-	boolean deleteUser(DbUser dbUser);
-
-	int deleteInactiveUsers(long lastActivityBefore);
-
-	CourseScore getLearningProgress(Session session);
-
-	List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
-
-	List<SessionInfo> getPublicPoolSessionsInfo();
-
-	List<SessionInfo> getMyPublicPoolSessionsInfo(final User user);
-
-	List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit);
-
-	int deleteAllPreparationAnswers(Session session);
-
-	int deleteAllLectureAnswers(Session session);
-
-	SessionInfo importSession(User user, ImportExportSession importSession);
-
-	ImportExportSession exportSession(String sessionkey, Boolean withAnswer, Boolean withFeedbackQuestions);
-
-	Statistics getStatistics();
-
-	List<String> getSubjects(Session session, String questionVariant);
-
-	List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject);
-
-	List<Question> getQuestionsByIds(List<String> ids, Session session);
-
-	void resetQuestionsRoundState(Session session, List<Question> questions);
-
-	void setVotingAdmissions(Session session, boolean disableVoting, List<Question> questions);
-
-	List<Question> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting);
-
-	<T> T getObjectFromId(String documentId, Class<T> klass);
-
-	List<Motd> getAdminMotds();
-
-	List<Motd> getMotdsForAll();
-
-	List<Motd> getMotdsForLoggedIn();
-
-	List<Motd> getMotdsForTutors();
-
-	List<Motd> getMotdsForStudents();
-
-	List<Motd> getMotdsForSession(final String sessionkey);
-
-	List<Motd> getMotds(View view);
-
-	Motd getMotdByKey(String key);
-
-	Motd createOrUpdateMotd(Motd motd);
-
-	void deleteMotd(Motd motd);
-
-	MotdList getMotdListForUser(final String username);
-
-	MotdList createOrUpdateMotdList(MotdList motdlist);
-}
diff --git a/src/main/java/de/thm/arsnova/dao/package-info.java b/src/main/java/de/thm/arsnova/dao/package-info.java
deleted file mode 100644
index 1b1c0dd99512dc8a48b58c97a9d33b92f34b43a3..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/dao/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Classes and interfaces for accessing the database
- */
-package de.thm.arsnova.dao;
diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java
index 89a750ce1675bf3fe33d75f571413499b4124ef9..a5596c0b73bafa8a37d2ed287ce4b9e66853606b 100644
--- a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java
+++ b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java
@@ -17,8 +17,8 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.events.*;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.context.ApplicationEventPublisher;
@@ -34,7 +34,7 @@ import org.springframework.stereotype.Component;
 public class LearningProgressFactory implements NovaEventVisitor, ILearningProgressFactory, ApplicationEventPublisherAware {
 
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private SessionStatisticsRepository sessionStatisticsRepository;
 
 	private ApplicationEventPublisher publisher;
 
@@ -42,19 +42,19 @@ public class LearningProgressFactory implements NovaEventVisitor, ILearningProgr
 	public LearningProgress create(String progressType, String questionVariant) {
 		VariantLearningProgress learningProgress;
 		if ("questions".equals(progressType)) {
-			learningProgress = new QuestionBasedLearningProgress(databaseDao);
+			learningProgress = new QuestionBasedLearningProgress(sessionStatisticsRepository);
 		} else {
-			learningProgress = new PointBasedLearningProgress(databaseDao);
+			learningProgress = new PointBasedLearningProgress(sessionStatisticsRepository);
 		}
 		learningProgress.setQuestionVariant(questionVariant);
 		return learningProgress;
 	}
 
 	@Override
-	public void visit(NewInterposedQuestionEvent event) { }
+	public void visit(NewCommentEvent event) { }
 
 	@Override
-	public void visit(DeleteInterposedQuestionEvent deleteInterposedQuestionEvent) { }
+	public void visit(DeleteCommentEvent deleteCommentEvent) { }
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
diff --git a/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java b/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java
index a904caf6b4599c9720a34a4917691cb2b3212bbd..21f2fbd3d966ccd6b92749ddde815fb184a15fae 100644
--- a/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java
@@ -17,17 +17,17 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
  * Calculates learning progress based on a question's value.
  */
 public class PointBasedLearningProgress extends VariantLearningProgress {
 
-	public PointBasedLearningProgress(IDatabaseDao dao) {
-		super(dao);
+	public PointBasedLearningProgress(SessionStatisticsRepository sessionStatisticsRepository) {
+		super(sessionStatisticsRepository);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java b/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java
index dba9b41803ba939c47338945805eebd4a30d6b13..37b6c63974f639cca4e76bdf8563b25f5e9dada7 100644
--- a/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java
@@ -17,9 +17,9 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
  * Calculates learning progress based on overall correctness of an answer. A question is answered correctly if and
@@ -27,8 +27,8 @@ import de.thm.arsnova.entities.transport.LearningProgressValues;
  */
 public class QuestionBasedLearningProgress extends VariantLearningProgress {
 
-	public QuestionBasedLearningProgress(IDatabaseDao dao) {
-		super(dao);
+	public QuestionBasedLearningProgress(SessionStatisticsRepository sessionStatisticsRepository) {
+		super(sessionStatisticsRepository);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java b/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java
index 708acb4b79b454803a6d0bb3233e4e9de3ece960..7e1c069701fa3fe336d6f7a1042185a69d080ac0 100644
--- a/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java
@@ -17,10 +17,10 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
  * Base class for the learning progress feature that allows filtering on the question variant.
@@ -31,14 +31,14 @@ abstract class VariantLearningProgress implements LearningProgress {
 
 	private String questionVariant;
 
-	private final IDatabaseDao databaseDao;
+	private final SessionStatisticsRepository sessionStatisticsRepository;
 
-	public VariantLearningProgress(final IDatabaseDao dao) {
-		this.databaseDao = dao;
+	public VariantLearningProgress(final SessionStatisticsRepository sessionStatisticsRepository) {
+		this.sessionStatisticsRepository = sessionStatisticsRepository;
 	}
 
 	private void loadProgress(final Session session) {
-		this.courseScore = databaseDao.getLearningProgress(session);
+		this.courseScore = sessionStatisticsRepository.getLearningProgress(session);
 	}
 
 	public void setQuestionVariant(final String variant) {
diff --git a/src/main/java/de/thm/arsnova/entities/Answer.java b/src/main/java/de/thm/arsnova/entities/Answer.java
index 283060bb155cd307944bb5b7037f428bfadd2708..146acaf85f960b692c825dc7baf2b4f5f0fafc54 100644
--- a/src/main/java/de/thm/arsnova/entities/Answer.java
+++ b/src/main/java/de/thm/arsnova/entities/Answer.java
@@ -18,6 +18,8 @@
 package de.thm.arsnova.entities;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -29,10 +31,9 @@ import java.io.Serializable;
  * This class has additional fields to transport generated answer statistics.
  */
 @ApiModel(value = "Answer", description = "the answer entity")
-public class Answer implements Serializable {
-
-	private String _id;
-	private String _rev;
+public class Answer implements Entity {
+	private String id;
+	private String rev;
 	private String type;
 	private String sessionId;
 	private String questionId;
@@ -58,143 +59,158 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public final String get_id() {
-		return _id;
-	}
-
-	public final void set_id(String _id) {
-		this._id = _id;
-	}
-
-	public final String get_rev() {
-		return _rev;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
 	}
 
-	public final void set_rev(final String _rev) {
-		this._rev = _rev;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
 	}
 
-	@ApiModelProperty(required = true, value = "\"skill_question_answer\" - used to filter in the couchDB")
-	public final String getType() {
-		return type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
-	public final void setType(final String type) {
-		this.type = type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
 	@ApiModelProperty(required = true, value = "ID of the session, the answer is assigned to")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getSessionId() {
 		return sessionId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSessionId(final String sessionId) {
 		this.sessionId = sessionId;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display question id")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getQuestionId() {
 		return questionId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setQuestionId(final String questionId) {
 		this.questionId = questionId;
 	}
 
 	@ApiModelProperty(required = true, value = "the answer text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getAnswerText() {
 		return answerText;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setAnswerText(final String answerText) {
 		this.answerText = answerText;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getAnswerTextRaw() {
 		return this.answerTextRaw;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setAnswerTextRaw(final String answerTextRaw) {
 		this.answerTextRaw = answerTextRaw;
 	}
 
 	@ApiModelProperty(required = true, value = "the answer subject")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getAnswerSubject() {
 		return answerSubject;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setAnswerSubject(final String answerSubject) {
 		this.answerSubject = answerSubject;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final boolean isSuccessfulFreeTextAnswer() {
 		return this.successfulFreeTextAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSuccessfulFreeTextAnswer(final boolean successfulFreeTextAnswer) {
 		this.successfulFreeTextAnswer = successfulFreeTextAnswer;
 	}
 
 	@ApiModelProperty(required = true, value = "the peer instruction round nr.")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getPiRound() {
 		return piRound;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRound(int piRound) {
 		this.piRound = piRound;
 	}
 
-	/* TODO: use JsonViews instead of JsonIgnore when supported by Spring (4.1)
-	 * http://wiki.fasterxml.com/JacksonJsonViews
-	 * https://jira.spring.io/browse/SPR-7156 */
 	@ApiModelProperty(required = true, value = "the user")
-	@JsonIgnore
+	@JsonView(View.Persistence.class)
 	public final String getUser() {
 		return user;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
+	public final void setUser(final String user) {
+		this.user = user;
+	}
+
 	@ApiModelProperty(required = true, value = "the answer image")
-	@JsonIgnore
+	@JsonView(View.Persistence.class)
 	public String getAnswerImage() {
 		return answerImage;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAnswerImage(String answerImage) {
 		this.answerImage = answerImage;
 	}
 
 	@ApiModelProperty(required = true, value = "the answer thumbnail")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getAnswerThumbnailImage() {
 		return answerThumbnailImage;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAnswerThumbnailImage(String answerThumbnailImage) {
 		this.answerThumbnailImage = answerThumbnailImage;
 	}
 
-	public final void setUser(final String user) {
-		this.user = user;
-	}
-
 	@ApiModelProperty(required = true, value = "the creation date timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getTimestamp() {
 		return timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setTimestamp(long timestamp) {
 		this.timestamp = timestamp;
 	}
 
 	@ApiModelProperty(required = true, value = "displays whether the answer is read")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isRead() {
 		return read;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setRead(boolean read) {
 		this.read = read;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of answers given. used for statistics")
+	@JsonView(View.Public.class)
 	public final int getAnswerCount() {
 		return answerCount;
 	}
@@ -204,15 +220,18 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "the abstention")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isAbstention() {
 		return abstention;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAbstention(boolean abstention) {
 		this.abstention = abstention;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of abstentions given. used for statistics")
+	@JsonView(View.Public.class)
 	public int getAbstentionCount() {
 		return abstentionCount;
 	}
@@ -222,19 +241,23 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "either lecture or preparation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getQuestionVariant() {
 		return questionVariant;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setQuestionVariant(String questionVariant) {
 		this.questionVariant = questionVariant;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display question value")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getQuestionValue() {
 		return questionValue;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setQuestionValue(int questionValue) {
 		this.questionValue = questionValue;
 	}
@@ -255,8 +278,8 @@ public class Answer implements Serializable {
 		// auto generated!
 		final int prime = 31;
 		int result = 1;
-		result = prime * result + ((_id == null) ? 0 : _id.hashCode());
-		result = prime * result + ((_rev == null) ? 0 : _rev.hashCode());
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
+		result = prime * result + ((rev == null) ? 0 : rev.hashCode());
 		result = prime * result + ((answerSubject == null) ? 0 : answerSubject.hashCode());
 		result = prime * result + ((answerText == null) ? 0 : answerText.hashCode());
 		result = prime * result + piRound;
@@ -280,18 +303,18 @@ public class Answer implements Serializable {
 			return false;
 		}
 		Answer other = (Answer) obj;
-		if (_id == null) {
-			if (other._id != null) {
+		if (id == null) {
+			if (other.id != null) {
 				return false;
 			}
-		} else if (!_id.equals(other._id)) {
+		} else if (!id.equals(other.id)) {
 			return false;
 		}
-		if (_rev == null) {
-			if (other._rev != null) {
+		if (rev == null) {
+			if (other.rev != null) {
 				return false;
 			}
-		} else if (!_rev.equals(other._rev)) {
+		} else if (!rev.equals(other.rev)) {
 			return false;
 		}
 		if (answerSubject == null) {
diff --git a/src/main/java/de/thm/arsnova/entities/Authorize.java b/src/main/java/de/thm/arsnova/entities/Authorize.java
index de37e72a5164c3991549d71af68dc843f8b931e0..eacc7c5a859be40fd1758e8f287f0ea6da27b68a 100644
--- a/src/main/java/de/thm/arsnova/entities/Authorize.java
+++ b/src/main/java/de/thm/arsnova/entities/Authorize.java
@@ -17,10 +17,14 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 public class Authorize {
 	private String user;
 	private String socketid;
 
+	@JsonView(View.Public.class)
 	public final String getUser() {
 		return user;
 	}
@@ -29,6 +33,7 @@ public class Authorize {
 		this.user = user;
 	}
 
+	@JsonView(View.Public.class)
 	public final String getSocketid() {
 		return socketid;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/InterposedQuestion.java b/src/main/java/de/thm/arsnova/entities/Comment.java
similarity index 63%
rename from src/main/java/de/thm/arsnova/entities/InterposedQuestion.java
rename to src/main/java/de/thm/arsnova/entities/Comment.java
index 7b206698b9d2ee5b93198696fada21a3c5b25b75..de2a93955c148c67ede7b3afa709643293a3e598 100644
--- a/src/main/java/de/thm/arsnova/entities/InterposedQuestion.java
+++ b/src/main/java/de/thm/arsnova/entities/Comment.java
@@ -17,21 +17,18 @@
  */
 package de.thm.arsnova.entities;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
-import java.io.Serializable;
-
 /**
- * A question the user is asking the teacher. Also known as feedback or audience question.
+ * A question the user is asking the teacher. Also known as comment, feedback or audience question.
  */
-@ApiModel(value = "audiencequestion", description = "the interposed question entity")
-public class InterposedQuestion implements Serializable {
-
-	private String _id;
-	private String _rev;
-	private String type;
+@ApiModel(value = "comment", description = "the comment entity")
+public class Comment implements Entity {
+	private String id;
+	private String rev;
 	private String subject;
 	private String text;
 	/* FIXME sessionId actually is used to hold the sessionKey.
@@ -44,77 +41,87 @@ public class InterposedQuestion implements Serializable {
 	private boolean read;
 	private String creator;
 
-	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public String get_id() {
-		return _id;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
 	}
-	public void set_id(String _id) {
-		this._id = _id;
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
 	}
 
-	public String get_rev() {
-		return _rev;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
-	public void set_rev(String _rev) {
-		this._rev = _rev;
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
 	@ApiModelProperty(required = true, value = "is read")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isRead() {
 		return read;
 	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setRead(boolean read) {
 		this.read = read;
 	}
 
-	@ApiModelProperty(required = true, value = "used to display the type")
-	public String getType() {
-		return type;
-	}
-	public void setType(String type) {
-		this.type = type;
-	}
-
 	@ApiModelProperty(required = true, value = "the subject")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSubject() {
 		return subject;
 	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSubject(String subject) {
 		this.subject = subject;
 	}
 
 	@ApiModelProperty(required = true, value = "the Text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getText() {
 		return text;
 	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setText(String text) {
 		this.text = text;
 	}
 
-	@ApiModelProperty(required = true, value = "ID of the session, the question is assigned to")
+	@ApiModelProperty(required = true, value = "ID of the session, the comment is assigned to")
+	@JsonView(View.Persistence.class)
 	public String getSessionId() {
 		return sessionId;
 	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSessionId(String sessionId) {
 		this.sessionId = sessionId;
 	}
 
 	@ApiModelProperty(required = true, value = "creation date timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getTimestamp() {
 		return timestamp;
 	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTimestamp(long timestamp) {
 		this.timestamp = timestamp;
 	}
 
-	/* TODO: use JsonViews instead of JsonIgnore when supported by Spring (4.1)
-	 * http://wiki.fasterxml.com/JacksonJsonViews
-	 * https://jira.spring.io/browse/SPR-7156 */
-	@JsonIgnore
+	@JsonView(View.Persistence.class)
 	public String getCreator() {
 		return creator;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setCreator(String creator) {
 		this.creator = creator;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/InterposedReadingCount.java b/src/main/java/de/thm/arsnova/entities/CommentReadingCount.java
similarity index 76%
rename from src/main/java/de/thm/arsnova/entities/InterposedReadingCount.java
rename to src/main/java/de/thm/arsnova/entities/CommentReadingCount.java
index a254cb6c691b3154d2feb071e176a04d027dd0b2..eb6382e4018eb75392dc9ec1ede4a2891d8f6f5a 100644
--- a/src/main/java/de/thm/arsnova/entities/InterposedReadingCount.java
+++ b/src/main/java/de/thm/arsnova/entities/CommentReadingCount.java
@@ -17,29 +17,32 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
 /**
- * Wrapper class for counting read and unread interposed questions for a session or a single user.
+ * Wrapper class for counting read and unread comments for a session or a single user.
  */
-@ApiModel(value = "audiencequestion/readcount", description = "the interposed reading count entity")
-public class InterposedReadingCount {
+@ApiModel(value = "audiencequestion/readcount", description = "the comment reading count entity")
+public class CommentReadingCount {
 
 	private int read;
 	private int unread;
 
-	public InterposedReadingCount(int readCount, int unreadCount) {
+	public CommentReadingCount(int readCount, int unreadCount) {
 		this.read = readCount;
 		this.unread = unreadCount;
 	}
 
-	public InterposedReadingCount() {
+	public CommentReadingCount() {
 		this.read = 0;
 		this.unread = 0;
 	}
 
-	@ApiModelProperty(required = true, value = "the number of read interposed questions")
+	@ApiModelProperty(required = true, value = "the number of read comments")
+	@JsonView(View.Public.class)
 	public int getRead() {
 		return read;
 	}
@@ -48,7 +51,8 @@ public class InterposedReadingCount {
 		this.read = read;
 	}
 
-	@ApiModelProperty(required = true, value = "the number of unread interposed questions")
+	@ApiModelProperty(required = true, value = "the number of unread comments")
+	@JsonView(View.Public.class)
 	public int getUnread() {
 		return unread;
 	}
@@ -57,7 +61,8 @@ public class InterposedReadingCount {
 		this.unread = unread;
 	}
 
-	@ApiModelProperty(required = true, value = "the number of total interposed questions")
+	@ApiModelProperty(required = true, value = "the number of total comments")
+	@JsonView(View.Public.class)
 	public int getTotal() {
 		return getRead() + getUnread();
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/Question.java b/src/main/java/de/thm/arsnova/entities/Content.java
similarity index 75%
rename from src/main/java/de/thm/arsnova/entities/Question.java
rename to src/main/java/de/thm/arsnova/entities/Content.java
index a99670287ba7b6ce8c87f1bdbc730db9c2969e0e..653a645518299427ef98036fd3e8e110114fde57 100644
--- a/src/main/java/de/thm/arsnova/entities/Question.java
+++ b/src/main/java/de/thm/arsnova/entities/Content.java
@@ -17,20 +17,21 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
-import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
 /**
  * A question the teacher is asking.
  */
-@ApiModel(value = "lecturerquestion", description = "the question entity")
-public class Question implements Serializable {
-
-	private String type;
+@ApiModel(value = "content", description = "the content entity")
+public class Content implements Entity {
+	private String id;
+	private String rev;
 	private String questionType;
 	private String questionVariant;
 	private String subject;
@@ -62,8 +63,6 @@ public class Question implements Serializable {
 	private boolean strictMode;
 	private int rating;
 	private String correctAnswer;
-	private String _id;
-	private String _rev;
 
 	private String image;
 	private String fcImage;
@@ -92,56 +91,78 @@ public class Question implements Serializable {
 	private String hint;
 	private String solution;
 
-	@ApiModelProperty(required = true, value = "the type")
-	public final String getType() {
-		return type;
+	@ApiModelProperty(required = true, value = "the couchDB ID")
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
-	public final void setType(final String type) {
-		this.type = type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
 	@ApiModelProperty(required = true, value = "the question type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getQuestionType() {
 		return questionType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setQuestionType(final String questionType) {
 		this.questionType = questionType;
 	}
 
 	@ApiModelProperty(required = true, value = "either lecture or preparation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getQuestionVariant() {
 		return questionVariant;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setQuestionVariant(final String questionVariant) {
 		this.questionVariant = questionVariant;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display subject")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getSubject() {
 		return subject;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSubject(final String subject) {
 		this.subject = subject;
 	}
 
 	@ApiModelProperty(required = true, value = "the text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getText() {
 		return text;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setText(final String text) {
 		this.text = text;
 	}
 
 	@ApiModelProperty(required = true, value = "true for active question")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final boolean isActive() {
 		return active;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setActive(final boolean active) {
 		this.active = active;
 	}
@@ -156,10 +177,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "list of possible answers")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final List<PossibleAnswer> getPossibleAnswers() {
 		return possibleAnswers;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setPossibleAnswers(final List<PossibleAnswer> possibleAnswers) {
 		this.possibleAnswers = possibleAnswers;
 	}
@@ -174,10 +197,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "couchDB ID of the session, the question is assigned to")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getSessionId() {
 		return sessionId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSessionId(final String sessionId) {
 		this.sessionId = sessionId;
 	}
@@ -201,10 +226,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "creation date timestamp")
+	@JsonView(View.Persistence.class)
 	public final long getTimestamp() {
 		return timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public final void setTimestamp(final long timestamp) {
 		this.timestamp = timestamp;
 	}
@@ -219,46 +246,56 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display duration")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final int getDuration() {
 		return duration;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
+	public final void setDuration(final int duration) {
+		this.duration = duration;
+	}
+
 	@ApiModelProperty(required = true, value = "true for image question")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final boolean isImageQuestion() {
 		return imageQuestion;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImageQuestion(boolean imageQuestion) {
 		this.imageQuestion = imageQuestion;
 	}
 
-	public final void setDuration(final int duration) {
-		this.duration = duration;
-	}
-
 	@ApiModelProperty(required = true, value = "the peer instruction round no.")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getPiRound() {
 		return piRound;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRound(final int piRound) {
 		this.piRound = piRound;
 	}
 
 	@ApiModelProperty(required = true, value = "the peer instruction round end timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getPiRoundEndTime() {
 		return piRoundEndTime;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRoundEndTime(long piRoundEndTime) {
 		this.piRoundEndTime = piRoundEndTime;
 	}
 
 	@ApiModelProperty(required = true, value = "the peer instruction round start timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getPiRoundStartTime() {
 		return piRoundStartTime;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRoundStartTime(long piRoundStartTime) {
 		this.piRoundStartTime = piRoundStartTime;
 	}
@@ -282,340 +319,395 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display showStatistic")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isShowStatistic() {
 		return showStatistic;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setShowStatistic(final boolean showStatistic) {
 		this.showStatistic = showStatistic;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display cvIsColored")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getCvIsColored() {
 		return cvIsColored;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCvIsColored(boolean cvIsColored) {
 		this.cvIsColored = cvIsColored;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display showAnswer")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isShowAnswer() {
 		return showAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setShowAnswer(final boolean showAnswer) {
 		this.showAnswer = showAnswer;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display abstention")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isAbstention() {
 		return abstention;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAbstention(final boolean abstention) {
 		this.abstention = abstention;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnoreCaseSensitive() {
 		return ignoreCaseSensitive;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnoreCaseSensitive(final boolean ignoreCaseSensitive) {
 		this.ignoreCaseSensitive = ignoreCaseSensitive;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnoreWhitespaces() {
 		return ignoreWhitespaces;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnoreWhitespaces(final boolean ignoreWhitespaces) {
 		this.ignoreWhitespaces = ignoreWhitespaces;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnorePunctuation() {
 		return ignorePunctuation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnorePunctuation(final boolean ignorePunctuation) {
 		this.ignorePunctuation = ignorePunctuation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isFixedAnswer() {
 		return this.fixedAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFixedAnswer(final boolean fixedAnswer) {
 		this.fixedAnswer = fixedAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isStrictMode() {
 		return this.strictMode;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setStrictMode(final boolean strictMode) {
 		this.strictMode = strictMode;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final int getRating() {
 		return this.rating;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setRating(final int rating) {
 		this.rating = rating;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getCorrectAnswer() {
 		return correctAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setCorrectAnswer(final String correctAnswer) {
 		this.correctAnswer = correctAnswer;
 	}
 
-	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public final String get_id() {
-		return _id;
-	}
-
-	public final void set_id(final String _id) {
-		this._id = _id;
-	}
-
-	public final String get_rev() {
-		return _rev;
-	}
-
-	public final void set_rev(final String _rev) {
-		this._rev = _rev;
-	}
-
 	@ApiModelProperty(required = true, value = "the image")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getImage() {
 		return image;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImage(final String image) {
 		this.image = image;
 	}
 
 	@ApiModelProperty(required = true, value = "the fcImage")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getFcImage() {
 		return fcImage;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFcImage(final String fcImage) {
 		this.fcImage = fcImage;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSize() {
 		return gridSize;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSize(final int gridSize) {
 		this.gridSize = gridSize;
 	}
 
 	@ApiModelProperty(required = true, value = "the image X offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getOffsetX() {
 		return offsetX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setOffsetX(final int offsetX) {
 		this.offsetX = offsetX;
 	}
 
 	@ApiModelProperty(required = true, value = "the image Y offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getOffsetY() {
 		return offsetY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setOffsetY(final int offsetY) {
 		this.offsetY = offsetY;
 	}
 
 	@ApiModelProperty(required = true, value = "the image zoom level")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getZoomLvl() {
 		return zoomLvl;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setZoomLvl(final int zoomLvl) {
 		this.zoomLvl = zoomLvl;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid X offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridOffsetX() {
 		return gridOffsetX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridOffsetX(int gridOffsetX) {
 		this.gridOffsetX = gridOffsetX;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid Y offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridOffsetY() {
 		return gridOffsetY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridOffsetY(int gridOffsetY) {
 		this.gridOffsetY = gridOffsetY;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid zoom lvl")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridZoomLvl() {
 		return gridZoomLvl;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridZoomLvl(int gridZoomLvl) {
 		this.gridZoomLvl = gridZoomLvl;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid X size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSizeX() {
 		return gridSizeX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSizeX(int gridSizeX) {
 		this.gridSizeX = gridSizeX;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid Y size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSizeY() {
 		return gridSizeY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSizeY(int gridSizeY) {
 		this.gridSizeY = gridSizeY;
 	}
 
 	@ApiModelProperty(required = true, value = "true for hidden grid")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getGridIsHidden() {
 		return gridIsHidden;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridIsHidden(boolean gridIsHidden) {
 		this.gridIsHidden = gridIsHidden;
 	}
 
 	@ApiModelProperty(required = true, value = "the image rotation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getImgRotation() {
 		return imgRotation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImgRotation(int imgRotation) {
 		this.imgRotation = imgRotation;
 	}
 
 	@ApiModelProperty(required = true, value = "the toggled left fields")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getToggleFieldsLeft() {
 		return toggleFieldsLeft;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setToggleFieldsLeft(boolean toggleFieldsLeft) {
 		this.toggleFieldsLeft = toggleFieldsLeft;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of clickable fields")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getNumClickableFields() {
 		return numClickableFields;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setNumClickableFields(int numClickableFields) {
 		this.numClickableFields = numClickableFields;
 	}
 
 	@ApiModelProperty(required = true, value = "the threshold of correct answers")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getThresholdCorrectAnswers() {
 		return thresholdCorrectAnswers;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setThresholdCorrectAnswers(int thresholdCorrectAnswers) {
 		this.thresholdCorrectAnswers = thresholdCorrectAnswers;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid line color")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridLineColor() {
 		return gridLineColor;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridLineColor(String gridLineColor) {
 		this.gridLineColor = gridLineColor;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of dots")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getNumberOfDots() {
 		return numberOfDots;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setNumberOfDots(int numberOfDots) {
 		this.numberOfDots = numberOfDots;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridType() {
 		return gridType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridType(String gridType) {
 		this.gridType = gridType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setScaleFactor(String scaleFactor) {
 		this.scaleFactor = scaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "the image scale factor")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getScaleFactor() {
 		return this.scaleFactor;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridScaleFactor(String scaleFactor) {
 		this.gridScaleFactor = scaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid scale factor")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridScaleFactor() {
 		return this.gridScaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "true for a question that can be answered via text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isTextAnswerEnabled() {
 		return this.textAnswerEnabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTextAnswerEnabled(boolean textAnswerEnabled) {
 		this.textAnswerEnabled = textAnswerEnabled;
 	}
 
 	@ApiModelProperty(required = true, value = "true for disabled voting")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isVotingDisabled() {
 		return votingDisabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setVotingDisabled(boolean votingDisabled) {
 		this.votingDisabled = votingDisabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getHint() {
 		return hint;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setHint(String hint) {
 		this.hint = hint;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSolution() {
 		return solution;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSolution(String solution) {
 		this.solution = solution;
 	}
 
 	@Override
 	public final String toString() {
-		return "Question type '" + type + "': " + subject + ";\n" + text + possibleAnswers;
+		return "Content type '" + questionType + "': " + subject + ";\n" + text + possibleAnswers;
 	}
 
 	@Override
@@ -623,7 +715,7 @@ public class Question implements Serializable {
 		// auto generated!
 		final int prime = 31;
 		int result = 1;
-		result = prime * result + ((_id == null) ? 0 : _id.hashCode());
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
 		return result;
 	}
 
@@ -639,12 +731,12 @@ public class Question implements Serializable {
 		if (getClass() != obj.getClass()) {
 			return false;
 		}
-		Question other = (Question) obj;
-		if (_id == null) {
-			if (other._id != null) {
+		Content other = (Content) obj;
+		if (id == null) {
+			if (other.id != null) {
 				return false;
 			}
-		} else if (!_id.equals(other._id)) {
+		} else if (!id.equals(other.id)) {
 			return false;
 		}
 		return true;
diff --git a/src/main/java/de/thm/arsnova/entities/DbUser.java b/src/main/java/de/thm/arsnova/entities/DbUser.java
index 75d9ba712d373b262cd2c36e9553b655c8ac89bc..0bdc45121c35f9975ae0cb33fba49d53cdcf0fd7 100644
--- a/src/main/java/de/thm/arsnova/entities/DbUser.java
+++ b/src/main/java/de/thm/arsnova/entities/DbUser.java
@@ -17,10 +17,13 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 /**
  * A user account for ARSnova's own registration and login process.
  */
-public class DbUser {
+public class DbUser implements Entity {
 	private String id;
 	private String rev;
 	private String username;
@@ -31,90 +34,93 @@ public class DbUser {
 	private long creation;
 	private long lastLogin;
 
+	@JsonView(View.Persistence.class)
 	public String getId() {
 		return id;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setId(String id) {
 		this.id = id;
 	}
 
-	/* CouchDB deserialization */
-	public void set_id(String id) {
-		this.id = id;
-	}
-
+	@JsonView(View.Persistence.class)
 	public String getRev() {
 		return rev;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setRev(String rev) {
 		this.rev = rev;
 	}
 
-	/* CouchDB deserialization */
-	public void set_rev(String rev) {
-		this.rev = rev;
-	}
-
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getUsername() {
 		return username;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setUsername(String username) {
 		this.username = username;
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getPassword() {
 		return password;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setPassword(String password) {
 		this.password = password;
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getActivationKey() {
 		return activationKey;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setActivationKey(String activationKey) {
 		this.activationKey = activationKey;
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getPasswordResetKey() {
 		return passwordResetKey;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setPasswordResetKey(String passwordResetKey) {
 		this.passwordResetKey = passwordResetKey;
 	}
 
+	@JsonView(View.Persistence.class)
 	public long getPasswordResetTime() {
 		return passwordResetTime;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setPasswordResetTime(long passwordResetTime) {
 		this.passwordResetTime = passwordResetTime;
 	}
 
+	@JsonView(View.Persistence.class)
 	public long getCreation() {
 		return creation;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setCreation(long creation) {
 		this.creation = creation;
 	}
 
+	@JsonView(View.Persistence.class)
 	public long getLastLogin() {
 		return lastLogin;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setLastLogin(long lastLogin) {
 		this.lastLogin = lastLogin;
 	}
-
-	/* CouchDB deserialization */
-	public void setType(String type) {
-		/* no op */
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/entities/Entity.java b/src/main/java/de/thm/arsnova/entities/Entity.java
new file mode 100644
index 0000000000000000000000000000000000000000..4de04ab4b7d1923cbfce3a50649e1af520db468f
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/Entity.java
@@ -0,0 +1,31 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
+public interface Entity {
+	String getId();
+	void setId(String id);
+
+	@JsonView(View.Persistence.class)
+	default Class<? extends Entity> getType() {
+		return getClass();
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/Feedback.java b/src/main/java/de/thm/arsnova/entities/Feedback.java
index c50480f094e537aed33f579f4d71f94e1ed83313..dcccb924cfa4e7a713abd310128ccf6fab8bc707 100644
--- a/src/main/java/de/thm/arsnova/entities/Feedback.java
+++ b/src/main/java/de/thm/arsnova/entities/Feedback.java
@@ -17,6 +17,9 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,6 +45,7 @@ public class Feedback {
 		values.add(d);
 	}
 
+	@JsonView(View.Public.class)
 	public final List<Integer> getValues() {
 		return values;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java b/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java
index 4d682884a1b50bb7e7f60077bc92c710a0f854da..342d2c29ae9fda8142356792dadd863211d1b9ee 100644
--- a/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java
+++ b/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -41,19 +43,23 @@ public class LearningProgressOptions implements Serializable {
 	public LearningProgressOptions() { }
 
 	@ApiModelProperty(required = true, value = "the type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getType() {
 		return type;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setType(String learningProgressType) {
 		this.type = learningProgressType;
 	}
 
 	@ApiModelProperty(required = true, value = "either lecture or preparation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getQuestionVariant() {
 		return questionVariant;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setQuestionVariant(String questionVariant) {
 		this.questionVariant = questionVariant;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/LogEntry.java b/src/main/java/de/thm/arsnova/entities/LogEntry.java
index d4e20cc1706675d4d8c9aeebfa3a88d3fc2918d3..f317f053bae15ae5205374b37339750d402d5846 100644
--- a/src/main/java/de/thm/arsnova/entities/LogEntry.java
+++ b/src/main/java/de/thm/arsnova/entities/LogEntry.java
@@ -17,9 +17,13 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 import java.util.Map;
 
-public class LogEntry {
+public class LogEntry implements Entity {
 	public enum LogLevel {
 		TRACE,
 		DEBUG,
@@ -31,69 +35,78 @@ public class LogEntry {
 
 	private String id;
 	private String rev;
-	private long timestamp;
+	private long timestamp = System.currentTimeMillis();
 	private String event;
 	private int level;
 	private Map<String, Object> payload;
 
-	public String getId() {
-		return id;
+	public LogEntry(@JsonProperty String event, @JsonProperty int level, @JsonProperty Map<String, Object> payload) {
+		this.event = event;
+		this.level = level;
+		this.payload = payload;
 	}
 
-	public void setId(String id) {
-		this.id = id;
+	@JsonView(View.Persistence.class)
+	public String getId() {
+		return id;
 	}
 
-	/* CouchDB deserialization */
-	public void set_id(String id) {
+	@JsonView(View.Persistence.class)
+	public void setId(final String id) {
 		this.id = id;
 	}
 
-	public String getRev() {
-		return rev;
-	}
-
-	public void setRev(String rev) {
+	@JsonView(View.Persistence.class)
+	public void setRevision(final String rev) {
 		this.rev = rev;
 	}
 
-	/* CouchDB deserialization */
-	public void set_rev(String rev) {
-		this.rev = rev;
+	@JsonView(View.Persistence.class)
+	public String getRevision() {
+		return rev;
 	}
 
+	@JsonView(View.Persistence.class)
 	public long getTimestamp() {
 		return timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setTimestamp(long timestamp) {
 		this.timestamp = timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getEvent() {
 		return event;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setEvent(String event) {
 		this.event = event;
 	}
 
+	@JsonView(View.Persistence.class)
 	public int getLevel() {
 		return level;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setLevel(int level) {
 		this.level = level;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setLevel(LogLevel level) {
 		this.level = level.ordinal();
 	}
 
+	@JsonView(View.Persistence.class)
 	public Map<String, Object> getPayload() {
 		return payload;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setPayload(Map<String, Object> payload) {
 		this.payload = payload;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/LoggedIn.java b/src/main/java/de/thm/arsnova/entities/LoggedIn.java
index 69f0eebf187547c57c016cb4fc49bc08042db0fd..cb9982c88fe24ee2219a6bb5114f1e259639a8df 100644
--- a/src/main/java/de/thm/arsnova/entities/LoggedIn.java
+++ b/src/main/java/de/thm/arsnova/entities/LoggedIn.java
@@ -17,25 +17,24 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Once a user joins a session, this class is used to identify a returning user.
  */
-public class LoggedIn {
-
-	private String _id;
-	private String _rev;
-	private String type;
+public class LoggedIn implements Entity {
+	private String id;
+	private String rev;
 	private String user;
 	private String sessionId;
 	private long timestamp;
 	private List<VisitedSession> visitedSessions = new ArrayList<>();
-	private List<String> _conflicts;
 
 	public LoggedIn() {
-		this.type = "logged_in";
 		this.updateTimestamp();
 	}
 
@@ -47,88 +46,80 @@ public class LoggedIn {
 
 	private boolean isAlreadyVisited(Session s) {
 		for (VisitedSession vs : this.visitedSessions) {
-			if (vs.get_id().equals(s.get_id())) {
+			if (vs.getId().equals(s.getId())) {
 				return true;
 			}
 		}
 		return false;
 	}
 
-	public void updateTimestamp() {
-		this.timestamp = System.currentTimeMillis();
-	}
-
-	public String get_id() {
-		return _id;
-	}
-
-	public void set_id(String _id) {
-		this._id = _id;
+	@JsonView(View.Persistence.class)
+	public String getId() {
+		return id;
 	}
 
-	public String get_rev() {
-		return _rev;
+	@JsonView(View.Persistence.class)
+	public void setId(final String id) {
+		this.id = id;
 	}
 
-	public void set_rev(String _rev) {
-		this._rev = _rev;
+	@JsonView(View.Persistence.class)
+	public String getRevision() {
+		return rev;
 	}
 
-	public String getType() {
-		return type;
+	@JsonView(View.Persistence.class)
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
-	public void setType(String type) {
-		this.type = type;
+	public void updateTimestamp() {
+		this.timestamp = System.currentTimeMillis();
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getUser() {
 		return user;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setUser(String user) {
 		this.user = user;
 	}
 
+	@JsonView(View.Persistence.class)
 	public String getSessionId() {
 		return sessionId;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setSessionId(String sessionId) {
 		this.sessionId = sessionId;
 	}
 
+	@JsonView(View.Persistence.class)
 	public long getTimestamp() {
 		return timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setTimestamp(long timestamp) {
 		this.timestamp = timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public List<VisitedSession> getVisitedSessions() {
 		return visitedSessions;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setVisitedSessions(List<VisitedSession> visitedSessions) {
 		this.visitedSessions = visitedSessions;
 	}
 
-	public List<String> get_conflicts() {
-		return _conflicts;
-	}
-
-	public void set_conflicts(List<String> _conflicts) {
-		this._conflicts = _conflicts;
-	}
-
-	public boolean hasConflicts() {
-		return !(_conflicts == null || _conflicts.isEmpty());
-	}
-
 	@Override
 	public String toString() {
-		return "LoggedIn [_id=" + _id + ", _rev=" + _rev + ", type=" + type
+		return "LoggedIn [id=" + id + ", rev=" + rev + ", type=" + getType()
 				+ ", user=" + user + ", sessionId=" + sessionId
 				+ ", timestamp=" + timestamp + ", visitedSessions="
 				+ visitedSessions + "]";
diff --git a/src/main/java/de/thm/arsnova/entities/Motd.java b/src/main/java/de/thm/arsnova/entities/Motd.java
index 35864703d31170f44226a2ffbeba76f8d8607685..1b4ea1d73d9b604abe74f995c3057ffad83d392b 100644
--- a/src/main/java/de/thm/arsnova/entities/Motd.java
+++ b/src/main/java/de/thm/arsnova/entities/Motd.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -26,7 +28,7 @@ import java.util.Date;
  * This class represents a message of the day.
  */
 @ApiModel(value = "motd", description = "the message of the day entity")
-public class Motd {
+public class Motd implements Entity {
 
 	private String motdkey; //ID
 	private Date startdate;
@@ -36,95 +38,115 @@ public class Motd {
 	private String audience;
 	private String sessionId;
 	private String sessionkey;
-	private String _id;
-	private String _rev;
+	private String id;
+	private String rev;
 
 	@ApiModelProperty(required = true, value = "the identification string")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getMotdkey() {
 		return motdkey;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setMotdkey(final String key) {
 		motdkey = key;
 	}
 
 	@ApiModelProperty(required = true, value = "startdate for showing this message (timestamp format)")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public Date getStartdate() {
 		return startdate;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setStartdate(final Date timestamp) {
 		startdate = timestamp;
 	}
 
 	@ApiModelProperty(required = true, value = "enddate for showing this message (timestamp format)")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public Date getEnddate() {
 		return enddate;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setEnddate(final Date timestamp) {
 		enddate = timestamp;
 	}
 
 	@ApiModelProperty(required = true, value = "tite of the message")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getTitle() {
 		return title;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTitle(final String ttitle) {
 		title = ttitle;
 	}
 
 	@ApiModelProperty(required = true, value = "text of the message")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getText() {
 		return text;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setText(final String ttext) {
 		text = ttext;
 	}
 
 	@ApiModelProperty(required = true, value = "defines the target audience for this motd (one of the following: 'student', 'tutor', 'loggedIn', 'all')")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getAudience() {
 		return audience;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAudience(String a) {
 		audience = a;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSessionId() {
 		return sessionId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSessionId(String sessionId) {
 		this.sessionId = sessionId;
 	}
 
 	@ApiModelProperty(required = true, value = "when audience equals session, the sessionkey referes to the session the messages belong to")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSessionkey() {
 		return sessionkey;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSessionkey(String a) {
 		sessionkey = a;
 	}
 
 	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public String get_id() {
-		return _id;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
 	}
 
-	public void set_id(final String id) {
-		_id = id;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
 	}
 
-	public void set_rev(final String rev) {
-		_rev = rev;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
-	public String get_rev() {
-		return _rev;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/entities/MotdList.java b/src/main/java/de/thm/arsnova/entities/MotdList.java
index 307fb6696398d1b95e94b581f9a389e48960fae8..35b611fee0c03f8f62f5201d982b5cf645ae08ac 100644
--- a/src/main/java/de/thm/arsnova/entities/MotdList.java
+++ b/src/main/java/de/thm/arsnova/entities/MotdList.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -24,45 +26,52 @@ import io.swagger.annotations.ApiModelProperty;
  * This class represents a list of motdkeys for a user.
  */
 @ApiModel(value = "motdlist", description = "the motdlist to save the messages a user has confirmed to be read")
-public class MotdList {
-
+public class MotdList implements Entity {
+	private String id;
+	private String rev;
 	private String motdkeys;
 	private String username;
-	private String _id;
-	private String _rev;
+
+	@ApiModelProperty(required = true, value = "the couchDB ID")
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
+	}
 
 	@ApiModelProperty(required = true, value = "the motdkeylist")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getMotdkeys() {
 		return motdkeys;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setMotdkeys(String motds) {
 		motdkeys = motds;
 	}
 
 	@ApiModelProperty(required = true, value = "the username")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getUsername() {
 		return username;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setUsername(final String u) {
 		username = u;
 	}
-
-	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public String get_id() {
-		return _id;
-	}
-
-	public void set_id(final String id) {
-		_id = id;
-	}
-
-	public void set_rev(final String rev) {
-		_rev = rev;
-	}
-
-	public String get_rev() {
-		return _rev;
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java b/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
index 0b7fa7983f2dfadf0a92c1ea9e32002e5b87a995..f3bfb039ade4d7ed66a4d8e8b13125004e01c1cf 100644
--- a/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
+++ b/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -43,28 +45,34 @@ public class PossibleAnswer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "the text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getText() {
 		return text;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setText(String text) {
 		this.text = text;
 	}
 
 	@ApiModelProperty(required = true, value = "true for a correct answer")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isCorrect() {
 		return correct;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCorrect(boolean correct) {
 		this.correct = correct;
 	}
 
 	@ApiModelProperty(required = true, value = "the value")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getValue() {
 		return value;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setValue(int value) {
 		this.value = value;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/ServiceDescription.java b/src/main/java/de/thm/arsnova/entities/ServiceDescription.java
index 421cc20471345e4483acbbec26dc4dbd8140657a..96d1251788186cb93ead776b33be90f634f55ad2 100644
--- a/src/main/java/de/thm/arsnova/entities/ServiceDescription.java
+++ b/src/main/java/de/thm/arsnova/entities/ServiceDescription.java
@@ -17,6 +17,9 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 /**
  * A login service description. For example, this class is used to display the login buttons in ARSnova mobile.
  */
@@ -51,6 +54,7 @@ public class ServiceDescription {
 		}
 	}
 
+	@JsonView(View.Public.class)
 	public String getId() {
 		return id;
 	}
@@ -59,6 +63,7 @@ public class ServiceDescription {
 		this.id = id;
 	}
 
+	@JsonView(View.Public.class)
 	public String getName() {
 		return name;
 	}
@@ -67,6 +72,7 @@ public class ServiceDescription {
 		this.name = name;
 	}
 
+	@JsonView(View.Public.class)
 	public String getDialogUrl() {
 		return dialogUrl;
 	}
@@ -75,6 +81,7 @@ public class ServiceDescription {
 		this.dialogUrl = dialogUrl;
 	}
 
+	@JsonView(View.Public.class)
 	public String getImage() {
 		return image;
 	}
@@ -83,6 +90,7 @@ public class ServiceDescription {
 		this.image = image;
 	}
 
+	@JsonView(View.Public.class)
 	public int getOrder() {
 		return order;
 	}
@@ -91,6 +99,7 @@ public class ServiceDescription {
 		this.order = order;
 	}
 
+	@JsonView(View.Public.class)
 	public String[] getAllowedRoles() {
 		return allowedRoles;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/Session.java b/src/main/java/de/thm/arsnova/entities/Session.java
index 28ad60ecc01caaae4b8230bdd6fd1085d10f5628..65bf6c6263075a06a9f40260e202ea676a88dea1 100644
--- a/src/main/java/de/thm/arsnova/entities/Session.java
+++ b/src/main/java/de/thm/arsnova/entities/Session.java
@@ -18,21 +18,18 @@
 package de.thm.arsnova.entities;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
-import java.io.Serializable;
-import java.util.List;
-
 /**
  * Represents an ARSnova session.
  */
 @ApiModel(value = "session", description = "the session entity")
-public class Session implements Serializable {
-
-	private static final long serialVersionUID = 1L;
-
-	private String type;
+public class Session implements Entity {
+	private String id;
+	private String rev;
 	private String name;
 	private String shortName;
 	private String keyword;
@@ -41,7 +38,6 @@ public class Session implements Serializable {
 	private long lastOwnerActivity;
 	private String courseType;
 	private String courseId;
-	private List<String> _conflicts;
 	private long creationTime;
 	private LearningProgressOptions learningProgressOptions = new LearningProgressOptions();
 	private SessionFeature features = new SessionFeature();
@@ -59,9 +55,6 @@ public class Session implements Serializable {
 	private boolean feedbackLock;
 	private boolean flipFlashcards;
 
-	private String _id;
-	private String _rev;
-
 	/**
 	 * Returns a copy of the given session without any information that identifies a person.
 	 * @param original The session to create a anonymized copy of
@@ -69,7 +62,6 @@ public class Session implements Serializable {
 	 */
 	public static Session anonymizedCopy(final Session original) {
 		final Session copy = new Session();
-		copy.type = original.type;
 		copy.name = original.name;
 		copy.shortName = original.shortName;
 		copy.keyword = original.keyword;
@@ -95,118 +87,119 @@ public class Session implements Serializable {
 		copy.feedbackLock = original.feedbackLock;
 		copy.flipFlashcards = original.flipFlashcards;
 
-		copy._id = original._id;
-		copy._rev = original._rev;
+		copy.id = original.id;
+		copy.rev = original.rev;
 		return copy;
 	}
 
-	@ApiModelProperty(required = true, value = "\"session\" - used to filter in the couchDB")
-	public String getType() {
-		return type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
-	public void setType(final String type) {
-		this.type = type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
 	@ApiModelProperty(required = true, value = "the name")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getName() {
 		return name;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setName(final String name) {
 		this.name = name;
 	}
 
 	@ApiModelProperty(required = true, value = "the short name")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getShortName() {
 		return shortName;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setShortName(final String shortName) {
 		this.shortName = shortName;
 	}
 
 	@ApiModelProperty(required = true, value = "the keyword")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getKeyword() {
 		return keyword;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setKeyword(final String keyword) {
 		this.keyword = keyword;
 	}
 
 	@ApiModelProperty(required = true, value = "the session creator")
+	@JsonView(View.Persistence.class)
 	public String getCreator() {
 		return creator;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setCreator(final String creator) {
 		this.creator = creator;
 	}
 
 	@ApiModelProperty(required = true, value = "true for active session")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isActive() {
 		return active;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setActive(final boolean active) {
 		this.active = active;
 	}
 
 	@ApiModelProperty(required = true, value = "timestamp from the last activity of the owner")
+	@JsonView(View.Persistence.class)
 	public long getLastOwnerActivity() {
 		return lastOwnerActivity;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setLastOwnerActivity(final long lastOwnerActivity) {
 		this.lastOwnerActivity = lastOwnerActivity;
 	}
 
-	public void set_id(final String id) {
-		_id = id;
-	}
-
-	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public String get_id() {
-		return _id;
-	}
-
-	public void set_rev(final String rev) {
-		_rev = rev;
-	}
-
-	public String get_rev() {
-		return _rev;
-	}
-
-	public void set_conflicts(final List<String> conflicts) {
-		_conflicts = conflicts;
-	}
-
-	@ApiModelProperty(required = true, value = "potential couchDB conflicts")
-	public List<String> get_conflicts() {
-		return _conflicts;
-	}
-
 	public boolean isCreator(final User user) {
 		return user.getUsername().equals(creator);
 	}
 
 	@ApiModelProperty(required = true, value = "the source the course comes from (example: moodle)")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getCourseType() {
 		return courseType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCourseType(final String courseType) {
 		this.courseType = courseType;
 	}
 
 	@ApiModelProperty(required = true, value = "the course ID")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getCourseId() {
 		return courseId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCourseId(final String courseId) {
 		this.courseId = courseId;
 	}
@@ -217,143 +210,173 @@ public class Session implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "creation timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getCreationTime() {
 		return creationTime;
 	}
 
+	@JsonView(View.Persistence.class)
 	public void setCreationTime(long creationTime) {
 		this.creationTime = creationTime;
 	}
 
 	@ApiModelProperty(required = true, value = "the learning progress options")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public LearningProgressOptions getLearningProgressOptions() {
 		return learningProgressOptions;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setLearningProgressOptions(LearningProgressOptions learningProgressOptions) {
 		this.learningProgressOptions = learningProgressOptions;
 	}
 
 	@ApiModelProperty(required = true, value = "the enabled features (e.g. feedback, interposed, learning Progress, lecture)")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public SessionFeature getFeatures() {
 		return features;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFeatures(SessionFeature features) {
 		this.features = features;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool author name")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpAuthorName() {
 		return ppAuthorName;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpAuthorName(final String ppAuthorName) {
 		this.ppAuthorName = ppAuthorName;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool author email")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpAuthorMail() {
 		return ppAuthorMail;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpAuthorMail(final String ppAuthorMail) {
 		this.ppAuthorMail = ppAuthorMail;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool university")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpUniversity() {
 		return ppUniversity;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpUniversity(final String ppUniversity) {
 		this.ppUniversity = ppUniversity;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool logo")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpLogo() {
 		return ppLogo;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpLogo(final String ppLogo) {
 		this.ppLogo = ppLogo;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display subject")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpSubject() {
 		return ppSubject;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpSubject(final String ppSubject) {
 		this.ppSubject = ppSubject;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool license")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpLicense() {
 		return ppLicense;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpLicense(final String ppLicense) {
 		this.ppLicense = ppLicense;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool description")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpDescription() {
 		return ppDescription;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpDescription(final String ppDescription) {
 		this.ppDescription = ppDescription;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool faculty")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpFaculty() {
 		return ppFaculty;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpFaculty(final String ppFaculty) {
 		this.ppFaculty = ppFaculty;
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool level")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getPpLevel() {
 		return ppLevel;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPpLevel(final String ppLevel) {
 		this.ppLevel = ppLevel;
 	}
 
 	@ApiModelProperty(required = true, value = "the session type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSessionType() {
 		return sessionType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSessionType(final String sessionType) {
 		this.sessionType = sessionType;
 	}
 
 	@ApiModelProperty(required = true, value = "the feedback lock status")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getFeedbackLock() {
 		return feedbackLock;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFeedbackLock(Boolean lock) {
 		this.feedbackLock = lock;
 	}
 
 	@ApiModelProperty(required = true, value = "the flashcard flip condition")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getFlipFlashcards() {
 		return flipFlashcards;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFlipFlashcards(Boolean flip) {
 		this.flipFlashcards = flip;
 	}
 
 	@Override
 	public String toString() {
-		return "Session [keyword=" + keyword + ", type=" + type + ", creator=" + creator + "]";
+		return "Session [keyword=" + keyword + ", type=" + getType() + ", creator=" + creator + "]";
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/entities/SessionFeature.java b/src/main/java/de/thm/arsnova/entities/SessionFeature.java
index fd3ac40f56f552fff63c4fe2ac34bfb5ef02fb26..e04390add673beb3c89c54e8a85bde3cd7d1dc2e 100644
--- a/src/main/java/de/thm/arsnova/entities/SessionFeature.java
+++ b/src/main/java/de/thm/arsnova/entities/SessionFeature.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -72,143 +74,177 @@ public class SessionFeature implements Serializable {
 
 	public SessionFeature() { }
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isLecture() {
 		return lecture;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setLecture(boolean lecture) {
 		this.lecture = lecture;
 	}
 
 	@ApiModelProperty(required = true, value = "jitt")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isJitt() {
 		return jitt;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setJitt(boolean jitt) {
 		this.jitt = jitt;
 	}
 
 	@ApiModelProperty(required = true, value = "feedback")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isFeedback() {
 		return feedback;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFeedback(boolean feedback) {
 		this.feedback = feedback;
 	}
 
 	@ApiModelProperty(required = true, value = "interposed")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isInterposed() {
 		return interposed;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setInterposed(boolean interposed) {
 		this.interposed = interposed;
 	}
 
 	@ApiModelProperty(required = true, value = "peer instruction")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isPi() {
 		return pi;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPi(boolean pi) {
 		this.pi = pi;
 	}
 
 	@ApiModelProperty(required = true, value = "learning progress")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isLearningProgress() {
 		return learningProgress;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setLearningProgress(boolean learningProgress) {
 		this.learningProgress = learningProgress;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isCustom() {
 		return custom;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCustom(boolean custom) {
 		this.custom = custom;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isClicker() {
 		return clicker;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setClicker(boolean clicker) {
 		this.clicker = clicker;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isPeerGrading() {
 		return peerGrading;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPeerGrading(boolean peerGrading) {
 		this.peerGrading = peerGrading;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isFlashcardFeature() {
 		return flashcardFeature;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFlashcardFeature(boolean flashcardFeature) {
 		this.flashcardFeature = flashcardFeature;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isFlashcard() {
 		return flashcard;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFlashcard(boolean flashcard) {
 		this.flashcard = flashcard;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isTotal() {
 		return total;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTotal(boolean total) {
 		this.total = total;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isLiveFeedback() {
 		return liveFeedback;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setLiveFeedback(boolean liveFeedback) {
 		this.liveFeedback = liveFeedback;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isInterposedFeedback() {
 		return interposedFeedback;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setInterposedFeedback(boolean interposedFeedback) {
 		this.interposedFeedback = interposedFeedback;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isLiveClicker() {
 		return liveClicker;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setLiveClicker(boolean liveClicker) {
 		this.liveClicker = liveClicker;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isTwitterWall() {
 		return twitterWall;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTwitterWall(boolean twitterWall) {
 		this.twitterWall = twitterWall;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isSlides() {
 		return slides;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSlides(boolean slides) {
 		this.slides = slides;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/SessionInfo.java b/src/main/java/de/thm/arsnova/entities/SessionInfo.java
index e34227862f8f815e53779cd5cd7ef8fd3223ab9c..4c498982e38f7e65f88e2c1702f8b6f7722b6996 100644
--- a/src/main/java/de/thm/arsnova/entities/SessionInfo.java
+++ b/src/main/java/de/thm/arsnova/entities/SessionInfo.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -42,8 +44,8 @@ public class SessionInfo {
 
 	private int numQuestions;
 	private int numAnswers;
-	private int numInterposed;
-	private int numUnredInterposed;
+	private int numComments;
+	private int numUnreadComments;
 	private int numUnanswered;
 
 	public SessionInfo(Session session) {
@@ -69,6 +71,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the name")
+	@JsonView(View.Public.class)
 	public String getName() {
 		return name;
 	}
@@ -78,6 +81,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the short name")
+	@JsonView(View.Public.class)
 	public String getShortName() {
 		return shortName;
 	}
@@ -87,6 +91,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the keyword")
+	@JsonView(View.Public.class)
 	public String getKeyword() {
 		return keyword;
 	}
@@ -96,6 +101,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "true for active")
+	@JsonView(View.Public.class)
 	public boolean isActive() {
 		return active;
 	}
@@ -105,6 +111,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the source the course comes from (example: moodle)")
+	@JsonView(View.Public.class)
 	public String getCourseType() {
 		return courseType;
 	}
@@ -114,6 +121,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the session type")
+	@JsonView(View.Public.class)
 	public String getSessionType() {
 		return sessionType;
 	}
@@ -123,6 +131,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display level")
+	@JsonView(View.Public.class)
 	public String getPpLevel() {
 		return ppLevel;
 	}
@@ -132,6 +141,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the public pool subject")
+	@JsonView(View.Public.class)
 	public String getPpSubject() {
 		return ppSubject;
 	}
@@ -141,6 +151,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of questions")
+	@JsonView(View.Public.class)
 	public int getNumQuestions() {
 		return numQuestions;
 	}
@@ -150,6 +161,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of answers")
+	@JsonView(View.Public.class)
 	public int getNumAnswers() {
 		return numAnswers;
 	}
@@ -158,16 +170,20 @@ public class SessionInfo {
 		this.numAnswers = numAnswers;
 	}
 
-	@ApiModelProperty(required = true, value = "used to display interposed number")
+	/* Still named "Interposed" instead of "Comments" here for compatibilty reasons. */
+	@ApiModelProperty(required = true, value = "used to display comment number")
+	@JsonView(View.Public.class)
 	public int getNumInterposed() {
-		return numInterposed;
+		return numComments;
 	}
 
-	public void setNumInterposed(int numInterposed) {
-		this.numInterposed = numInterposed;
+	/* Still named "Interposed" instead of "Comments" here for compatibilty reasons. */
+	public void setNumInterposed(int numComments) {
+		this.numComments = numComments;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of unanswered questions")
+	@JsonView(View.Public.class)
 	public int getNumUnanswered() {
 		return numUnanswered;
 	}
@@ -177,6 +193,7 @@ public class SessionInfo {
 	}
 
 	@ApiModelProperty(required = true, value = "the creation timestamp")
+	@JsonView(View.Public.class)
 	public long getCreationTime() {
 		return creationTime;
 	}
@@ -185,13 +202,16 @@ public class SessionInfo {
 		this.creationTime = creationTime;
 	}
 
-	@ApiModelProperty(required = true, value = "the number of unread interposed questions")
+	/* Still named "Interposed" instead of "Comments" here for compatibilty reasons. */
+	@ApiModelProperty(required = true, value = "the number of unread comments")
+	@JsonView(View.Public.class)
 	public int getNumUnredInterposed() {
-		return numUnredInterposed;
+		return numUnreadComments;
 	}
 
-	public void setNumUnredInterposed(int numUnredInterposed) {
-		this.numUnredInterposed = numUnredInterposed;
+	/* Still named "Interposed" instead of "Comments" here for compatibilty reasons. */
+	public void setNumUnredInterposed(int numUnreadComments) {
+		this.numUnreadComments = numUnreadComments;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/entities/Statistics.java b/src/main/java/de/thm/arsnova/entities/Statistics.java
index 49f9179730fea33687d094bc932a939409f442d0..c0635f0200b85b0b6baa50939d88e651d847a5de 100644
--- a/src/main/java/de/thm/arsnova/entities/Statistics.java
+++ b/src/main/java/de/thm/arsnova/entities/Statistics.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -40,6 +42,7 @@ public class Statistics {
 	private int flashcards;
 
 	@ApiModelProperty(required = true, value = "the number of answers")
+	@JsonView(View.Public.class)
 	public int getAnswers() {
 		return answers;
 	}
@@ -49,6 +52,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of lecture questions")
+	@JsonView(View.Public.class)
 	public int getLectureQuestions() {
 		return lectureQuestions;
 	}
@@ -58,6 +62,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of prepartion uestions")
+	@JsonView(View.Public.class)
 	public int getPreparationQuestions() {
 		return preparationQuestions;
 	}
@@ -67,11 +72,13 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the total number of questions")
+	@JsonView(View.Public.class)
 	public int getQuestions() {
 		return getLectureQuestions() + getPreparationQuestions();
 	}
 
 	@ApiModelProperty(required = true, value = "the number of open sessions")
+	@JsonView(View.Public.class)
 	public int getOpenSessions() {
 		return openSessions;
 	}
@@ -81,6 +88,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of closed Sessions")
+	@JsonView(View.Public.class)
 	public int getClosedSessions() {
 		return closedSessions;
 	}
@@ -90,11 +98,13 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the total number of Sessions")
+	@JsonView(View.Public.class)
 	public int getSessions() {
 		return getOpenSessions() + getClosedSessions();
 	}
 
 	@ApiModelProperty(required = true, value = "used to display Active Users")
+	@JsonView(View.Public.class)
 	public int getActiveUsers() {
 		return activeUsers;
 	}
@@ -104,6 +114,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of users that are logged")
+	@JsonView(View.Public.class)
 	public int getLoggedinUsers() {
 		return loggedinUsers;
 	}
@@ -113,6 +124,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of interposed Questions")
+	@JsonView(View.Public.class)
 	public int getInterposedQuestions() {
 		return interposedQuestions;
 	}
@@ -122,6 +134,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of flashcards")
+	@JsonView(View.Public.class)
 	public int getFlashcards() {
 		return flashcards;
 	}
@@ -131,6 +144,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of creators")
+	@JsonView(View.Public.class)
 	public int getCreators() {
 		return creators;
 	}
@@ -140,6 +154,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of concept Questions")
+	@JsonView(View.Public.class)
 	public int getConceptQuestions() {
 		return conceptQuestions;
 	}
@@ -149,6 +164,7 @@ public class Statistics {
 	}
 
 	@ApiModelProperty(required = true, value = "the number of active Students")
+	@JsonView(View.Public.class)
 	public int getActiveStudents() {
 		return activeStudents;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/User.java b/src/main/java/de/thm/arsnova/entities/User.java
index c3ed9bf78650a2236a06631794b71ddf6204900a..9d4729d44887ce9c8024b57300c31618db6ed1e0 100644
--- a/src/main/java/de/thm/arsnova/entities/User.java
+++ b/src/main/java/de/thm/arsnova/entities/User.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import de.thm.arsnova.services.UserSessionService;
 import org.jasig.cas.client.authentication.AttributePrincipal;
 import org.pac4j.oauth.profile.facebook.FacebookProfile;
@@ -76,6 +78,7 @@ public class User implements Serializable {
 		setType(LDAP);
 	}
 
+	@JsonView(View.Public.class)
 	public String getUsername() {
 		return username;
 	}
@@ -84,6 +87,7 @@ public class User implements Serializable {
 		this.username = username;
 	}
 
+	@JsonView(View.Public.class)
 	public String getType() {
 		return type;
 	}
@@ -108,6 +112,7 @@ public class User implements Serializable {
 		this.isAdmin = a;
 	}
 
+	@JsonView(View.Public.class)
 	public boolean isAdmin() {
 		return this.isAdmin;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/VisitedSession.java b/src/main/java/de/thm/arsnova/entities/VisitedSession.java
index 166496fc043f76de916dcb52bdc8f80aaf85c00c..3566b5f942d3636f62293f97daca6cb86c2f54ca 100644
--- a/src/main/java/de/thm/arsnova/entities/VisitedSession.java
+++ b/src/main/java/de/thm/arsnova/entities/VisitedSession.java
@@ -17,11 +17,14 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
+
 /**
  * A session a user has visited previously.
  */
 public class VisitedSession {
-	private String _id;
+	private String id;
 	private String name;
 	private String keyword;
 
@@ -29,38 +32,44 @@ public class VisitedSession {
 	}
 
 	public VisitedSession(Session s) {
-		this._id = s.get_id();
+		this.id = s.getId();
 		this.name = s.getName();
 		this.keyword = s.getKeyword();
 	}
 
-	public String get_id() {
-		return _id;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
 	}
 
-	public void set_id(String _id) {
-		this._id = _id;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getName() {
 		return name;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setName(String name) {
 		this.name = name;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getKeyword() {
 		return keyword;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setKeyword(String keyword) {
 		this.keyword = keyword;
 	}
 
 	@Override
 	public String toString() {
-		return "VisitedSession [_id=" + _id + ", name=" + name + ", keyword="
+		return "VisitedSession [id=" + id + ", name=" + name + ", keyword="
 				+ keyword + "]";
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentMixIn.java b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentMixIn.java
new file mode 100644
index 0000000000000000000000000000000000000000..6928dad0589ea1452f69ec7f1c7ea9fe383639f2
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentMixIn.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities.serialization;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import de.thm.arsnova.entities.Entity;
+
+@JsonIgnoreProperties(value = {"type"}, allowGetters = true)
+public abstract class CouchDbDocumentMixIn {
+	@JsonProperty("_id")
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	abstract String getId();
+
+	@JsonProperty("_id") abstract void setId(String id);
+
+	@JsonProperty("_rev")
+	@JsonInclude(JsonInclude.Include.NON_EMPTY)
+	abstract String getRevision();
+
+	@JsonProperty("_rev") abstract String setRevision(String rev);
+
+	@JsonSerialize(converter = CouchDbTypeFieldConverter.class)
+	abstract Class<? extends Entity> getType();
+}
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentModule.java b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..1579458a207c33d2e98d12d71da3b337bc7d96e5
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbDocumentModule.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities.serialization;
+
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import de.thm.arsnova.entities.Entity;
+
+public class CouchDbDocumentModule extends SimpleModule {
+	public CouchDbDocumentModule() {
+		super("CouchDbDocumentModule");
+	}
+
+	@Override
+	public void setupModule(SetupContext context) {
+		context.setMixInAnnotations(Entity.class, CouchDbDocumentMixIn.class);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbObjectMapperFactory.java b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbObjectMapperFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..51f822800d8729015f5ffbe2a7e24f6c5576f509
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbObjectMapperFactory.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities.serialization;
+
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.impl.StdObjectMapperFactory;
+
+public class CouchDbObjectMapperFactory extends StdObjectMapperFactory {
+	public ObjectMapper createObjectMapper(CouchDbConnector connector) {
+		ObjectMapper om = super.createObjectMapper(connector);
+		om.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
+		om.setConfig(om.getSerializationConfig().withView(View.Persistence.class));
+		om.registerModule(new CouchDbDocumentModule());
+
+		return om;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b2199fef199ea1c1b1e454a658c35d9770074e9
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities.serialization;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.Converter;
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.entities.Entity;
+import de.thm.arsnova.entities.LogEntry;
+import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.MotdList;
+import de.thm.arsnova.entities.Session;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CouchDbTypeFieldConverter implements Converter<Class<? extends Entity>, String> {
+	private static final Map<Class<? extends Entity>, String> typeMapping = new HashMap<>();
+
+	{
+		typeMapping.put(LogEntry.class, "log");
+		typeMapping.put(DbUser.class, "userdetails");
+		typeMapping.put(Motd.class, "motd");
+		typeMapping.put(MotdList.class, "motdlist");
+		typeMapping.put(Session.class, "session");
+		typeMapping.put(Comment.class, "interposed_question");
+		typeMapping.put(Content.class, "skill_question");
+		typeMapping.put(Answer.class, "skill_question_answer");
+	}
+
+	@Override
+	public String convert(Class<? extends Entity> aClass) {
+		return typeMapping.get(aClass);
+	}
+
+	@Override
+	public JavaType getInputType(TypeFactory typeFactory) {
+		return typeFactory.constructGeneralizedType(typeFactory.constructType(Class.class), Entity.class);
+	}
+
+	@Override
+	public JavaType getOutputType(TypeFactory typeFactory) {
+		return typeFactory.constructType(String.class);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/View.java b/src/main/java/de/thm/arsnova/entities/serialization/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2bcb7a60286d28b97dcf5022cdd4cafacdf1d6a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/entities/serialization/View.java
@@ -0,0 +1,23 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.entities.serialization;
+
+public class View {
+	public interface Public {}
+	public interface Persistence {}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/transport/Answer.java b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
index 4eaa7ba714a836492edb4a3e6777af3663e11917..de074d3e1cc30029d1ee9a60c315aa69319931fc 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/Answer.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
@@ -18,8 +18,10 @@
 package de.thm.arsnova.entities.transport;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
-import de.thm.arsnova.entities.Question;
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -59,6 +61,7 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display text answer")
+	@JsonView(View.Public.class)
 	public String getAnswerText() {
 		return answerText;
 	}
@@ -68,6 +71,7 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display subject answer")
+	@JsonView(View.Public.class)
 	public String getAnswerSubject() {
 		return answerSubject;
 	}
@@ -76,6 +80,7 @@ public class Answer implements Serializable {
 		this.answerSubject = answerSubject;
 	}
 
+	@JsonView(View.Public.class)
 	public final String getAnswerTextRaw() {
 		return this.answerTextRaw;
 	}
@@ -84,6 +89,7 @@ public class Answer implements Serializable {
 		this.answerTextRaw = answerTextRaw;
 	}
 
+	@JsonView(View.Public.class)
 	public final String getAnswerSubjectRaw() {
 		return this.answerSubjectRaw;
 	}
@@ -92,6 +98,7 @@ public class Answer implements Serializable {
 		this.answerSubjectRaw = answerSubjectRaw;
 	}
 
+	@JsonView(View.Public.class)
 	public final double getFreeTextScore() {
 		return this.freeTextScore;
 	}
@@ -110,6 +117,7 @@ public class Answer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "abstention")
+	@JsonView(View.Public.class)
 	public boolean isAbstention() {
 		return abstention;
 	}
@@ -118,34 +126,35 @@ public class Answer implements Serializable {
 		this.abstention = abstention;
 	}
 
-	public de.thm.arsnova.entities.Answer generateAnswerEntity(final User user, final Question question) {
+	public de.thm.arsnova.entities.Answer generateAnswerEntity(final User user, final Content content) {
 		// rewrite all fields so that no manipulated data gets written
 		// only answerText, answerSubject, and abstention are allowed
 		de.thm.arsnova.entities.Answer theAnswer = new de.thm.arsnova.entities.Answer();
 		theAnswer.setAnswerSubject(this.getAnswerSubject());
 		theAnswer.setAnswerText(this.getAnswerText());
 		theAnswer.setAnswerTextRaw(this.getAnswerTextRaw());
-		theAnswer.setSessionId(question.getSessionId());
+		theAnswer.setSessionId(content.getSessionId());
 		theAnswer.setUser(user.getUsername());
-		theAnswer.setQuestionId(question.get_id());
+		theAnswer.setQuestionId(content.getId());
 		theAnswer.setTimestamp(new Date().getTime());
-		theAnswer.setQuestionVariant(question.getQuestionVariant());
+		theAnswer.setQuestionVariant(content.getQuestionVariant());
 		theAnswer.setAbstention(this.isAbstention());
 		// calculate learning progress value after all properties are set
-		theAnswer.setQuestionValue(question.calculateValue(theAnswer));
+		theAnswer.setQuestionValue(content.calculateValue(theAnswer));
 		theAnswer.setAnswerImage(this.getAnswerImage());
 		theAnswer.setSuccessfulFreeTextAnswer(this.isSuccessfulFreeTextAnswer());
 
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			theAnswer.setPiRound(0);
 		} else {
-			theAnswer.setPiRound(question.getPiRound());
+			theAnswer.setPiRound(content.getPiRound());
 		}
 
 		return theAnswer;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display image answer")
+	@JsonView(View.Public.class)
 	public String getAnswerImage() {
 		return answerImage;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
index 92b8b4950ba24b3407beca5e3a8e2648f7d25300..694de60fcd8d8791394a7f47e4aeadedd93521ed 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.entities.transport;
 
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 
@@ -30,15 +30,15 @@ public class AnswerQueueElement {
 
 	private final Session session;
 
-	private final Question question;
+	private final Content content;
 
 	private final Answer answer;
 
 	private final User user;
 
-	public AnswerQueueElement(Session session, Question question, Answer answer, User user) {
+	public AnswerQueueElement(Session session, Content content, Answer answer, User user) {
 		this.session = session;
-		this.question = question;
+		this.content = content;
 		this.answer = answer;
 		this.user = user;
 	}
@@ -47,8 +47,8 @@ public class AnswerQueueElement {
 		return session;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 
 	public Answer getAnswer() {
diff --git a/src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java b/src/main/java/de/thm/arsnova/entities/transport/Comment.java
similarity index 68%
rename from src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java
rename to src/main/java/de/thm/arsnova/entities/transport/Comment.java
index 86d3115267712ba2ea96c93e73b5c6fb2a540c30..e3006b9913bdb7aa80aa5faa074a303a61b64411 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/InterposedQuestion.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/Comment.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities.transport;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -24,10 +26,10 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * A question a student is asking. Also known as feedback or audience question.
+ * A question a student is asking. Also known as comment, feedback or audience question.
  */
-@ApiModel(value = "audiencequestion/{questionId}", description = "the Interposed Question API")
-public class InterposedQuestion {
+@ApiModel(value = "audiencequestion/{questionId}", description = "the comment API")
+public class Comment {
 
 	private String id;
 	private String subject;
@@ -35,25 +37,26 @@ public class InterposedQuestion {
 	private long timestamp;
 	private boolean read;
 
-	public static List<InterposedQuestion> fromList(List<de.thm.arsnova.entities.InterposedQuestion> questions) {
-		ArrayList<InterposedQuestion> interposedQuestions = new ArrayList<>();
-		for (de.thm.arsnova.entities.InterposedQuestion question : questions) {
-			interposedQuestions.add(new InterposedQuestion(question));
+	public static List<Comment> fromList(List<de.thm.arsnova.entities.Comment> comments) {
+		ArrayList<Comment> transportComments = new ArrayList<>();
+		for (de.thm.arsnova.entities.Comment comment : comments) {
+			transportComments.add(new Comment(comment));
 		}
-		return interposedQuestions;
+		return transportComments;
 	}
 
-	public InterposedQuestion(de.thm.arsnova.entities.InterposedQuestion question) {
-		this.id = question.get_id();
-		this.subject = question.getSubject();
-		this.text = question.getText();
-		this.timestamp = question.getTimestamp();
-		this.read = question.isRead();
+	public Comment(de.thm.arsnova.entities.Comment comment) {
+		this.id = comment.getId();
+		this.subject = comment.getSubject();
+		this.text = comment.getText();
+		this.timestamp = comment.getTimestamp();
+		this.read = comment.isRead();
 	}
 
-	public InterposedQuestion() { }
+	public Comment() { }
 
 	@ApiModelProperty(required = true, value = "used to display Id")
+	@JsonView(View.Public.class)
 	public String getId() {
 		return id;
 	}
@@ -63,6 +66,7 @@ public class InterposedQuestion {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display Subject")
+	@JsonView(View.Public.class)
 	public String getSubject() {
 		return subject;
 	}
@@ -72,6 +76,7 @@ public class InterposedQuestion {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display Text")
+	@JsonView(View.Public.class)
 	public String getText() {
 		return text;
 	}
@@ -81,6 +86,7 @@ public class InterposedQuestion {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display Timetamp")
+	@JsonView(View.Public.class)
 	public long getTimestamp() {
 		return timestamp;
 	}
@@ -90,6 +96,7 @@ public class InterposedQuestion {
 	}
 
 	@ApiModelProperty(required = true, value = "is read")
+	@JsonView(View.Public.class)
 	public boolean isRead() {
 		return read;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
index 02a01e975c8818aa1cbdac2b65a06c85ac535c55..1ef60232f0e80f12197be2720cdb27886dfa4191 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
@@ -17,12 +17,14 @@
  */
 package de.thm.arsnova.entities.transport;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Motd;
-import de.thm.arsnova.entities.Question;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.SessionFeature;
 import de.thm.arsnova.entities.SessionInfo;
 import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -38,9 +40,9 @@ public class ImportExportSession {
 
 	private ImportExportSesssion session;
 
-	private List<ImportExportQuestion> questions;
+	private List<ImportExportContent> questions;
 
-	private List<InterposedQuestion> feedbackQuestions;
+	private List<Comment> feedbackQuestions;
 
 	private List<Motd> motds;
 
@@ -56,6 +58,7 @@ public class ImportExportSession {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display session")
+	@JsonView(View.Public.class)
 	public ImportExportSesssion getSession() {
 		return session;
 	}
@@ -65,23 +68,26 @@ public class ImportExportSession {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display questions")
-	public List<ImportExportQuestion> getQuestions() {
+	@JsonView(View.Public.class)
+	public List<ImportExportContent> getQuestions() {
 		return questions;
 	}
 
-	public void setQuestions(List<ImportExportQuestion> questions) {
+	public void setQuestions(List<ImportExportContent> questions) {
 		this.questions = questions;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display questions feedback")
-	public List<InterposedQuestion> getFeedbackQuestions() {
+	@JsonView(View.Public.class)
+	public List<Comment> getFeedbackQuestions() {
 		return feedbackQuestions;
 	}
 
-	public void setFeedbackQuestions(List<InterposedQuestion> feedbackQuestions) {
+	public void setFeedbackQuestions(List<Comment> feedbackQuestions) {
 		this.feedbackQuestions = feedbackQuestions;
 	}
 
+	@JsonView(View.Public.class)
 	public List<Motd> getMotds() {
 		return motds;
 	}
@@ -90,6 +96,7 @@ public class ImportExportSession {
 		this.motds = mL;
 	}
 
+	@JsonView(View.Public.class)
 	public SessionFeature getSessionFeature() {
 		return sessionFeature;
 	}
@@ -98,6 +105,7 @@ public class ImportExportSession {
 		sessionFeature = sF;
 	}
 
+	@JsonView(View.Public.class)
 	public SessionInfo getSessionInfo() {
 		return sessionInfo;
 	}
@@ -118,8 +126,8 @@ public class ImportExportSession {
 		session = iesession;
 	}
 
-	public void addQuestionWithAnswers(Question q, List<Answer> aL) {
-		ImportExportQuestion ieq = new ImportExportQuestion(q);
+	public void addQuestionWithAnswers(Content q, List<Answer> aL) {
+		ImportExportContent ieq = new ImportExportContent(q);
 		ieq.setAnswers(aL);
 		questions.add(ieq);
 	}
@@ -145,22 +153,20 @@ public class ImportExportSession {
 		s.setPpSubject(session.getPublicPool().getPpSubject());
 		s.setPpUniversity(session.getPublicPool().getPpUniversity());
 		// other fields
-		s.setType("session");
 		s.setCreator(user.getUsername());
 		s.setCreationTime(new Date().getTime());
 		return s;
 	}
 
-	public static class ImportExportQuestion extends Question {
+	public static class ImportExportContent extends Content {
 
 		private List<Answer> answers;
 
-		public ImportExportQuestion() {
+		public ImportExportContent() {
 
 		}
 
-		public ImportExportQuestion(Question q) {
-			setType(q.getType());
+		public ImportExportContent(Content q) {
 			setQuestionType(q.getQuestionType());
 			setQuestionVariant(q.getQuestionVariant());
 			setSubject(q.getSubject());
@@ -217,6 +223,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = " used to display answers")
+		@JsonView(View.Public.class)
 		public List<Answer> getAnswers() {
 			return answers;
 		}
@@ -241,6 +248,7 @@ public class ImportExportSession {
 		private SessionFeature sessionFeature;
 
 		@ApiModelProperty(required = true, value = "used to display short name")
+		@JsonView(View.Public.class)
 		public String getName() {
 			return name;
 		}
@@ -250,6 +258,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = false, value = "used to identify public pool sessions")
+		@JsonView(View.Public.class)
 		public String getSessionType() {
 			return sessionType;
 		}
@@ -259,6 +268,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display short name")
+		@JsonView(View.Public.class)
 		public String getShortName() {
 			return shortName;
 		}
@@ -268,6 +278,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "active")
+		@JsonView(View.Public.class)
 		public boolean isActive() {
 			return active;
 		}
@@ -277,6 +288,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display public pool")
+		@JsonView(View.Public.class)
 		public PublicPool getPublicPool() {
 			return publicPool;
 		}
@@ -285,6 +297,7 @@ public class ImportExportSession {
 			this.publicPool = publicPool;
 		}
 
+		@JsonView(View.Public.class)
 		public SessionFeature getSessionFeature() {
 			return this.sessionFeature;
 		}
@@ -333,6 +346,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display author name")
+		@JsonView(View.Public.class)
 		public String getPpAuthorName() {
 			return ppAuthorName;
 		}
@@ -342,6 +356,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display author mail")
+		@JsonView(View.Public.class)
 		public String getPpAuthorMail() {
 			return ppAuthorMail;
 		}
@@ -351,6 +366,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display university")
+		@JsonView(View.Public.class)
 		public String getPpUniversity() {
 			return ppUniversity;
 		}
@@ -360,6 +376,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display logo")
+		@JsonView(View.Public.class)
 		public String getPpLogo() {
 			return ppLogo;
 		}
@@ -369,6 +386,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display subject")
+		@JsonView(View.Public.class)
 		public String getPpSubject() {
 			return ppSubject;
 		}
@@ -378,6 +396,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display license")
+		@JsonView(View.Public.class)
 		public String getPpLicense() {
 			return ppLicense;
 		}
@@ -387,6 +406,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display level")
+		@JsonView(View.Public.class)
 		public String getPpLevel() {
 			return ppLevel;
 		}
@@ -396,6 +416,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display description")
+		@JsonView(View.Public.class)
 		public String getPpDescription() {
 			return ppDescription;
 		}
@@ -405,6 +426,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display faculty")
+		@JsonView(View.Public.class)
 		public String getPpFaculty() {
 			return ppFaculty;
 		}
@@ -414,6 +436,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display name")
+		@JsonView(View.Public.class)
 		public String getName() {
 			return name;
 		}
@@ -423,6 +446,7 @@ public class ImportExportSession {
 		}
 
 		@ApiModelProperty(required = true, value = "used to display short name")
+		@JsonView(View.Public.class)
 		public String getShortName() {
 			return shortName;
 		}
diff --git a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java b/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java
index 041ee4d54c7bcf6e214eeb7da4d5cbb304909f38..686a733d09775ba257e97c8a11970bb105942ba4 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities.transport;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -39,6 +41,7 @@ public class LearningProgressValues {
 	private int numUsers;
 
 	@ApiModelProperty(required = true, value = "used to display course progress")
+	@JsonView(View.Public.class)
 	public int getCourseProgress() {
 		return courseProgress;
 	}
@@ -48,6 +51,7 @@ public class LearningProgressValues {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display my progress")
+	@JsonView(View.Public.class)
 	public int getMyProgress() {
 		return myProgress;
 	}
@@ -57,6 +61,7 @@ public class LearningProgressValues {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display questions number")
+	@JsonView(View.Public.class)
 	public int getNumQuestions() {
 		return numQuestions;
 	}
@@ -65,6 +70,7 @@ public class LearningProgressValues {
 		this.numQuestions = numQuestions;
 	}
 
+	@JsonView(View.Public.class)
 	public int getNumerator() {
 		return numerator;
 	}
@@ -73,6 +79,7 @@ public class LearningProgressValues {
 		this.numerator = numerator;
 	}
 
+	@JsonView(View.Public.class)
 	public int getDenominator() {
 		return denominator;
 	}
@@ -82,6 +89,7 @@ public class LearningProgressValues {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display user number")
+	@JsonView(View.Public.class)
 	public int getNumUsers() {
 		return numUsers;
 	}
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
index 8d400e92823539cab79c6ca09ed484e772604b02..55970e207457824eff5d3ba23772ed3e24ad4e68 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
@@ -27,11 +27,11 @@ public class DeleteAnswerEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public DeleteAnswerEvent(Object source, Session session, Question question) {
+	public DeleteAnswerEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	@Override
@@ -39,7 +39,7 @@ public class DeleteAnswerEvent extends SessionEvent {
 		visitor.visit(this);
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
similarity index 71%
rename from src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java
rename to src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
index fb0b6ce0283f88f1242e3445a195fc026c4364c4..a22b545aa4a81d7f0e80d9059531537943f3a3fd 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteInterposedQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
@@ -17,21 +17,21 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.InterposedQuestion;
+import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever an interposed question is deleted.
+ * Fires whenever an comment is deleted.
  */
-public class DeleteInterposedQuestionEvent extends SessionEvent {
+public class DeleteCommentEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final InterposedQuestion question;
+	private final Comment comment;
 
-	public DeleteInterposedQuestionEvent(Object source, Session session, InterposedQuestion question) {
+	public DeleteCommentEvent(Object source, Session session, Comment comment) {
 		super(source, session);
-		this.question = question;
+		this.comment = comment;
 	}
 
 	@Override
@@ -39,8 +39,8 @@ public class DeleteInterposedQuestionEvent extends SessionEvent {
 		visitor.visit(this);
 	}
 
-	public InterposedQuestion getQuestion() {
-		return question;
+	public Comment getQuestion() {
+		return comment;
 	}
 
 }
diff --git a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
index 2f3cfd71b2518377694c7c3e160a3ee4dcc373cf..0204ee5b7d5ca8bdd101a0cf41ddb7c005da0800 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is deleted.
+ * Fires whenever a content is deleted.
  */
 public class DeleteQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public DeleteQuestionEvent(Object source, Session session, Question question) {
+	public DeleteQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
index 7daf6d22a51e66a6153e487d5622019714e1e19d..3bffe73bb4639cc8c8509a3fad8fabf74dada234 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.events;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a session is deleted. Note that this implies that all related data such as interposed questions,
+ * Fires whenever a session is deleted. Note that this implies that all related data such as comments,
  * lecturer questions, and answers are deleted as well, even though those events are not fired.
  */
 public class DeleteSessionEvent extends SessionEvent {
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
index 389b3ea97b08bb76af634a26036283c2344c46d1..8371c8b01c59573a2f7b6320421623a1dba4ff26 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is disabled, i.e., it is hidden from students.
+ * Fires whenever a content is disabled, i.e., it is hidden from students.
  */
 public class LockQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public LockQuestionEvent(Object source, Session session, Question question) {
+	public LockQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
index 9ee93fa3b232f2f4ec517dc5ffa304256553f2a2..2981f4e18f5610a5799167146c1036e0cb286be2 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever a set of questions are disabled, i.e., they are hidden from students.
+ * Fires whenever a set of contents are disabled, i.e., they are hidden from students.
  */
 public class LockQuestionsEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public LockQuestionsEvent(Object source, Session session, List<Question> questions) {
+	public LockQuestionsEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
index b33922f4f25bdc75a5df1a520fffce1e14c0b548..e3e9f000fd8fafa074572445ce493510a1a06d68 100644
--- a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
@@ -17,36 +17,36 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Fires whenever voting on a question is disabled.
+ * Fires whenever voting on a content is disabled.
  */
 public class LockVoteEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public LockVoteEvent(Object source, Session session, Question question) {
+	public LockVoteEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	public String getQuestionId() {
-		return this.question.get_id();
+		return this.content.getId();
 	}
 
 	public String getQuestionVariant() {
-		return this.question.getQuestionVariant();
+		return this.content.getQuestionVariant();
 	}
 
 	public Boolean getVotingDisabled() {
-		return this.question.isVotingDisabled();
+		return this.content.isVotingDisabled();
 	}
 
 	public Map<String, Object> getVotingAdmission() {
diff --git a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
index fb5acd7c16e404db66a3a716ba48f52f7461253e..6f8043c18ae279962546d180e4191bde2d5e193e 100644
--- a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever voting of multiple questions is disabled.
+ * Fires whenever voting of multiple contents is disabled.
  */
 public class LockVotesEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public LockVotesEvent(Object source, Session session, List<Question> questions) {
+	public LockVotesEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
index c2fd68a5237b2e0e73fc1ec9535405989d4ca579..9172a5b5a5b307be3ed77a881ee0a868872674f7 100644
--- a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.events;
 
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 
@@ -33,13 +33,13 @@ public class NewAnswerEvent extends SessionEvent {
 
 	private final User user;
 
-	private final Question question;
+	private final Content content;
 
-	public NewAnswerEvent(Object source, Session session, Answer answer, User user, Question question) {
+	public NewAnswerEvent(Object source, Session session, Answer answer, User user, Content content) {
 		super(source, session);
 		this.answer = answer;
 		this.user = user;
-		this.question = question;
+		this.content = content;
 	}
 
 	@Override
@@ -55,7 +55,7 @@ public class NewAnswerEvent extends SessionEvent {
 		return user;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getContent() {
+		return content;
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/NewInterposedQuestionEvent.java b/src/main/java/de/thm/arsnova/events/NewCommentEvent.java
similarity index 70%
rename from src/main/java/de/thm/arsnova/events/NewInterposedQuestionEvent.java
rename to src/main/java/de/thm/arsnova/events/NewCommentEvent.java
index 3983c70177bd98d08a3a44a66af0535709e6530e..8330cbcd5b40c4d6ecf4b7cd2a1ad3c2e0dd0865 100644
--- a/src/main/java/de/thm/arsnova/events/NewInterposedQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewCommentEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.InterposedQuestion;
+import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a new interposed (aka. feedback or audience) question is added.
+ * Fires whenever a new comment is added.
  */
-public class NewInterposedQuestionEvent extends SessionEvent {
+public class NewCommentEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final InterposedQuestion question;
+	private final Comment comment;
 
-	public NewInterposedQuestionEvent(Object source, Session session, InterposedQuestion question) {
+	public NewCommentEvent(Object source, Session session, Comment comment) {
 		super(source, session);
-		this.question = question;
+		this.comment = comment;
 	}
 
-	public InterposedQuestion getQuestion() {
-		return question;
+	public Comment getQuestion() {
+		return comment;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
index 6ac4be12e84ad0c21b835f08f434aa72c9dfa11d..1a9e1616ec35819e5825b14b6ad1fb5609e82f92 100644
--- a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a new question is added.
+ * Fires whenever a new content is added.
  */
 public class NewQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public NewQuestionEvent(Object source, Session session, Question question) {
+	public NewQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java b/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java
index 0152851ce50df48bb3063bc115449059e911d6c1..f02c32154ca8897d5f95251bb59a895387b72c76 100644
--- a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java
+++ b/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java
@@ -22,9 +22,9 @@ package de.thm.arsnova.events;
  */
 public interface NovaEventVisitor {
 
-	void visit(NewInterposedQuestionEvent newInterposedQuestionEvent);
+	void visit(NewCommentEvent newCommentEvent);
 
-	void visit(DeleteInterposedQuestionEvent deleteInterposedQuestionEvent);
+	void visit(DeleteCommentEvent deleteCommentEvent);
 
 	void visit(NewQuestionEvent newQuestionEvent);
 
@@ -81,6 +81,6 @@ public interface NovaEventVisitor {
 	void visit(FeatureChangeEvent featureChangeEvent);
 
 	void visit(LockFeedbackEvent lockFeedbackEvent);
-	
+
 	void visit(FlipFlashcardsEvent flipFlashcardsEvent);
 }
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
index 86ff69ae311e784a077d40aa8eda3ba44627f341..64b51aea8780c1a4d5b68efd667b21f5eecc3e4d 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
@@ -27,8 +27,8 @@ public class PiRoundCancelEvent extends PiRoundEndEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	public PiRoundCancelEvent(Object source, Session session, Question question) {
-		super(source, session, question);
+	public PiRoundCancelEvent(Object source, Session session, Content content) {
+		super(source, session, content);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
index 6fdc587e38791ae1c56c54e2fa329841dd93b9f2..dfaed2bb4fbf87b73fc27b9dae220de29b3bf29f 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -36,13 +36,13 @@ public class PiRoundDelayedStartEvent extends SessionEvent {
 	private final String questionVariant;
 	private int piRound;
 
-	public PiRoundDelayedStartEvent(Object source, Session session, Question question) {
+	public PiRoundDelayedStartEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.questionId = question.get_id();
-		this.startTime = question.getPiRoundStartTime();
-		this.endTime = question.getPiRoundEndTime();
-		this.questionVariant = question.getQuestionVariant();
-		this.piRound = question.getPiRound();
+		this.questionId = content.getId();
+		this.startTime = content.getPiRoundStartTime();
+		this.endTime = content.getPiRoundEndTime();
+		this.questionVariant = content.getQuestionVariant();
+		this.piRound = content.getPiRound();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
index b87d12590bdd7e3667d7998367b86cbb4514b07d..335345185364f88e718c0f19d4b50c55520390f4 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -33,10 +33,10 @@ public class PiRoundEndEvent extends SessionEvent {
 	private final String questionId;
 	private final String questionVariant;
 
-	public PiRoundEndEvent(Object source, Session session, Question question) {
+	public PiRoundEndEvent(Object source, Session session, Content content) {
 		super(source, session);
-		questionId = question.get_id();
-		questionVariant = question.getQuestionVariant();
+		questionId = content.getId();
+		questionVariant = content.getQuestionVariant();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
index 429aeb274c1b26fecbc1f779734316a9d2835ca1..96bd50df0d30637ea3c4fc3b87c65cba5b0bee70 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -33,10 +33,10 @@ public class PiRoundResetEvent extends SessionEvent {
 	private final String questionId;
 	private final String questionVariant;
 
-	public PiRoundResetEvent(Object source, Session session, Question question) {
+	public PiRoundResetEvent(Object source, Session session, Content content) {
 		super(source, session);
-		questionId = question.get_id();
-		questionVariant = question.getQuestionVariant();
+		questionId = content.getId();
+		questionVariant = content.getQuestionVariant();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
index d04c709e8af2b89761f7bcf4d2de508558096f82..1d8e24c61ae3661a45b6d42b3f92d464df9f71e2 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is enabled, i.e., it becomes visible to students.
+ * Fires whenever a content is enabled, i.e., it becomes visible to students.
  */
 public class UnlockQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public UnlockQuestionEvent(Object source, Session session, Question question) {
+	public UnlockQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
index b760b4ce5f4fb73e528432b782f9fbe3687d6cf0..153b702a6fbdd1177eb6fbb5fa920c8d99319215 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever a set of questions are enabled, i.e., they become visible to students.
+ * Fires whenever a set of contents are enabled, i.e., they become visible to students.
  */
 public class UnlockQuestionsEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public UnlockQuestionsEvent(Object source, Session session, List<Question> questions) {
+	public UnlockQuestionsEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
index 69a1f45717e077339a28267700a43a8e6123703a..00bf47a4d8781e8368cc33fcd14a0c1f122314b3 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
@@ -17,36 +17,36 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Fires whenever voting on a question is enabled.
+ * Fires whenever voting on a content is enabled.
  */
 public class UnlockVoteEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public UnlockVoteEvent(Object source, Session session, Question question) {
+	public UnlockVoteEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	public String getQuestionId() {
-		return this.question.get_id();
+		return this.content.getId();
 	}
 
 	public String getQuestionVariant() {
-		return this.question.getQuestionVariant();
+		return this.content.getQuestionVariant();
 	}
 
 	public Boolean getVotingDisabled() {
-		return this.question.isVotingDisabled();
+		return this.content.isVotingDisabled();
 	}
 
 	public Map<String, Object> getVotingAdmission() {
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
index c489aefcf5d8b22ab91e8d649985201e1f85a4cf..2dd3d4671849ac43cccf7a457ddc2e43a63feec7 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever voting of multiple questions is enabled.
+ * Fires whenever voting of multiple contents is enabled.
  */
 public class UnlockVotesEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public UnlockVotesEvent(Object source, Session session, List<Question> questions) {
+	public UnlockVotesEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/persistance/AnswerRepository.java b/src/main/java/de/thm/arsnova/persistance/AnswerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..27693fa2b45a20be0526e70078d2d278f40422aa
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/AnswerRepository.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+
+import java.util.List;
+
+public interface AnswerRepository {
+	Answer get(String id);
+	Answer getMyAnswer(User me, String questionId, int piRound);
+	List<Answer> getAnswers(Content content, int piRound);
+	List<Answer> getAnswers(Content content);
+	List<Answer> getAllAnswers(Content content);
+	int getAnswerCount(Content content, int piRound);
+	int getTotalAnswerCountByQuestion(Content content);
+	int getAbstentionAnswerCount(String questionId);
+	List<Answer> getFreetextAnswers(String questionId, final int start, final int limit);
+	List<Answer> getMyAnswers(User me, Session session);
+	int getTotalAnswerCount(String sessionKey);
+	int deleteAnswers(Content content);
+	Answer saveAnswer(Answer answer, User user, Content content, Session session);
+	Answer updateAnswer(Answer answer);
+	void deleteAnswer(String answerId);
+	int countLectureQuestionAnswers(Session session);
+	int countPreparationQuestionAnswers(Session session);
+	int deleteAllQuestionsAnswers(Session session);
+	int deleteAllPreparationAnswers(Session session);
+	int deleteAllLectureAnswers(Session session);
+	int[] deleteAllAnswersWithQuestions(List<Content> contents);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/CommentRepository.java b/src/main/java/de/thm/arsnova/persistance/CommentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5f8bc350a907609195cda995c01401349a8f136
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/CommentRepository.java
@@ -0,0 +1,22 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+
+import java.util.List;
+
+public interface CommentRepository {
+	int getInterposedCount(String sessionKey);
+	CommentReadingCount getInterposedReadingCount(Session session);
+	CommentReadingCount getInterposedReadingCount(Session session, User user);
+	List<Comment> getInterposedQuestions(Session session, final int start, final int limit);
+	List<Comment> getInterposedQuestions(Session session, User user, final int start, final int limit);
+	Comment getInterposedQuestion(String commentId);
+	Comment saveQuestion(Session session, Comment comment, User user);
+	void markInterposedQuestionAsRead(Comment comment);
+	void deleteInterposedQuestion(Comment comment);
+	int deleteAllInterposedQuestions(Session session);
+	int deleteAllInterposedQuestions(Session session, User user);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/ContentRepository.java b/src/main/java/de/thm/arsnova/persistance/ContentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c49aa7892f774b4eed4b6bb99b99bb6b70de7a2
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/ContentRepository.java
@@ -0,0 +1,44 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+
+import java.util.List;
+
+public interface ContentRepository {
+	List<Content> getQuestions(Object... keys);
+	Content getQuestion(String id);
+	Content saveQuestion(Session session, Content content);
+	List<Content> getSkillQuestionsForUsers(Session session);
+	List<Content> getSkillQuestionsForTeachers(Session session);
+	int getSkillQuestionCount(Session session);
+	List<String> getQuestionIds(Session session, User user);
+	int deleteQuestionWithAnswers(Content content);
+	int[] deleteAllQuestionsWithAnswers(Session session);
+	List<String> getUnAnsweredQuestionIds(Session session, User user);
+	Content updateQuestion(Content content);
+	List<Content> getLectureQuestionsForUsers(Session session);
+	List<Content> getLectureQuestionsForTeachers(Session session);
+	List<Content> getFlashcardsForUsers(Session session);
+	List<Content> getFlashcardsForTeachers(Session session);
+	List<Content> getPreparationQuestionsForUsers(Session session);
+	List<Content> getPreparationQuestionsForTeachers(Session session);
+	List<Content> getAllSkillQuestions(Session session);
+	int getLectureQuestionCount(Session session);
+	int getFlashcardCount(Session session);
+	int getPreparationQuestionCount(Session session);
+	void publishQuestions(Session session, boolean publish, List<Content> contents);
+	List<Content> publishAllQuestions(Session session, boolean publish);
+	List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject);
+	List<Content> getQuestionsByIds(List<String> ids, Session session);
+	void resetQuestionsRoundState(Session session, List<Content> contents);
+	void setVotingAdmissions(Session session, boolean disableVoting, List<Content> contents);
+	List<Content> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting);
+	int[] deleteAllLectureQuestionsWithAnswers(Session session);
+	int[] deleteAllFlashcardsWithAnswers(Session session);
+	int[] deleteAllPreparationQuestionsWithAnswers(Session session);
+	List<String> getSubjects(Session session, String questionVariant);
+	List<String> getUnAnsweredLectureQuestionIds(Session session, User user);
+	List<String> getUnAnsweredPreparationQuestionIds(Session session, User user);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/LogEntryRepository.java b/src/main/java/de/thm/arsnova/persistance/LogEntryRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ec30eb51c5b8407680b629b72a2f99f4f76c54a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/LogEntryRepository.java
@@ -0,0 +1,90 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.LogEntry;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public interface LogEntryRepository {
+	/**
+	 * Logs an event to the database. Arbitrary data can be attached as payload. Database logging should only be used
+	 * if the logged data is later analyzed by the backend itself. Otherwise use the default logging mechanisms.
+	 *
+	 * @param event type of the event
+	 * @param level severity of the event
+	 * @param payload arbitrary logging data
+	 */
+	void create(String event, LogEntry.LogLevel level, Map<String, Object> payload);
+
+	/**
+	 * Logs an event to the database. Arbitrary data can be attached as payload. Database logging should only be used
+	 * if the logged data is later analyzed by the backend itself. Otherwise use the default logging mechanisms.
+	 *
+	 * @param event type of the event
+	 * @param payload arbitrary logging data
+	 * @param level severity of the event
+	 */
+	default void log(String event, Map<String, Object> payload, LogEntry.LogLevel level) {
+		create(event, level, payload);
+	}
+
+	/**
+	 * Logs an event of informational severity to the database. Arbitrary data can be attached as payload. Database
+	 * logging should only be used if the logged data is later analyzed by the backend itself. Otherwise use the default
+	 * logging mechanisms.
+	 *
+	 * @param event type of the event
+	 * @param payload arbitrary logging data
+	 */
+	default void log(String event, Map<String, Object> payload) {
+		create(event, LogEntry.LogLevel.INFO, payload);
+	}
+
+	/**
+	 * Logs an event to the database. Arbitrary data can be attached as payload. Database logging should only be used
+	 * if the logged data is later analyzed by the backend itself. Otherwise use the default logging mechanisms.
+	 *
+	 * @param event type of the event
+	 * @param level severity of the event
+	 * @param rawPayload key/value pairs of arbitrary logging data
+	 */
+	default void log(String event, LogEntry.LogLevel level, Object... rawPayload) {
+		if (rawPayload.length % 2 != 0) {
+			throw new IllegalArgumentException("");
+		}
+		Map<String, Object> payload = new HashMap<>();
+		for (int i = 0; i < rawPayload.length; i += 2) {
+			payload.put((String) rawPayload[i], rawPayload[i + 1]);
+		}
+		create(event, level, payload);
+	}
+
+	/**
+	 * Logs an event of informational severity to the database. Arbitrary data can be attached as payload. Database
+	 * logging should only be used if the logged data is later analyzed by the backend itself. Otherwise use the default
+	 * logging mechanisms.
+	 *
+	 * @param event type of the event
+	 * @param rawPayload key/value pairs of arbitrary logging data
+	 */
+	default void log(String event, Object... rawPayload) {
+		log(event, LogEntry.LogLevel.INFO, rawPayload);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/MotdListRepository.java b/src/main/java/de/thm/arsnova/persistance/MotdListRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..a044fe7ed6b637ea6c6bc0c487908f4082c42609
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/MotdListRepository.java
@@ -0,0 +1,8 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.MotdList;
+
+public interface MotdListRepository {
+	MotdList getMotdListForUser(final String username);
+	MotdList createOrUpdateMotdList(MotdList motdlist);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/MotdRepository.java b/src/main/java/de/thm/arsnova/persistance/MotdRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c783165d8affbbfc057223ca76a87dbf17fb61f
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/MotdRepository.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Motd;
+
+import java.util.List;
+
+public interface MotdRepository {
+	List<Motd> getAdminMotds();
+	List<Motd> getMotdsForAll();
+	List<Motd> getMotdsForLoggedIn();
+	List<Motd> getMotdsForTutors();
+	List<Motd> getMotdsForStudents();
+	List<Motd> getMotdsForSession(final String sessionkey);
+	Motd getMotdByKey(String key);
+	Motd createOrUpdateMotd(Motd motd);
+	boolean deleteMotd(Motd motd);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/SessionRepository.java b/src/main/java/de/thm/arsnova/persistance/SessionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c57f848416dd1cfb37f665758689e344839ecd1
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/SessionRepository.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.connector.model.Course;
+import de.thm.arsnova.entities.LoggedIn;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.SessionInfo;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.transport.ImportExportSession;
+
+import java.util.List;
+
+public interface SessionRepository {
+	Session getSessionFromId(String sessionId);
+	Session getSessionFromKeyword(String keyword);
+	Session saveSession(User user, Session session);
+	Session updateSession(Session session);
+
+	/**
+	 * Deletes a session and related data.
+	 *
+	 * @param session the session for deletion
+	 */
+	int[] deleteSession(Session session);
+
+	Session changeSessionCreator(Session session, String newCreator);
+	int[] deleteInactiveGuestSessions(long lastActivityBefore);
+	List<Session> getMySessions(User user, final int start, final int limit);
+	List<Session> getSessionsForUsername(String username, final int start, final int limit);
+	List<Session> getPublicPoolSessions();
+	List<Session> getMyPublicPoolSessions(User user);
+	boolean sessionKeyAvailable(String keyword);
+	Session updateSessionOwnerActivity(Session session);
+	List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit);
+	List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
+	List<SessionInfo> getPublicPoolSessionsInfo();
+	List<SessionInfo> getMyPublicPoolSessionsInfo(final User user);
+	List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit);
+	List<Session> getCourseSessions(List<Course> courses);
+	SessionInfo importSession(User user, ImportExportSession importSession);
+	ImportExportSession exportSession(String sessionkey, Boolean withAnswer, Boolean withFeedbackQuestions);
+	LoggedIn registerAsOnlineUser(final User user, final Session session);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..193da0333f4aaf143065f69a12eaef674b1a75c3
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java
@@ -0,0 +1,8 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.domain.CourseScore;
+import de.thm.arsnova.entities.Session;
+
+public interface SessionStatisticsRepository {
+	CourseScore getLearningProgress(Session session);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/StatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/StatisticsRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea334f0d6932f2770a65c3f24aa71c7418a73847
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/StatisticsRepository.java
@@ -0,0 +1,7 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Statistics;
+
+public interface StatisticsRepository {
+	Statistics getStatistics();
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/UserRepository.java b/src/main/java/de/thm/arsnova/persistance/UserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7807f66aa3beb3583f2d7b6a22746ce158f4f327
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/UserRepository.java
@@ -0,0 +1,27 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.DbUser;
+
+public interface UserRepository {
+	DbUser findUserByUsername(String username);
+	DbUser createOrUpdateUser(DbUser user);
+	boolean deleteUser(DbUser user);
+	int deleteInactiveUsers(long lastActivityBefore);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/VisitedSessionRepository.java b/src/main/java/de/thm/arsnova/persistance/VisitedSessionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce0b2358155f8b539ecaec84f976c32b02577a84
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/VisitedSessionRepository.java
@@ -0,0 +1,5 @@
+package de.thm.arsnova.persistance;
+
+public interface VisitedSessionRepository {
+	int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbAnswerRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbAnswerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ebb3273e4c7ba6deac9ecf6ecd9090d860d52d8
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbAnswerRepository.java
@@ -0,0 +1,385 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Lists;
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.transport.AnswerQueueElement;
+import de.thm.arsnova.events.NewAnswerEvent;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.AnswerRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import de.thm.arsnova.persistance.SessionRepository;
+import org.ektorp.BulkDeleteDocument;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.DocumentOperationResult;
+import org.ektorp.UpdateConflictException;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class CouchDbAnswerRepository extends CouchDbRepositorySupport<Answer> implements AnswerRepository, ApplicationEventPublisherAware {
+	private static final int BULK_PARTITION_SIZE = 500;
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbAnswerRepository.class);
+
+	private final Queue<AnswerQueueElement> answerQueue = new ConcurrentLinkedQueue<>();
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	@Autowired
+	private SessionRepository sessionRepository;
+
+	@Autowired
+	private ContentRepository contentRepository;
+
+	private ApplicationEventPublisher publisher;
+
+	public CouchDbAnswerRepository(Class<Answer> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Scheduled(fixedDelay = 5000)
+	public void flushAnswerQueue() {
+		if (answerQueue.isEmpty()) {
+			// no need to send an empty bulk request.
+			return;
+		}
+
+		final List<Answer> answerList = new ArrayList<>();
+		final List<AnswerQueueElement> elements = new ArrayList<>();
+		AnswerQueueElement entry;
+		while ((entry = this.answerQueue.poll()) != null) {
+			final Answer answer = entry.getAnswer();
+			answerList.add(answer);
+			elements.add(entry);
+		}
+		try {
+			db.executeBulk(answerList);
+
+			// Send NewAnswerEvents ...
+			for (AnswerQueueElement e : elements) {
+				this.publisher.publishEvent(new NewAnswerEvent(this, e.getSession(), e.getAnswer(), e.getUser(), e.getQuestion()));
+			}
+		} catch (DbAccessException e) {
+			logger.error("Could not bulk save answers from queue.", e);
+		}
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	@CacheEvict("answers")
+	@Override
+	public int deleteAnswers(final Content content) {
+		try {
+			final ViewResult result = db.queryView(createQuery("by_questionid")
+					.key(content.getId()));
+			final List<List<ViewResult.Row>> partitions = Lists.partition(result.getRows(), BULK_PARTITION_SIZE);
+
+			int count = 0;
+			for (List<ViewResult.Row> partition: partitions) {
+				List<BulkDeleteDocument> answersToDelete = new ArrayList<>();
+				for (final ViewResult.Row a : partition) {
+					final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
+					answersToDelete.add(d);
+				}
+				List<DocumentOperationResult> errors = db.executeBulk(answersToDelete);
+				count += partition.size() - errors.size();
+				if (errors.size() > 0) {
+					logger.error("Could not bulk delete {} of {} answers.", errors.size(), partition.size());
+				}
+			}
+			dbLogger.log("delete", "type", "answer", "answerCount", count);
+
+			return count;
+		} catch (final DbAccessException e) {
+			logger.error("Could not delete answers for content {}.", content.getId(), e);
+		}
+
+		return 0;
+	}
+
+	@Override
+	public Answer getMyAnswer(final User me, final String questionId, final int piRound) {
+		final List<Answer> answerList = queryView("by_questionid_user_piround",
+				ComplexKey.of(questionId, me.getUsername(), piRound));
+		return answerList.isEmpty() ? null : answerList.get(0);
+	}
+
+	@Override
+	public List<Answer> getAnswers(final Content content, final int piRound) {
+		final String questionId = content.getId();
+		final ViewResult result = db.queryView(createQuery("by_questionid_piround_text_subject")
+						.group(true)
+						.startKey(ComplexKey.of(questionId, piRound))
+						.endKey(ComplexKey.of(questionId, piRound, ComplexKey.emptyObject())));
+		final int abstentionCount = getAbstentionAnswerCount(questionId);
+
+		List<Answer> answers = new ArrayList<>();
+		for (final ViewResult.Row d : result) {
+			final Answer a = new Answer();
+			a.setAnswerCount(d.getValueAsInt());
+			a.setAbstentionCount(abstentionCount);
+			a.setQuestionId(d.getKeyAsNode().get(0).asText());
+			a.setPiRound(piRound);
+			final JsonNode answerTextNode = d.getKeyAsNode().get(3);
+			a.setAnswerText(answerTextNode.isNull() ? null : answerTextNode.asText());
+			answers.add(a);
+		}
+
+		return answers;
+	}
+
+	@Override
+	public List<Answer> getAllAnswers(final Content content) {
+		final String questionId = content.getId();
+		final ViewResult result = db.queryView(createQuery("by_questionid_piround_text_subject")
+				.group(true)
+				.startKey(ComplexKey.of(questionId))
+				.endKey(ComplexKey.of(questionId, ComplexKey.emptyObject())));
+		final int abstentionCount = getAbstentionAnswerCount(questionId);
+
+		final List<Answer> answers = new ArrayList<>();
+		for (final ViewResult.Row d : result.getRows()) {
+			final Answer a = new Answer();
+			a.setAnswerCount(d.getValueAsInt());
+			a.setAbstentionCount(abstentionCount);
+			a.setQuestionId(d.getKeyAsNode().get(0).asText());
+			final JsonNode answerTextNode = d.getKeyAsNode().get(3);
+			final JsonNode answerSubjectNode = d.getKeyAsNode().get(4);
+			final boolean successfulFreeTextAnswer = d.getKeyAsNode().get(5).asBoolean();
+			a.setAnswerText(answerTextNode.isNull() ? null : answerTextNode.asText());
+			a.setAnswerSubject(answerSubjectNode.isNull() ? null : answerSubjectNode.asText());
+			a.setSuccessfulFreeTextAnswer(successfulFreeTextAnswer);
+			answers.add(a);
+		}
+
+		return answers;
+	}
+
+	@Cacheable("answers")
+	@Override
+	public List<Answer> getAnswers(final Content content) {
+		return this.getAnswers(content, content.getPiRound());
+	}
+
+	@Override
+	public int getAbstentionAnswerCount(final String questionId) {
+		final ViewResult result = db.queryView(createQuery("by_questionid_piround_text_subject")
+				//.group(true)
+				.startKey(ComplexKey.of(questionId))
+				.endKey(ComplexKey.of(questionId, ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getAnswerCount(final Content content, final int piRound) {
+		final ViewResult result = db.queryView(createQuery("by_questionid_piround_text_subject")
+				//.group(true)
+				.startKey(ComplexKey.of(content.getId(), piRound))
+				.endKey(ComplexKey.of(content.getId(), piRound, ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getTotalAnswerCountByQuestion(final Content content) {
+		final ViewResult result = db.queryView(createQuery("by_questionid_piround_text_subject")
+				//.group(true)
+				.startKey(ComplexKey.of(content.getId()))
+				.endKey(ComplexKey.of(content.getId(), ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public List<Answer> getFreetextAnswers(final String questionId, final int start, final int limit) {
+		final int qSkip = start > 0 ? start : -1;
+		final int qLimit = limit > 0 ? limit : -1;
+
+		final List<Answer> answers = db.queryView(createQuery("by_questionid_timestamp")
+						.skip(qSkip)
+						.limit(qLimit)
+						//.includeDocs(true)
+						.startKey(ComplexKey.of(questionId))
+						.endKey(ComplexKey.of(questionId, ComplexKey.emptyObject()))
+						.descending(true),
+				Answer.class);
+
+		return answers;
+	}
+
+	@Override
+	public List<Answer> getMyAnswers(final User me, final Session s) {
+		return queryView("by_user_sessionid", ComplexKey.of(me.getUsername(), s.getId()));
+	}
+
+	@Override
+	public int getTotalAnswerCount(final String sessionKey) {
+		final Session s = sessionRepository.getSessionFromKeyword(sessionKey);
+		if (s == null) {
+			throw new NotFoundException();
+		}
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant").key(s.getId()));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@CacheEvict(value = "answers", key = "#content")
+	@Override
+	public Answer saveAnswer(final Answer answer, final User user, final Content content, final Session session) {
+		db.create(answer);
+		this.answerQueue.offer(new AnswerQueueElement(session, content, answer, user));
+
+		return answer;
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@CacheEvict(value = "answers", allEntries = true)
+	@Override
+	public Answer updateAnswer(final Answer answer) {
+		try {
+			update(answer);
+			return answer;
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not update answer {}.", answer, e);
+		}
+
+		return null;
+	}
+
+	/* TODO: Only evict cache entry for the answer's session. This requires some refactoring. */
+	@CacheEvict(value = "answers", allEntries = true)
+	@Override
+	public void deleteAnswer(final String answerId) {
+		try {
+			/* TODO: use id and rev instead of loading the answer */
+			db.delete(get(answerId));
+			dbLogger.log("delete", "type", "answer");
+		} catch (final DbAccessException e) {
+			logger.error("Could not delete answer {}.", answerId, e);
+		}
+	}
+
+	@Override
+	public int countLectureQuestionAnswers(final Session session) {
+		return countQuestionVariantAnswers(session, "lecture");
+	}
+
+	@Override
+	public int countPreparationQuestionAnswers(final Session session) {
+		return countQuestionVariantAnswers(session, "preparation");
+	}
+
+	private int countQuestionVariantAnswers(final Session session, final String variant) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant")
+				.key(ComplexKey.of(session.getId(), variant)));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@CacheEvict(value = "answers", allEntries = true)
+	@Override
+	public int deleteAllQuestionsAnswers(final Session session) {
+		final List<Content> contents = contentRepository.getQuestions(session.getId());
+		contentRepository.resetQuestionsRoundState(session, contents);
+
+		return deleteAllAnswersForQuestions(contents);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@CacheEvict(value = "answers", allEntries = true)
+	@Override
+	public int deleteAllPreparationAnswers(final Session session) {
+		final List<Content> contents = contentRepository.getQuestions(session.getId(), "preparation");
+		contentRepository.resetQuestionsRoundState(session, contents);
+
+		return deleteAllAnswersForQuestions(contents);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@CacheEvict(value = "answers", allEntries = true)
+	@Override
+	public int deleteAllLectureAnswers(final Session session) {
+		final List<Content> contents = contentRepository.getQuestions(session.getId(), "lecture");
+		contentRepository.resetQuestionsRoundState(session, contents);
+
+		return deleteAllAnswersForQuestions(contents);
+	}
+
+	public int deleteAllAnswersForQuestions(List<Content> contents) {
+		List<String> questionIds = new ArrayList<>();
+		for (Content q : contents) {
+			questionIds.add(q.getId());
+		}
+		final ViewResult result = db.queryView(createQuery("by_questionid")
+				.keys(questionIds));
+		final List<BulkDeleteDocument> allAnswers = new ArrayList<>();
+		for (ViewResult.Row a : result.getRows()) {
+			final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
+			allAnswers.add(d);
+		}
+		try {
+			List<DocumentOperationResult> errors = db.executeBulk(allAnswers);
+
+			return allAnswers.size() - errors.size();
+		} catch (DbAccessException e) {
+			logger.error("Could not bulk delete answers.", e);
+		}
+
+		return 0;
+	}
+
+	public int[] deleteAllAnswersWithQuestions(List<Content> contents) {
+		List<String> questionIds = new ArrayList<>();
+		final List<BulkDeleteDocument> allQuestions = new ArrayList<>();
+		for (Content q : contents) {
+			final BulkDeleteDocument d = new BulkDeleteDocument(q.getId(), q.getRevision());
+			questionIds.add(q.getId());
+			allQuestions.add(d);
+		}
+
+		final ViewResult result = db.queryView(createQuery("by_questionid")
+				.key(questionIds));
+		final List<BulkDeleteDocument> allAnswers = new ArrayList<>();
+		for (ViewResult.Row a : result.getRows()) {
+			final BulkDeleteDocument d = new BulkDeleteDocument(a.getId(), a.getValueAsNode().get("_rev").asText());
+			allAnswers.add(d);
+		}
+
+		try {
+			List<BulkDeleteDocument> deleteList = new ArrayList<>(allAnswers);
+			deleteList.addAll(allQuestions);
+			List<DocumentOperationResult> errors = db.executeBulk(deleteList);
+
+			/* TODO: subtract errors from count */
+			return new int[] {allQuestions.size(), allAnswers.size()};
+		} catch (DbAccessException e) {
+			logger.error("Could not bulk delete contents and answers.", e);
+		}
+
+		return new int[] {0, 0};
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbCommentRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbCommentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff1deeda8a5fb73646a7053d5a7536c952753ca6
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbCommentRepository.java
@@ -0,0 +1,239 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import de.thm.arsnova.persistance.SessionRepository;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DocumentNotFoundException;
+import org.ektorp.UpdateConflictException;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+public class CouchDbCommentRepository extends CouchDbRepositorySupport<Comment> implements CommentRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbCommentRepository.class);
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	@Autowired
+	private SessionRepository sessionRepository;
+
+	public CouchDbCommentRepository(Class<Comment> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	public int getInterposedCount(final String sessionKey) {
+		final Session s = sessionRepository.getSessionFromKeyword(sessionKey);
+		if (s == null) {
+			throw new NotFoundException();
+		}
+
+		final ViewResult result = db.queryView(createQuery("by_sessionid").key(s.getId()).group(true));
+		if (result.isEmpty()) {
+			return 0;
+		}
+
+		return result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public CommentReadingCount getInterposedReadingCount(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_read")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+				.group(true));
+		return getInterposedReadingCount(result);
+	}
+
+	@Override
+	public CommentReadingCount getInterposedReadingCount(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_creator_read")
+				.startKey(ComplexKey.of(session.getId(), user.getUsername()))
+				.endKey(ComplexKey.of(session.getId(), user.getUsername(), ComplexKey.emptyObject()))
+				.group(true));
+		return getInterposedReadingCount(result);
+	}
+
+	private CommentReadingCount getInterposedReadingCount(final ViewResult viewResult) {
+		if (viewResult.isEmpty()) {
+			return new CommentReadingCount();
+		}
+		// A complete result looks like this. Note that the second row is optional, and that the first one may be
+		// 'unread' or 'read', i.e., results may be switched around or only one result may be present.
+		// count = {"rows":[
+		// {"key":["cecebabb21b096e592d81f9c1322b877","Guestc9350cf4a3","read"],"value":1},
+		// {"key":["cecebabb21b096e592d81f9c1322b877","Guestc9350cf4a3","unread"],"value":1}
+		// ]}
+		int read = 0, unread = 0;
+		boolean isRead = false;
+		final ViewResult.Row fst = viewResult.getRows().get(0);
+		final ViewResult.Row snd = viewResult.getRows().size() > 1 ? viewResult.getRows().get(1) : null;
+
+		final JsonNode fstkey = fst.getKeyAsNode();
+		if (fstkey.size() == 2) {
+			isRead = fstkey.get(1).asBoolean();
+		} else if (fstkey.size() == 3) {
+			isRead = fstkey.get(2).asBoolean();
+		}
+		if (isRead) {
+			read = fst.getValueAsInt();
+		} else {
+			unread = fst.getValueAsInt();
+		}
+
+		if (snd != null) {
+			final JsonNode sndkey = snd.getKeyAsNode();
+			if (sndkey.size() == 2) {
+				isRead = sndkey.get(1).asBoolean();
+			} else {
+				isRead = sndkey.get(2).asBoolean();
+			}
+			if (isRead) {
+				read = snd.getValueAsInt();
+			} else {
+				unread = snd.getValueAsInt();
+			}
+		}
+		return new CommentReadingCount(read, unread);
+	}
+
+	@Override
+	public List<Comment> getInterposedQuestions(final Session session, final int start, final int limit) {
+		final int qSkip = start > 0 ? start : -1;
+		final int qLimit = limit > 0 ? limit : -1;
+
+		final List<Comment> comments = db.queryView(createQuery("by_sessionid_timestamp")
+						.skip(qSkip)
+						.limit(qLimit)
+						.descending(true)
+						.startKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+						.endKey(ComplexKey.of(session.getId()))
+						.includeDocs(true),
+				Comment.class);
+//		for (Comment comment : comments) {
+//			comment.setSessionId(session.getKeyword());
+//		}
+
+		return comments;
+	}
+
+	@Override
+	public List<Comment> getInterposedQuestions(final Session session, final User user, final int start, final int limit) {
+		final int qSkip = start > 0 ? start : -1;
+		final int qLimit = limit > 0 ? limit : -1;
+
+		final List<Comment> comments = db.queryView(createQuery("by_sessionid_creator_timestamp")
+						.skip(qSkip)
+						.limit(qLimit)
+						.descending(true)
+						.startKey(ComplexKey.of(session.getId(), user.getUsername(), ComplexKey.emptyObject()))
+						.endKey(ComplexKey.of(session.getId(), user.getUsername()))
+						.includeDocs(true),
+				Comment.class);
+//		for (Comment comment : comments) {
+//			comment.setSessionId(session.getKeyword());
+//		}
+
+		return comments;
+	}
+
+	@Override
+	public Comment getInterposedQuestion(final String commentId) {
+		try {
+			final Comment comment = get(commentId);
+			/* TODO: Refactor code so the next line can be removed */
+			//comment.setSessionId(sessionRepository.getSessionFromKeyword(comment.getSessionId()).getId());
+			return comment;
+		} catch (final DocumentNotFoundException e) {
+			logger.error("Could not load comment {}.", commentId, e);
+		}
+		return null;
+	}
+
+	@Override
+	public Comment saveQuestion(final Session session, final Comment comment, User user) {
+		comment.setSessionId(session.getId());
+		comment.setCreator(user.getUsername());
+		comment.setRead(false);
+		if (comment.getTimestamp() == 0) {
+			comment.setTimestamp(System.currentTimeMillis());
+		}
+		try {
+			db.create(comment);
+
+			return comment;
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not save comment {}.", comment, e);
+		}
+
+		return null;
+	}
+
+	@Override
+	public void markInterposedQuestionAsRead(final Comment comment) {
+		try {
+			comment.setRead(true);
+			db.update(comment);
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not mark comment as read {}.", comment.getId(), e);
+		}
+	}
+
+	@Override
+	public void deleteInterposedQuestion(final Comment comment) {
+		try {
+			db.delete(comment.getId(), comment.getRevision());
+			dbLogger.log("delete", "type", "comment");
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not delete comment {}.", comment.getId(), e);
+		}
+	}
+
+	@Override
+	public int deleteAllInterposedQuestions(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid").key(session.getId()));
+
+		return deleteAllInterposedQuestions(session, result);
+	}
+
+	@Override
+	public int deleteAllInterposedQuestions(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_creator_read")
+				.startKey(ComplexKey.of(session.getId(), user.getUsername()))
+				.endKey(ComplexKey.of(session.getId(), user.getUsername(), ComplexKey.emptyObject())));
+
+		return deleteAllInterposedQuestions(session, result);
+	}
+
+	private int deleteAllInterposedQuestions(final Session session, final ViewResult comments) {
+		if (comments.isEmpty()) {
+			return 0;
+		}
+		/* TODO: use bulk delete */
+		for (final ViewResult.Row row : comments.getRows()) {
+			try {
+				db.delete(row.getId(), row.getValueAsNode().get("rev").asText());
+			} catch (final UpdateConflictException e) {
+				logger.error("Could not delete all comments {}.", session, e);
+			}
+		}
+
+		/* This does account for failed deletions */
+		dbLogger.log("delete", "type", "comment", "commentCount", comments.getSize());
+
+		return comments.getSize();
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e91fb34eb68dff249749afbdc38ec0e405eff10
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java
@@ -0,0 +1,514 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.persistance.AnswerRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.DocumentNotFoundException;
+import org.ektorp.UpdateConflictException;
+import org.ektorp.ViewQuery;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class CouchDbContentRepository extends CouchDbRepositorySupport<Content> implements ContentRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbContentRepository.class);
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	@Autowired
+	private AnswerRepository answerRepository;
+
+	public CouchDbContentRepository(Class<Content> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Cacheable("skillquestions")
+	@Override
+	public List<Content> getSkillQuestionsForUsers(final Session session) {
+		final List<Content> contents = new ArrayList<>();
+		final List<Content> questions1 = getQuestions(session.getId(), "lecture", true);
+		final List<Content> questions2 = getQuestions(session.getId(), "preparation", true);
+		final List<Content> questions3 = getQuestions(session.getId(), "flashcard", true);
+		contents.addAll(questions1);
+		contents.addAll(questions2);
+		contents.addAll(questions3);
+
+		return contents;
+	}
+
+	@Cacheable("skillquestions")
+	@Override
+	public List<Content> getSkillQuestionsForTeachers(final Session session) {
+		return getQuestions(new Object[] {session.getId()}, session);
+	}
+
+	@Override
+	public int getSkillQuestionCount(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
+
+		return result.getSize();
+	}
+
+	@Caching(evict = {@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session", condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", key = "#session", condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", key = "#session", condition = "#content.getQuestionVariant().equals('flashcard')") },
+			put = {@CachePut(value = "questions", key = "#content.id")})
+	@Override
+	public Content saveQuestion(final Session session, final Content content) {
+		content.setSessionId(session.getId());
+		try {
+			db.create(content);
+
+			return content;
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not save content {}.", content, e);
+		}
+
+		return null;
+	}
+
+	/* TODO: Only evict cache entry for the content's session. This requires some refactoring. */
+	@Caching(evict = {@CacheEvict(value = "skillquestions", allEntries = true),
+			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('flashcard')") },
+			put = {@CachePut(value = "questions", key = "#content.id")})
+	@Override
+	public Content updateQuestion(final Content content) {
+		try {
+			/* TODO: Make sure that sessionId is valid before so the content does not need to be retrieved. */
+			final Content oldContent = get(content.getId());
+			content.setId(oldContent.getId());
+			content.setRevision(oldContent.getRevision());
+			content.updateRoundManagementState();
+			update(content);
+
+			return content;
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not update content {}.", content, e);
+		}
+
+		return null;
+	}
+
+	@Cacheable("questions")
+	@Override
+	public Content getQuestion(final String id) {
+		try {
+			final Content content = get(id);
+			content.updateRoundManagementState();
+			//content.setSessionKeyword(sessionRepository.getSessionFromId(content.getSessionId()).getKeyword());
+
+			return content;
+		} catch (final DocumentNotFoundException e) {
+			logger.error("Could not get question {}.", id, e);
+		}
+
+		return null;
+	}
+
+	@Override
+	public List<String> getQuestionIds(final Session session, final User user) {
+		return collectQuestionIds(db.queryView(createQuery("by_sessionid_variant_active").key(session.getId())));
+	}
+
+	/* TODO: Only evict cache entry for the content's session. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", key = "#content.id"),
+			@CacheEvict(value = "skillquestions", allEntries = true),
+			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('flashcard')") })
+	@Override
+	public int deleteQuestionWithAnswers(final Content content) {
+		try {
+			int count = answerRepository.deleteAnswers(content);
+			db.delete(content);
+			dbLogger.log("delete", "type", "content", "answerCount", count);
+
+			return count;
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not delete content {}.", content.getId(), e);
+		}
+
+		return 0;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public int[] deleteAllQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	private int[] deleteAllQuestionDocumentsWithAnswers(final ViewResult viewResult) {
+		List<Content> contents = new ArrayList<>();
+		for (final ViewResult.Row row : viewResult.getRows()) {
+			final Content q = new Content();
+			q.setId(row.getId());
+			q.setRevision(row.getValueAsNode().get("_rev").asText());
+			contents.add(q);
+		}
+
+		int[] count = answerRepository.deleteAllAnswersWithQuestions(contents);
+		dbLogger.log("delete", "type", "question", "questionCount", count[0]);
+		dbLogger.log("delete", "type", "answer", "answerCount", count[1]);
+
+		return count;
+	}
+
+	@Override
+	public List<String> getUnAnsweredQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_by_user_sessionid_variant")
+				.designDocId("_design/Answer")
+				.startKey(ComplexKey.of(user.getUsername(), session.getId()))
+				.endKey(ComplexKey.of(user.getUsername(), session.getId(), ComplexKey.emptyObject())));
+		List<String> answeredIds = new ArrayList<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredIds.add(row.getId());
+		}
+		return collectUnansweredQuestionIds(getQuestionIds(session, user), answeredIds);
+	}
+
+	@Override
+	public List<String> getUnAnsweredLectureQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_piround_by_user_sessionid_variant")
+				.designDocId("_design/Answer")
+				.key(ComplexKey.of(user.getUsername(), session.getId(), "lecture")));
+		Map<String, Integer> answeredQuestions = new HashMap<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
+		}
+
+		return collectUnansweredQuestionIdsByPiRound(getLectureQuestionsForUsers(session), answeredQuestions);
+	}
+
+	@Override
+	public List<String> getUnAnsweredPreparationQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_piround_by_user_sessionid_variant")
+				.designDocId("_design/Answer")
+				.key(ComplexKey.of(user.getUsername(), session.getId(), "preparation")));
+		Map<String, Integer> answeredQuestions = new HashMap<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
+		}
+
+		return collectUnansweredQuestionIdsByPiRound(getPreparationQuestionsForUsers(session), answeredQuestions);
+	}
+
+	@Cacheable("lecturequestions")
+	@Override
+	public List<Content> getLectureQuestionsForUsers(final Session session) {
+		return getQuestions(session.getId(), "lecture", true);
+	}
+
+	@Override
+	public List<Content> getLectureQuestionsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "lecture");
+	}
+
+	@Cacheable("flashcardquestions")
+	@Override
+	public List<Content> getFlashcardsForUsers(final Session session) {
+		return getQuestions(session.getId(), "flashcard", true);
+	}
+
+	@Override
+	public List<Content> getFlashcardsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "flashcard");
+	}
+
+	@Cacheable("preparationquestions")
+	@Override
+	public List<Content> getPreparationQuestionsForUsers(final Session session) {
+		return getQuestions(session.getId(), "preparation", true);
+	}
+
+	@Override
+	public List<Content> getPreparationQuestionsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "preparation");
+	}
+
+	@Override
+	public List<Content> getAllSkillQuestions(final Session session) {
+		return getQuestions(session.getId());
+	}
+
+	@Override
+	public List<Content> getQuestions(final Object... keys) {
+		Object[] endKeys = Arrays.copyOf(keys, keys.length + 1);
+		endKeys[keys.length] = ComplexKey.emptyObject();
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.includeDocs(true)
+						.reduce(false)
+						.startKey(ComplexKey.of(keys))
+						.endKey(ComplexKey.of(endKeys)),
+				Content.class);
+		for (Content content : contents) {
+			content.updateRoundManagementState();
+			//content.setSessionKeyword(session.getKeyword());
+		}
+
+		return contents;
+	}
+
+	@Override
+	public int getLectureQuestionCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "lecture"))
+				.endKey(ComplexKey.of(session.getId(), "lecture", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getFlashcardCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "flashcard"))
+				.endKey(ComplexKey.of(session.getId(), "flashcard", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getPreparationQuestionCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "preparation"))
+				.endKey(ComplexKey.of(session.getId(), "preparation", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("lecturequestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllLectureQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "lecture"))
+				.endKey(ComplexKey.of(session.getId(), "lecture", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("flashcardquestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllFlashcardsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "flashcard"))
+				.endKey(ComplexKey.of(session.getId(), "flashcard", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("preparationquestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllPreparationQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "preparation"))
+				.endKey(ComplexKey.of(session.getId(), "preparation", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	private List<String> collectUnansweredQuestionIds(
+			final List<String> questions,
+			final List<String> answeredQuestions
+	) {
+		final List<String> unanswered = new ArrayList<>();
+		for (final String questionId : questions) {
+			if (!answeredQuestions.contains(questionId)) {
+				unanswered.add(questionId);
+			}
+		}
+		return unanswered;
+	}
+
+	private List<String> collectUnansweredQuestionIdsByPiRound(
+			final List<Content> contents,
+			final Map<String, Integer> answeredQuestions
+	) {
+		final List<String> unanswered = new ArrayList<>();
+
+		for (final Content content : contents) {
+			if (!"slide".equals(content.getQuestionType()) && (!answeredQuestions.containsKey(content.getId())
+					|| (answeredQuestions.containsKey(content.getId()) && answeredQuestions.get(content.getId()) != content.getPiRound()))) {
+				unanswered.add(content.getId());
+			}
+		}
+
+		return unanswered;
+	}
+
+	private List<String> collectQuestionIds(final ViewResult viewResult) {
+		final List<String> ids = new ArrayList<>();
+		for (final ViewResult.Row row : viewResult.getRows()) {
+			ids.add(row.getId());
+		}
+		return ids;
+	}
+
+	@Override
+	public List<Content> publishAllQuestions(final Session session, final boolean publish) {
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.startKey(ComplexKey.of(session.getId()))
+						.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())),
+				Content.class);
+		/* FIXME: caching */
+		publishQuestions(session, publish, contents);
+
+		return contents;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void publishQuestions(final Session session, final boolean publish, List<Content> contents) {
+		for (final Content content : contents) {
+			content.setActive(publish);
+		}
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk publish all contents.", e);
+		}
+	}
+
+	@Override
+	public List<Content> setVotingAdmissionForAllQuestions(final Session session, final boolean disableVoting) {
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.startKey(ComplexKey.of(session.getId()))
+						.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+						.includeDocs(true),
+				Content.class);
+		/* FIXME: caching */
+		setVotingAdmissions(session, disableVoting, contents);
+
+		return contents;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void setVotingAdmissions(final Session session, final boolean disableVoting, List<Content> contents) {
+		for (final Content q : contents) {
+			if (!"flashcard".equals(q.getQuestionType())) {
+				q.setVotingDisabled(disableVoting);
+			}
+		}
+
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk set voting admission for all contents.", e);
+		}
+	}
+
+	/* TODO: remove if this method is no longer used */
+	@Override
+	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), questionVariant, 1, subject))
+				.endKey(ComplexKey.of(session.getId(), questionVariant, 1, subject, ComplexKey.emptyObject())));
+
+		List<String> qids = new ArrayList<>();
+
+		for (final ViewResult.Row row : result.getRows()) {
+			final String s = row.getId();
+			qids.add(s);
+		}
+
+		return qids;
+	}
+
+	@Override
+	public List<Content> getQuestionsByIds(List<String> ids, final Session session) {
+		return db.queryView(new ViewQuery().allDocs().keys(ids).includeDocs(true), Content.class);
+	}
+
+	@Override
+	public List<String> getSubjects(Session session, String questionVariant) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), questionVariant))
+				.endKey(ComplexKey.of(session.getId(), questionVariant, ComplexKey.emptyObject())));
+
+		Set<String> uniqueSubjects = new HashSet<>();
+
+		for (final ViewResult.Row row : result.getRows()) {
+			uniqueSubjects.add(row.getKeyAsNode().get(3).asText());
+		}
+
+		return new ArrayList<>(uniqueSubjects);
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void resetQuestionsRoundState(final Session session, List<Content> contents) {
+		for (final Content q : contents) {
+			q.setSessionId(session.getId());
+			q.resetQuestionState();
+		}
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk reset all contents round state.", e);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbLogEntryRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbLogEntryRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..45f802a232fcdb7875e54584c7cad17a1ad7f9b7
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbLogEntryRepository.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.entities.LogEntry;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+public class CouchDbLogEntryRepository extends CouchDbRepositorySupport<LogEntry> implements LogEntryRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbLogEntryRepository.class);
+
+	public CouchDbLogEntryRepository(Class<LogEntry> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	public void create(String event, LogEntry.LogLevel level, Map<String, Object> payload) {
+		LogEntry log = new LogEntry(event, level.ordinal(), payload);
+		try {
+			db.create(log);
+		} catch (final IllegalArgumentException e) {
+			logger.error("Logging of '{}' event to database failed.", event, e);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdListRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdListRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..05bef30ab55e18cd69d2b6576bb54399c302c312
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdListRepository.java
@@ -0,0 +1,46 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.entities.MotdList;
+import de.thm.arsnova.persistance.MotdListRepository;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+
+import java.util.List;
+
+public class CouchDbMotdListRepository extends CouchDbRepositorySupport<MotdList> implements MotdListRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbMotdListRepository.class);
+
+	public CouchDbMotdListRepository(Class<MotdList> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motdlist", key = "#p0")
+	public MotdList getMotdListForUser(final String username) {
+		List<MotdList> motdListList = queryView("by_username", username);
+		return motdListList.isEmpty() ? new MotdList() : motdListList.get(0);
+	}
+
+	@Override
+	@CachePut(cacheNames = "motdlist", key = "#p0.username")
+	public MotdList createOrUpdateMotdList(MotdList motdlist) {
+		try {
+			if (motdlist.getId() != null) {
+				update(motdlist);
+			} else {
+				db.create(motdlist);
+			}
+
+			return motdlist;
+		} catch (DbAccessException e) {
+			logger.error("Could not save MotD list {}.", motdlist, e);
+		}
+
+		return null;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..4aafdb0685fe201d4ff4479cc9cda8a517e1308c
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java
@@ -0,0 +1,121 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.persistance.MotdRepository;
+import de.thm.arsnova.services.ISessionService;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CouchDbMotdRepository extends CouchDbRepositorySupport<Motd> implements MotdRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbMotdRepository.class);
+
+	@Autowired
+	private ISessionService sessionService;
+
+	public CouchDbMotdRepository(Class<Motd> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	public List<Motd> getAdminMotds() {
+		return getMotds("by_audience_for_global", null);
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motds", key = "'all'")
+	public List<Motd> getMotdsForAll() {
+		return getMotds("by_audience_for_global", "all");
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motds", key = "'loggedIn'")
+	public List<Motd> getMotdsForLoggedIn() {
+		return getMotds("by_audience_for_global", "loggedIn");
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motds", key = "'tutors'")
+	public List<Motd> getMotdsForTutors() {
+		final List<Motd> union = new ArrayList<>();
+		union.addAll(getMotds("by_audience_for_global", "loggedIn"));
+		union.addAll(getMotds("by_audience_for_global", "tutors"));
+
+		return union;
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motds", key = "'students'")
+	public List<Motd> getMotdsForStudents() {
+		final List<Motd> union = new ArrayList<>();
+		union.addAll(getMotds("by_audience_for_global", "loggedIn"));
+		union.addAll(getMotds("by_audience_for_global", "students"));
+
+		return union;
+	}
+
+	@Override
+	@Cacheable(cacheNames = "motds", key = "('session').concat(#p0)")
+	public List<Motd> getMotdsForSession(final String sessionkey) {
+		return getMotds("by_sessionkey", sessionkey);
+	}
+
+	private List<Motd> getMotds(String viewName, String key) {
+		return queryView(viewName, key);
+	}
+
+	@Override
+	public Motd getMotdByKey(String key) {
+		List<Motd> motd = queryView("by_motdkey", key);
+
+		return motd.get(0);
+	}
+
+	@Override
+	@CacheEvict(cacheNames = "motds", key = "#p0.audience.concat(#p0.sessionkey)")
+	public Motd createOrUpdateMotd(Motd motd) {
+		String id = motd.getId();
+		String rev = motd.getRevision();
+
+		if (null != id) {
+			Motd oldMotd = get(id);
+			motd.setMotdkey(oldMotd.getMotdkey());
+			update(motd);
+		} else {
+			motd.setMotdkey(sessionService.generateKeyword());
+			add(motd);
+		}
+
+		return motd;
+	}
+
+	@Override
+	@CacheEvict(cacheNames = "motds", key = "#p0.audience.concat(#p0.sessionkey)")
+	public boolean deleteMotd(Motd motd) {
+		return db.delete(motd) != null;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..7689fe39c2fd6508fd987de2898ffd9f9c5fcf79
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
@@ -0,0 +1,666 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.connector.model.Course;
+import de.thm.arsnova.entities.LoggedIn;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.SessionInfo;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.VisitedSession;
+import de.thm.arsnova.entities.transport.Comment;
+import de.thm.arsnova.entities.transport.ImportExportSession;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import de.thm.arsnova.persistance.MotdRepository;
+import de.thm.arsnova.persistance.SessionRepository;
+import de.thm.arsnova.services.ISessionService;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DocumentNotFoundException;
+import org.ektorp.UpdateConflictException;
+import org.ektorp.ViewQuery;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
+
+import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session> implements SessionRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbSessionRepository.class);
+
+	@Autowired
+	private ISessionService sessionService;
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	@Autowired
+	private MotdRepository motdRepository;
+
+	public CouchDbSessionRepository(Class<Session> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	@Cacheable("sessions")
+	public Session getSessionFromKeyword(final String keyword) {
+		final List<Session> session = queryView("by_keyword", keyword);
+
+		return !session.isEmpty() ? session.get(0) : null;
+	}
+
+	@Override
+	@Cacheable("sessions")
+	public Session getSessionFromId(final String sessionId) {
+		return get(sessionId);
+	}
+
+	@Override
+	@Caching(evict = @CacheEvict(cacheNames = "sessions", key = "#result.keyword"))
+	public Session saveSession(final User user, final Session session) {
+		session.setKeyword(sessionService.generateKeyword());
+		session.setCreator(user.getUsername());
+		session.setActive(true);
+		session.setFeedbackLock(false);
+
+		try {
+			db.create(session);
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not save session to database.", e);
+		}
+
+		return session.getId() != null ? session : null;
+	}
+
+	@Override
+	public boolean sessionKeyAvailable(final String keyword) {
+		return getSessionFromKeyword(keyword) == null;
+	}
+
+	private String getSessionKeyword(final String internalSessionId) throws IOException {
+		final Session session = get(internalSessionId);
+		if (session == null) {
+			logger.error("No session found for internal id {}.", internalSessionId);
+
+			return null;
+		}
+
+		return session.getKeyword();
+	}
+
+	@Override
+	@CachePut(value = "sessions")
+	public Session updateSessionOwnerActivity(final Session session) {
+		try {
+			/* Do not clutter CouchDB. Only update once every 3 hours. */
+			if (session.getLastOwnerActivity() > System.currentTimeMillis() - 3 * 3600000) {
+				return session;
+			}
+
+			session.setLastOwnerActivity(System.currentTimeMillis());
+			update(session);
+
+			return session;
+		} catch (final UpdateConflictException e) {
+			logger.error("Failed to update lastOwnerActivity for session {}.", session, e);
+			return session;
+		}
+	}
+
+	@Override
+	public List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit) {
+		final int qSkip = start > 0 ? start : -1;
+		final int qLimit = limit > 0 ? limit : -1;
+
+		try {
+			ViewResult visitedSessionResult = db.queryView(createQuery("visited_sessions_by_user")
+					.designDocId("_design/LoggedIn").key(username));
+			List<Session> visitedSessions = visitedSessionResult.getRows().stream().map(vs -> {
+				final Session s = new Session();
+				s.setId(vs.getValueAsNode().get("_id").asText());
+				s.setKeyword(vs.getValueAsNode().get("keyword").asText());
+				s.setName(vs.getValueAsNode().get("name").asText());
+
+				return s;
+			}).collect(Collectors.toList());
+
+			if (visitedSessions.isEmpty()) {
+				return new ArrayList<>();
+			}
+
+			// Filter sessions that don't exist anymore, also filter my own sessions
+			final List<Session> result = new ArrayList<>();
+			final List<Session> filteredSessions = new ArrayList<>();
+			for (final Session s : visitedSessions) {
+				try {
+					/* FIXME: caching (getSessionFromKeyword) */
+					final Session session = getSessionFromKeyword(s.getKeyword());
+					if (session != null && !(session.getCreator().equals(username))) {
+						result.add(session);
+					} else {
+						filteredSessions.add(s);
+					}
+				} catch (final NotFoundException e) {
+					filteredSessions.add(s);
+				}
+			}
+			if (filteredSessions.isEmpty()) {
+				return result;
+			}
+			// Update document to remove sessions that don't exist anymore
+				List<VisitedSession> newVisitedSessions = new ArrayList<>();
+				for (final Session s : result) {
+					newVisitedSessions.add(new VisitedSession(s));
+				}
+
+			try {
+				final LoggedIn loggedIn = db.get(LoggedIn.class, visitedSessionResult.getRows().get(0).getId());
+				loggedIn.setVisitedSessions(newVisitedSessions);
+				db.update(loggedIn);
+			} catch (UpdateConflictException e) {
+				logger.error("Could not clean up LoggedIn document of {}.", username, e);
+			}
+
+			return result;
+		} catch (DocumentNotFoundException e) {
+			return new ArrayList<>();
+		}
+	}
+
+	@Override
+	public List<SessionInfo> getMyVisitedSessionsInfo(final User user, final int start, final int limit) {
+		List<Session> sessions = getVisitedSessionsForUsername(user.getUsername(), start, limit);
+		if (sessions.isEmpty()) {
+			return new ArrayList<>();
+		}
+		return this.getInfosForVisitedSessions(sessions, user);
+	}
+
+	@Override
+	public List<Session> getCourseSessions(final List<Course> courses) {
+		return queryView("by_courseid",
+				ComplexKey.of(courses.stream().map(Course::getId).collect(Collectors.toList())));
+	}
+
+	@Override
+	@CachePut(value = "sessions")
+	public Session updateSession(final Session session) {
+		try {
+			update(session);
+
+			return session;
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not update session {}.", session, e);
+		}
+
+		return null;
+	}
+
+	@Override
+	@Caching(evict = { @CacheEvict("sessions"), @CacheEvict(cacheNames = "sessions", key = "#p0.keyword") })
+	public Session changeSessionCreator(final Session session, final String newCreator) {
+		Session s = get(session.getId());
+		s.setCreator(newCreator);
+		try {
+			update(s);
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not update creator for session {}.", session, e);
+		}
+
+		return s;
+	}
+
+	@Override
+	@Caching(evict = { @CacheEvict("sessions"), @CacheEvict(cacheNames = "sessions", key = "#p0.keyword") })
+	public int[] deleteSession(final Session session) {
+		/* FIXME: not yet migrated - move to service layer */
+		throw new UnsupportedOperationException();
+//		int[] count = new int[] {0, 0};
+//		try {
+//			count = deleteAllQuestionsWithAnswers(session);
+//			remove(session);
+//			logger.debug("Deleted session document {} and related data.", session.getId());
+//			dbLogger.log("delete", "type", "session", "id", session.getId());
+//		} catch (final Exception e) {
+//			/* TODO: improve error handling */
+//			logger.error("Could not delete session {}.", session, e);
+//		}
+//
+//		return count;
+	}
+
+	@Override
+	public int[] deleteInactiveGuestSessions(long lastActivityBefore) {
+		ViewResult result = db.queryView(
+				createQuery("by_lastactivity_for_guests").endKey(lastActivityBefore));
+		int[] count = new int[3];
+
+		for (ViewResult.Row row : result.getRows()) {
+			Session s = new Session();
+			s.setId(row.getId());
+			s.setRevision(row.getValueAsNode().get("_rev").asText());
+			int[] qaCount = deleteSession(s);
+			count[1] += qaCount[0];
+			count[2] += qaCount[1];
+		}
+
+		if (!result.isEmpty()) {
+			logger.info("Deleted {} inactive guest sessions.", result.getSize());
+			dbLogger.log("cleanup", "type", "session", "sessionCount", result.getSize(), "questionCount", count[1], "answerCount", count[2]);
+		}
+		count[0] = result.getSize();
+
+		return count;
+	}
+
+	@Override
+	public SessionInfo importSession(User user, ImportExportSession importSession) {
+		/* FIXME: not yet migrated - move to service layer */
+		throw new UnsupportedOperationException();
+//		final Session session = this.saveSession(user, importSession.generateSessionEntity(user));
+//		List<Document> questions = new ArrayList<>();
+//		// We need to remember which answers belong to which question.
+//		// The answers need a questionId, so we first store the questions to get the IDs.
+//		// Then we update the answer objects and store them as well.
+//		Map<Document, ImportExportSession.ImportExportContent> mapping = new HashMap<>();
+//		// Later, generate all answer documents
+//		List<Document> answers = new ArrayList<>();
+//		// We can then push answers together with comments in one large bulk request
+//		List<Document> interposedQuestions = new ArrayList<>();
+//		// Motds shouldn't be forgotten, too
+//		List<Document> motds = new ArrayList<>();
+//		try {
+//			// add session id to all questions and generate documents
+//			for (ImportExportSession.ImportExportContent question : importSession.getQuestions()) {
+//				Document doc = toQuestionDocument(session, question);
+//				question.setSessionId(session.getId());
+//				questions.add(doc);
+//				mapping.put(doc, question);
+//			}
+//			database.bulkSaveDocuments(questions.toArray(new Document[questions.size()]));
+//
+//			// bulk import answers together with interposed questions
+//			for (Map.Entry<Document, ImportExportSession.ImportExportContent> entry : mapping.entrySet()) {
+//				final Document doc = entry.getKey();
+//				final ImportExportSession.ImportExportContent question = entry.getValue();
+//				question.setId(doc.getId());
+//				question.setRevision(doc.getRev());
+//				for (de.thm.arsnova.entities.transport.Answer answer : question.getAnswers()) {
+//					final Answer a = answer.generateAnswerEntity(user, question);
+//					final Document answerDoc = new Document();
+//					answerDoc.put("type", "skill_question_answer");
+//					answerDoc.put("sessionId", a.getSessionId());
+//					answerDoc.put("questionId", a.getQuestionId());
+//					answerDoc.put("answerSubject", a.getAnswerSubject());
+//					answerDoc.put("questionVariant", a.getQuestionVariant());
+//					answerDoc.put("questionValue", a.getQuestionValue());
+//					answerDoc.put("answerText", a.getAnswerText());
+//					answerDoc.put("answerTextRaw", a.getAnswerTextRaw());
+//					answerDoc.put("timestamp", a.getTimestamp());
+//					answerDoc.put("piRound", a.getPiRound());
+//					answerDoc.put("abstention", a.isAbstention());
+//					answerDoc.put("successfulFreeTextAnswer", a.isSuccessfulFreeTextAnswer());
+//					// we do not store the user's name
+//					answerDoc.put("user", "");
+//					answers.add(answerDoc);
+//				}
+//			}
+//			for (de.thm.arsnova.entities.transport.Comment i : importSession.getFeedbackQuestions()) {
+//				final Document q = new Document();
+//				q.put("type", "interposed_question");
+//				q.put("sessionId", session.getId());
+//				q.put("subject", i.getSubject());
+//				q.put("text", i.getText());
+//				q.put("timestamp", i.getTimestamp());
+//				q.put("read", i.isRead());
+//				// we do not store the creator's name
+//				q.put("creator", "");
+//				interposedQuestions.add(q);
+//			}
+//			for (Motd m : importSession.getMotds()) {
+//				final Document d = new Document();
+//				d.put("type", "motd");
+//				d.put("motdkey", m.getMotdkey());
+//				d.put("title", m.getTitle());
+//				d.put("text", m.getText());
+//				d.put("audience", m.getAudience());
+//				d.put("sessionkey", session.getKeyword());
+//				d.put("startdate", String.valueOf(m.getStartdate().getTime()));
+//				d.put("enddate", String.valueOf(m.getEnddate().getTime()));
+//				motds.add(d);
+//			}
+//			List<Document> documents = new ArrayList<>(answers);
+//			database.bulkSaveDocuments(interposedQuestions.toArray(new Document[interposedQuestions.size()]));
+//			database.bulkSaveDocuments(motds.toArray(new Document[motds.size()]));
+//			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
+//		} catch (IOException e) {
+//			logger.error("Could not import session.", e);
+//			// Something went wrong, delete this session since we do not want a partial import
+//			this.deleteSession(session);
+//			return null;
+//		}
+//		return this.calculateSessionInfo(importSession, session);
+	}
+
+	@Override
+	public ImportExportSession exportSession(String sessionkey, Boolean withAnswers, Boolean withFeedbackQuestions) {
+		/* FIXME: not yet migrated - move to service layer */
+		throw new UnsupportedOperationException();
+//		ImportExportSession importExportSession = new ImportExportSession();
+//		Session session = getDatabaseDao().getSessionFromKeyword(sessionkey);
+//		importExportSession.setSessionFromSessionObject(session);
+//		List<Content> questionList = getDatabaseDao().getAllSkillQuestions(session);
+//		for (Content question : questionList) {
+//			List<de.thm.arsnova.entities.transport.Answer> answerList = new ArrayList<>();
+//			if (withAnswers) {
+//				for (Answer a : this.getDatabaseDao().getAllAnswers(question)) {
+//					de.thm.arsnova.entities.transport.Answer transportAnswer = new de.thm.arsnova.entities.transport.Answer(a);
+//					answerList.add(transportAnswer);
+//				}
+//				// getAllAnswers does not grep for whole answer object so i need to add empty entries for abstentions
+//				int i = this.getDatabaseDao().getAbstentionAnswerCount(question.getId());
+//				for (int b = 0; b < i; b++) {
+//					de.thm.arsnova.entities.transport.Answer ans = new de.thm.arsnova.entities.transport.Answer();
+//					ans.setAnswerSubject("");
+//					ans.setAnswerImage("");
+//					ans.setAnswerText("");
+//					ans.setAbstention(true);
+//					answerList.add(ans);
+//				}
+//			}
+//			importExportSession.addQuestionWithAnswers(question, answerList);
+//		}
+//		if (withFeedbackQuestions) {
+//			List<de.thm.arsnova.entities.transport.Comment> interposedQuestionList = new ArrayList<>();
+//			for (Comment i : getDatabaseDao().getInterposedQuestions(session, 0, 0)) {
+//				de.thm.arsnova.entities.transport.Comment transportInterposedQuestion = new de.thm.arsnova.entities.transport.Comment(i);
+//				interposedQuestionList.add(transportInterposedQuestion);
+//			}
+//			importExportSession.setFeedbackQuestions(interposedQuestionList);
+//		}
+//		if (withAnswers) {
+//			importExportSession.setSessionInfo(this.calculateSessionInfo(importExportSession, session));
+//		}
+//		importExportSession.setMotds(motdRepository.getMotdsForSession(session.getKeyword()));
+//		return importExportSession;
+	}
+
+	private SessionInfo calculateSessionInfo(ImportExportSession importExportSession, Session session) {
+		int unreadComments = 0;
+		int numUnanswered = 0;
+		int numAnswers = 0;
+		for (Comment i : importExportSession.getFeedbackQuestions()) {
+			if (!i.isRead()) {
+				unreadComments++;
+			}
+		}
+		for (ImportExportSession.ImportExportContent question : importExportSession.getQuestions()) {
+			numAnswers += question.getAnswers().size();
+			if (question.getAnswers().isEmpty()) {
+				numUnanswered++;
+			}
+		}
+		final SessionInfo info = new SessionInfo(session);
+		info.setNumQuestions(importExportSession.getQuestions().size());
+		info.setNumUnanswered(numUnanswered);
+		info.setNumAnswers(numAnswers);
+		info.setNumInterposed(importExportSession.getFeedbackQuestions().size());
+		info.setNumUnredInterposed(unreadComments);
+		return info;
+	}
+
+	@Override
+	public List<Session> getMySessions(final User user, final int start, final int limit) {
+		return getSessionsForUsername(user.getUsername(), start, limit);
+	}
+
+	@Override
+	public List<Session> getSessionsForUsername(String username, final int start, final int limit) {
+		final int qSkip = start > 0 ? start : -1;
+		final int qLimit = limit > 0 ? limit : -1;
+
+		/* TODO: Only load IDs and check against cache for data. */
+		List<Session> sessions = db.queryView(
+				createQuery("partial_by_sessiontype_creator_name")
+						.skip(qSkip)
+						.limit(qLimit)
+						.startKey(ComplexKey.of(null, username))
+						.endKey(ComplexKey.of(null, username, ComplexKey.emptyObject()))
+						.includeDocs(true),
+				Session.class);
+
+		return sessions;
+	}
+
+	@Override
+	public List<Session> getPublicPoolSessions() {
+		// TODO replace with new view
+		return queryView("partial_by_ppsubject_name_for_publicpool");
+	}
+
+	@Override
+	public List<SessionInfo> getPublicPoolSessionsInfo() {
+		final List<Session> sessions = this.getPublicPoolSessions();
+		return getInfosForSessions(sessions);
+	}
+
+	@Override
+	public List<Session> getMyPublicPoolSessions(final User user) {
+		/* TODO: Only load IDs and check against cache for data. */
+		return db.queryView(
+				createQuery("partial_by_sessiontype_creator_name")
+						.startKey(ComplexKey.of("public_pool", user.getUsername()))
+						.endKey(ComplexKey.of("public_pool", user.getUsername(), ComplexKey.emptyObject()))
+						.includeDocs(true),
+				Session.class);
+	}
+
+	@Override
+	public List<SessionInfo> getMyPublicPoolSessionsInfo(final User user) {
+		final List<Session> sessions = this.getMyPublicPoolSessions(user);
+		if (sessions.isEmpty()) {
+			return new ArrayList<>();
+		}
+		return getInfosForSessions(sessions);
+	}
+
+	@Override
+	public List<SessionInfo> getMySessionsInfo(final User user, final int start, final int limit) {
+		final List<Session> sessions = this.getMySessions(user, start, limit);
+		if (sessions.isEmpty()) {
+			return new ArrayList<>();
+		}
+		return getInfosForSessions(sessions);
+	}
+
+	private List<SessionInfo> getInfosForSessions(final List<Session> sessions) {
+		List<String> sessionIds = sessions.stream().map(Session::getId).collect(Collectors.toList());
+		final ViewQuery questionCountView = createQuery("by_sessionid").designDocId("_design/Content")
+				.group(true).keys(sessionIds);
+		final ViewQuery answerCountView = createQuery("by_sessionid").designDocId("_design/Answer")
+				.group(true).keys(sessionIds);
+		final ViewQuery commentCountView = createQuery("by_sessionid").designDocId("_design/Comment")
+				.group(true).keys(sessionIds);
+		final ViewQuery unreadCommentCountView = createQuery("by_sessionid_read").designDocId("_design/Comment")
+				.group(true).keys(sessions.stream().map(session -> ComplexKey.of(session.getId(), false)).collect(Collectors.toList()));
+
+		return getSessionInfoData(sessions, questionCountView, answerCountView, commentCountView, unreadCommentCountView);
+	}
+
+	private List<SessionInfo> getInfosForVisitedSessions(final List<Session> sessions, final User user) {
+		final ViewQuery answeredQuestionsView = createQuery("by_user_sessionid").designDocId("_design/Answer")
+				.keys(sessions.stream().map(session -> ComplexKey.of(user.getUsername(), session.getId())).collect(Collectors.toList()));
+		final ViewQuery questionIdsView = createQuery("by_sessionid").designDocId("_design/Content")
+				.keys(sessions.stream().map(Session::getId).collect(Collectors.toList()));
+
+		return getVisitedSessionInfoData(sessions, answeredQuestionsView, questionIdsView);
+	}
+
+	private List<SessionInfo> getVisitedSessionInfoData(List<Session> sessions,
+														ViewQuery answeredQuestionsView, ViewQuery questionIdsView) {
+		final Map<String, Set<String>> answeredQuestionsMap = new HashMap<>();
+		final Map<String, Set<String>> questionIdMap = new HashMap<>();
+
+		// Maps a session ID to a set of question IDs of answered questions of that session
+		for (final ViewResult.Row row : db.queryView(answeredQuestionsView).getRows()) {
+			final String sessionId = row.getKey();
+			final String questionId = row.getValue();
+			Set<String> questionIdsInSession = answeredQuestionsMap.get(sessionId);
+			if (questionIdsInSession == null) {
+				questionIdsInSession = new HashSet<>();
+			}
+			questionIdsInSession.add(questionId);
+			answeredQuestionsMap.put(sessionId, questionIdsInSession);
+		}
+
+		// Maps a session ID to a set of question IDs of that session
+		for (final ViewResult.Row row : db.queryView(questionIdsView).getRows()) {
+			final String sessionId = row.getKey();
+			final String questionId = row.getId();
+			Set<String> questionIdsInSession = questionIdMap.get(sessionId);
+			if (questionIdsInSession == null) {
+				questionIdsInSession = new HashSet<>();
+			}
+			questionIdsInSession.add(questionId);
+			questionIdMap.put(sessionId, questionIdsInSession);
+		}
+
+		// For each session, count the question IDs that are not yet answered
+		Map<String, Integer> unansweredQuestionsCountMap = new HashMap<>();
+		for (final Session s : sessions) {
+			if (!questionIdMap.containsKey(s.getId())) {
+				continue;
+			}
+			// Note: create a copy of the first set so that we don't modify the contents in the original set
+			Set<String> questionIdsInSession = new HashSet<>(questionIdMap.get(s.getId()));
+			Set<String> answeredQuestionIdsInSession = answeredQuestionsMap.get(s.getId());
+			if (answeredQuestionIdsInSession == null) {
+				answeredQuestionIdsInSession = new HashSet<>();
+			}
+			questionIdsInSession.removeAll(answeredQuestionIdsInSession);
+			unansweredQuestionsCountMap.put(s.getId(), questionIdsInSession.size());
+		}
+
+		List<SessionInfo> sessionInfos = new ArrayList<>();
+		for (Session session : sessions) {
+			int numUnanswered = 0;
+
+			if (unansweredQuestionsCountMap.containsKey(session.getId())) {
+				numUnanswered = unansweredQuestionsCountMap.get(session.getId());
+			}
+			SessionInfo info = new SessionInfo(session);
+			info.setNumUnanswered(numUnanswered);
+			sessionInfos.add(info);
+		}
+		return sessionInfos;
+	}
+
+	private List<SessionInfo> getSessionInfoData(final List<Session> sessions,
+												 final ViewQuery questionCountView,
+												 final ViewQuery answerCountView,
+												 final ViewQuery commentCountView,
+												 final ViewQuery unreadCommentCountView) {
+		Map<String, Integer> questionCountMap = db.queryView(questionCountView).getRows()
+				.stream().map(row -> new AbstractMap.SimpleImmutableEntry<>(row.getKey(), row.getValueAsInt()))
+				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+		Map<String, Integer> answerCountMap = db.queryView(answerCountView).getRows()
+				.stream().map(row -> new AbstractMap.SimpleImmutableEntry<>(row.getKey(), row.getValueAsInt()))
+				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+		Map<String, Integer> commentCountMap = db.queryView(commentCountView).getRows()
+				.stream().map(row -> new AbstractMap.SimpleImmutableEntry<>(row.getKey(), row.getValueAsInt()))
+				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+		Map<String, Integer> unreadCommentCountMap = db.queryView(unreadCommentCountView).getRows()
+				.stream().map(row -> new AbstractMap.SimpleImmutableEntry<>(row.getKey(), row.getValueAsInt()))
+				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+		List<SessionInfo> sessionInfos = new ArrayList<>();
+		for (Session session : sessions) {
+			int numQuestions = 0;
+			int numAnswers = 0;
+			int numComments = 0;
+			int numUnreadComments = 0;
+			if (questionCountMap.containsKey(session.getId())) {
+				numQuestions = questionCountMap.get(session.getId());
+			}
+			if (answerCountMap.containsKey(session.getId())) {
+				numAnswers = answerCountMap.get(session.getId());
+			}
+			if (commentCountMap.containsKey(session.getId())) {
+				numComments = commentCountMap.get(session.getId());
+			}
+			if (unreadCommentCountMap.containsKey(session.getId())) {
+				numUnreadComments = unreadCommentCountMap.get(session.getId());
+			}
+
+			SessionInfo info = new SessionInfo(session);
+			info.setNumQuestions(numQuestions);
+			info.setNumAnswers(numAnswers);
+			info.setNumInterposed(numComments);
+			info.setNumUnredInterposed(numUnreadComments);
+			sessionInfos.add(info);
+		}
+		return sessionInfos;
+	}
+
+	@Override
+	public LoggedIn registerAsOnlineUser(final User user, final Session session) {
+		LoggedIn loggedIn = new LoggedIn();
+		try {
+			List<LoggedIn> loggedInList = db.queryView(createQuery("all").designDocId("_design/LoggedIn").key(user.getUsername()), LoggedIn.class);
+
+			if (!loggedInList.isEmpty()) {
+				loggedIn = loggedInList.get(0);
+
+				/* Do not clutter CouchDB. Only update once every 3 hours per session. */
+				if (loggedIn.getSessionId().equals(session.getId()) && loggedIn.getTimestamp() > System.currentTimeMillis() - 3 * 3600000) {
+					return loggedIn;
+				}
+			}
+
+			loggedIn.setUser(user.getUsername());
+			loggedIn.setSessionId(session.getId());
+			loggedIn.addVisitedSession(session);
+			loggedIn.updateTimestamp();
+
+			if (loggedIn.getId() == null) {
+				db.create(loggedIn);
+			} else {
+				db.update(loggedIn);
+			}
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not save LoggedIn document of {}.", user.getUsername(), e);
+		}
+
+		return loggedIn;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..be48429e26556dd2de2f14d224a704fd592c2148
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java
@@ -0,0 +1,54 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.thm.arsnova.domain.CourseScore;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.springframework.cache.annotation.Cacheable;
+
+public class CouchDbSessionStatisticsRepository extends CouchDbRepositorySupport implements SessionStatisticsRepository {
+	public CouchDbSessionStatisticsRepository(Class type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, "learning_progress", createIfNotExists);
+	}
+
+	@Cacheable("learningprogress")
+	@Override
+	public CourseScore getLearningProgress(final Session session) {
+		final ViewResult maximumValueResult = db.queryView(createQuery("maximum_value_of_question")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
+		final ViewResult answerSumResult = db.queryView(createQuery("question_value_achieved_for_user")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
+		final CourseScore courseScore = new CourseScore();
+
+		// no results found
+		if (maximumValueResult.isEmpty() && answerSumResult.isEmpty()) {
+			return courseScore;
+		}
+
+		// collect mapping (questionId -> max value)
+		for (ViewResult.Row row : maximumValueResult) {
+			final String questionId = row.getKeyAsNode().get(1).asText();
+			final JsonNode value = row.getValueAsNode();
+			final int questionScore = value.get("value").asInt();
+			final String questionVariant = value.get("questionVariant").asText();
+			final int piRound = value.get("piRound").asInt();
+			courseScore.addQuestion(questionId, questionVariant, piRound, questionScore);
+		}
+		// collect mapping (questionId -> (user -> value))
+		for (ViewResult.Row row : answerSumResult) {
+			final String username = row.getKeyAsNode().get(1).asText();
+			final JsonNode value = row.getValueAsNode();
+			final String questionId = value.get("questionId").asText();
+			final int userscore = value.get("score").asInt();
+			final int piRound = value.get("piRound").asInt();
+			courseScore.addAnswer(questionId, piRound, username, userscore);
+		}
+		return courseScore;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbStatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbStatisticsRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..20ed9067693b9c08dfa11ef22309197432dd5e58
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbStatisticsRepository.java
@@ -0,0 +1,88 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.entities.Statistics;
+import de.thm.arsnova.persistance.StatisticsRepository;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cache.annotation.Cacheable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class CouchDbStatisticsRepository extends CouchDbRepositorySupport implements StatisticsRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbStatisticsRepository.class);
+
+	public CouchDbStatisticsRepository(Class type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, "statistics", createIfNotExists);
+	}
+
+	@Cacheable("statistics")
+	@Override
+	public Statistics getStatistics() {
+		final Statistics stats = new Statistics();
+		try {
+			final ViewResult statsResult = db.queryView(createQuery("statistics").group(true));
+			final ViewResult creatorResult = db.queryView(createQuery("unique_session_creators").group(true));
+			final ViewResult studentUserResult = db.queryView(createQuery("active_student_users").group(true));
+
+			if (!statsResult.isEmpty()) {
+				for (ViewResult.Row row: statsResult.getRows()) {
+					final int value = row.getValueAsInt();
+					switch (row.getKey()) {
+						case "openSessions":
+							stats.setOpenSessions(stats.getOpenSessions() + value);
+							break;
+						case "closedSessions":
+							stats.setClosedSessions(stats.getClosedSessions() + value);
+							break;
+						case "deletedSessions":
+						/* Deleted sessions are not exposed separately for now. */
+							stats.setClosedSessions(stats.getClosedSessions() + value);
+							break;
+						case "answers":
+							stats.setAnswers(stats.getAnswers() + value);
+							break;
+						case "lectureQuestions":
+							stats.setLectureQuestions(stats.getLectureQuestions() + value);
+							break;
+						case "preparationQuestions":
+							stats.setPreparationQuestions(stats.getPreparationQuestions() + value);
+							break;
+						case "interposedQuestions":
+							stats.setInterposedQuestions(stats.getInterposedQuestions() + value);
+							break;
+						case "conceptQuestions":
+							stats.setConceptQuestions(stats.getConceptQuestions() + value);
+							break;
+						case "flashcards":
+							stats.setFlashcards(stats.getFlashcards() + value);
+							break;
+					}
+				}
+			}
+			if (!creatorResult.isEmpty()) {
+				Set<String> creators = new HashSet<>();
+				for (ViewResult.Row row: statsResult.getRows()) {
+					creators.add(row.getKey());
+				}
+				stats.setCreators(creators.size());
+			}
+			if (!studentUserResult.isEmpty()) {
+				Set<String> students = new HashSet<>();
+				for (ViewResult.Row row: statsResult.getRows()) {
+					students.add(row.getKey());
+				}
+				stats.setActiveStudents(students.size());
+			}
+			return stats;
+		} catch (final DbAccessException e) {
+			logger.error("Could not retrieve statistics.", e);
+		}
+
+		return stats;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbUserRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbUserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..91d280d487187c15b1b466dd73522c569316f442
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbUserRepository.java
@@ -0,0 +1,110 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.persistance.couchdb;
+
+import com.google.common.collect.Lists;
+import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.persistance.UserRepository;
+import org.ektorp.BulkDeleteDocument;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DocumentOperationResult;
+import org.ektorp.ViewQuery;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CouchDbUserRepository extends CouchDbRepositorySupport<DbUser> implements UserRepository {
+	private static final int BULK_PARTITION_SIZE = 500;
+
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbUserRepository.class);
+
+	public CouchDbUserRepository(Class<DbUser> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	private void log(Object... strings) {
+		/* TODO: method stub */
+	}
+
+	@Override
+	public DbUser createOrUpdateUser(final DbUser user) {
+		String id = user.getId();
+
+		if (null != id) {
+			db.update(user);
+		}
+
+		db.create(user);
+
+		return user;
+	}
+
+	@Override
+	public DbUser findUserByUsername(String username) {
+		List<DbUser> users = queryView("by_username", username);
+
+		return !users.isEmpty() ? users.get(0) : null;
+	}
+
+	@Override
+	public boolean deleteUser(final DbUser user) {
+		if (db.delete(user) != null) {
+			log("delete", "type", "user", "id", user.getId());
+			return true;
+		} else {
+			logger.error("Could not delete user {}", user.getId());
+			return false;
+		}
+	}
+
+	@Override
+	public int deleteInactiveUsers(long lastActivityBefore) {
+		ViewQuery q = createQuery("by_creation_for_inactive").endKey(lastActivityBefore);
+		List<ViewResult.Row> rows = db.queryView(q).getRows();
+
+		int count = 0;
+		final List<List<ViewResult.Row>> partitions = Lists.partition(rows, BULK_PARTITION_SIZE);
+		for (List<ViewResult.Row> partition: partitions) {
+			final List<BulkDeleteDocument> newDocs = new ArrayList<>();
+			for (ViewResult.Row oldDoc : partition) {
+				final BulkDeleteDocument newDoc = new BulkDeleteDocument(oldDoc.getId(), oldDoc.getValue());
+				newDocs.add(newDoc);
+				logger.debug("Marked user document {} for deletion.", oldDoc.getId());
+			}
+
+			if (newDocs.size() > 0) {
+				List<DocumentOperationResult> results = db.executeBulk(newDocs);
+				if (!results.isEmpty()) {
+					/* TODO: This condition should be improved so that it checks the operation results. */
+					count += newDocs.size();
+				}
+			}
+		}
+
+		if (count > 0) {
+			logger.info("Deleted {} inactive users.", count);
+			log("cleanup", "type", "user", "count", count);
+		}
+
+		return count;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbVisitedSessionRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbVisitedSessionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..e682387a7a177cec90a529bbced532dec7af4c3c
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbVisitedSessionRepository.java
@@ -0,0 +1,70 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import com.google.common.collect.Lists;
+import de.thm.arsnova.entities.VisitedSession;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import de.thm.arsnova.persistance.VisitedSessionRepository;
+import org.ektorp.BulkDeleteDocument;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.DocumentOperationResult;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CouchDbVisitedSessionRepository extends CouchDbRepositorySupport<VisitedSession> implements VisitedSessionRepository {
+	private static final int BULK_PARTITION_SIZE = 500;
+
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbVisitedSessionRepository.class);
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	public CouchDbVisitedSessionRepository(Class<VisitedSession> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Override
+	public int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore) {
+		try {
+			ViewResult result = db.queryView(createQuery("by_last_activity_for_guests").endKey(lastActivityBefore));
+
+			int count = 0;
+			List<List<ViewResult.Row>> partitions = Lists.partition(result.getRows(), BULK_PARTITION_SIZE);
+			for (List<ViewResult.Row> partition: partitions) {
+				final List<BulkDeleteDocument> newDocs = new ArrayList<>();
+				for (final ViewResult.Row oldDoc : partition) {
+					final BulkDeleteDocument newDoc = new BulkDeleteDocument(oldDoc.getId(), oldDoc.getValueAsNode().get("_rev").asText());
+					newDocs.add(newDoc);
+					logger.debug("Marked logged_in document {} for deletion.", oldDoc.getId());
+					/* Use log type 'user' since effectively the user is deleted in case of guests */
+					dbLogger.log("delete", "type", "user", "id", oldDoc.getId());
+				}
+
+				if (!newDocs.isEmpty()) {
+					List<DocumentOperationResult> results = db.executeBulk(newDocs);
+					count += newDocs.size() - results.size();
+					if (!results.isEmpty()) {
+						logger.error("Could not bulk delete some visited session lists.");
+					}
+				}
+			}
+
+			if (count > 0) {
+				logger.info("Deleted {} visited session lists of inactive users.", count);
+				dbLogger.log("cleanup", "type", "visitedsessions", "count", count);
+			}
+
+			return count;
+		} catch (DbAccessException e) {
+			logger.error("Could not delete visited session lists of inactive users.", e);
+		}
+
+		return 0;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/InitializingCouchDbConnector.java b/src/main/java/de/thm/arsnova/persistance/couchdb/InitializingCouchDbConnector.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6675a48d380aa840ab07440b3d39d65fb4c6511
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/InitializingCouchDbConnector.java
@@ -0,0 +1,85 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.ektorp.CouchDbInstance;
+import org.ektorp.impl.ObjectMapperFactory;
+import org.ektorp.impl.StdCouchDbConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.util.FileCopyUtils;
+
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class InitializingCouchDbConnector extends StdCouchDbConnector implements InitializingBean, ResourceLoaderAware {
+	private static final Logger logger = LoggerFactory.getLogger(InitializingCouchDbConnector.class);
+	private final List<Bindings> docs = new ArrayList<>();
+
+	private ResourceLoader resourceLoader;
+
+	public InitializingCouchDbConnector(String databaseName, CouchDbInstance dbInstance) {
+		super(databaseName, dbInstance);
+	}
+
+	public InitializingCouchDbConnector(String databaseName, CouchDbInstance dbi, ObjectMapperFactory om) {
+		super(databaseName, dbi, om);
+	}
+
+	protected void loadDesignDocFiles() throws IOException, ScriptException {
+		final ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("application/javascript");
+		engine.eval(new InputStreamReader(new ClassPathResource("couchdb/jsToJson.js").getInputStream()));
+
+		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
+		Resource[] resources = resolver.getResources("classpath:couchdb/*.design.js");
+		for (Resource resource : resources) {
+			logger.debug("Loading CouchDB design doc: {}", resource.getFilename());
+			String js = FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream()));
+			/* Reset designDoc before parsing a new one. */
+			engine.eval("var designDoc = null;" + js);
+			Bindings jsonObject = (Bindings) engine.eval("jsToJson(designDoc)");
+			docs.add(jsonObject);
+		}
+	}
+
+	protected void createDesignDocs() {
+		docs.forEach(doc -> {
+			if (logger.isDebugEnabled()) {
+				try {
+					logger.debug("Creating design doc:\n{}", objectMapper.writeValueAsString(doc));
+				} catch (JsonProcessingException e) {
+					logger.warn("Failed to serialize design doc.", e);
+				}
+			}
+			String rev = getCurrentRevision((String) doc.get("_id"));
+			if (rev == null) {
+				create(doc);
+			} else {
+				doc.put("_rev", rev);
+				update(doc);
+			}
+		});
+	}
+
+	@Override
+	public void afterPropertiesSet() throws Exception {
+		loadDesignDocFiles();
+		createDesignDocs();
+	}
+
+	@Override
+	public void setResourceLoader(ResourceLoader resourceLoader) {
+		this.resourceLoader = resourceLoader;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
index df5f6c9f83070c7e1648fca4c0dd125489683351..fd72edb9c147432e3fa754f924cea85caac97b32 100644
--- a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
+++ b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
@@ -17,12 +17,14 @@
  */
 package de.thm.arsnova.security;
 
-import de.thm.arsnova.dao.IDatabaseDao;
-import de.thm.arsnova.entities.InterposedQuestion;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.SessionRepository;
 import org.pac4j.oauth.profile.facebook.FacebookProfile;
 import org.pac4j.oauth.profile.google2.Google2Profile;
 import org.pac4j.oauth.profile.twitter.TwitterProfile;
@@ -45,7 +47,13 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 	private String[] adminAccounts;
 
 	@Autowired
-	private IDatabaseDao dao;
+	private SessionRepository sessionRepository;
+
+	@Autowired
+	private CommentRepository commentRepository;
+
+	@Autowired
+	private ContentRepository contentRepository;
 
 	@Override
 	public boolean hasPermission(
@@ -80,12 +88,12 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 				&& checkSessionPermission(username, targetId, permission)) {
 			return true;
 		} else if (
-				"question".equals(targetType)
+				"content".equals(targetType)
 				&& checkQuestionPermission(username, targetId, permission)
 				) {
 			return true;
 		} else if (
-				"interposedquestion".equals(targetType)
+				"comment".equals(targetType)
 				&& checkInterposedQuestionPermission(username, targetId, permission)
 				) {
 			return true;
@@ -104,9 +112,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 			final Object permission
 			) {
 		if (permission instanceof String && ("owner".equals(permission) || "write".equals(permission))) {
-			return dao.getSessionFromKeyword(targetId.toString()).getCreator().equals(username);
+			return sessionRepository.getSessionFromKeyword(targetId.toString()).getCreator().equals(username);
 		} else if (permission instanceof String && "read".equals(permission)) {
-			return dao.getSessionFromKeyword(targetId.toString()).isActive();
+			return sessionRepository.getSessionFromKeyword(targetId.toString()).isActive();
 		}
 		return false;
 	}
@@ -117,9 +125,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 			final Object permission
 			) {
 		if (permission instanceof String && "owner".equals(permission)) {
-			final Question question = dao.getQuestion(targetId.toString());
-			if (question != null) {
-				final Session session = dao.getSessionFromId(question.getSessionId());
+			final Content content = contentRepository.getQuestion(targetId.toString());
+			if (content != null) {
+				final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 				return session != null && session.getCreator().equals(username);
 			}
@@ -133,14 +141,14 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 			final Object permission
 			) {
 		if (permission instanceof String && "owner".equals(permission)) {
-			final InterposedQuestion question = dao.getInterposedQuestion(targetId.toString());
-			if (question != null) {
-				// Does the creator want to delete his own question?
-				if (question.getCreator() != null && question.getCreator().equals(username)) {
+			final Comment comment = commentRepository.getInterposedQuestion(targetId.toString());
+			if (comment != null) {
+				// Does the creator want to delete his own comment?
+				if (comment.getCreator() != null && comment.getCreator().equals(username)) {
 					return true;
 				}
 				// Allow deletion if requested by session owner
-				final Session session = dao.getSessionFromKeyword(question.getSessionId());
+				final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
 
 				return session != null && session.getCreator().equals(username);
 			}
diff --git a/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java b/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java
index cb1000e97b2668b3071b7ded3a8b989974452481..8e3b308cb42e8dd9ff1cdd5e7fbc1ea593b6566d 100644
--- a/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java
+++ b/src/main/java/de/thm/arsnova/security/DbUserDetailsService.java
@@ -17,8 +17,8 @@
  */
 package de.thm.arsnova.security;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.persistance.UserRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,7 +39,7 @@ import java.util.List;
 @Service
 public class DbUserDetailsService implements UserDetailsService {
 	@Autowired
-	private IDatabaseDao dao;
+	private UserRepository userRepository;
 
 	private static final Logger logger = LoggerFactory
 			.getLogger(DbUserDetailsService.class);
@@ -48,7 +48,7 @@ public class DbUserDetailsService implements UserDetailsService {
 	public UserDetails loadUserByUsername(String username) {
 		String uid = username.toLowerCase();
 		logger.debug("Load user: " + uid);
-		DbUser dbUser = dao.getUser(uid);
+		DbUser dbUser = userRepository.findUserByUsername(uid);
 		if (null == dbUser) {
 			throw new UsernameNotFoundException("User does not exist.");
 		}
diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/ContentService.java
similarity index 56%
rename from src/main/java/de/thm/arsnova/services/QuestionService.java
rename to src/main/java/de/thm/arsnova/services/ContentService.java
index aa1e999c4db651d3d0345bb4aaad80e61c5e208d..2191d8cf7d1216bd621fbe8bb1ad194f8a0bffb9 100644
--- a/src/main/java/de/thm/arsnova/services/QuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/ContentService.java
@@ -18,11 +18,10 @@
 package de.thm.arsnova.services;
 
 import de.thm.arsnova.ImageUtils;
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.InterposedQuestion;
-import de.thm.arsnova.entities.InterposedReadingCount;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.events.*;
@@ -30,6 +29,10 @@ import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.AnswerRepository;
+import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.SessionRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -48,16 +51,24 @@ import java.util.Timer;
 import java.util.TimerTask;
 
 /**
- * Performs all question, interposed question, and answer related operations.
+ * Performs all question, comment, and answer related operations.
  */
 @Service
-public class QuestionService implements IQuestionService, ApplicationEventPublisherAware {
+public class ContentService implements IContentService, ApplicationEventPublisherAware {
+	@Autowired
+	private IUserService userService;
 
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private SessionRepository sessionRepository;
 
 	@Autowired
-	private IUserService userService;
+	private CommentRepository commentRepository;
+
+	@Autowired
+	private ContentRepository contentRepository;
+
+	@Autowired
+	private AnswerRepository answerRepository;
 
 	@Autowired
 	private ImageUtils imageUtils;
@@ -67,57 +78,54 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	private ApplicationEventPublisher publisher;
 
-	private static final Logger logger = LoggerFactory.getLogger(QuestionService.class);
+	private static final Logger logger = LoggerFactory.getLogger(ContentService.class);
 
 	private HashMap<String, Timer> timerList = new HashMap<>();
 
-	public void setDatabaseDao(final IDatabaseDao databaseDao) {
-		this.databaseDao = databaseDao;
-	}
-
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getSkillQuestions(final String sessionkey) {
+	public List<Content> getSkillQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getSkillQuestionsForTeachers(session);
+			return contentRepository.getSkillQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getSkillQuestionsForUsers(session);
+			return contentRepository.getSkillQuestionsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getSkillQuestionCount(final String sessionkey) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
-		return databaseDao.getSkillQuestionCount(session);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		return contentRepository.getSkillQuestionCount(session);
 	}
 
+	/* FIXME: #content.getSessionKeyword() cannot be checked since keyword is no longer set for content. */
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#question.getSessionKeyword(), 'session', 'owner')")
-	public Question saveQuestion(final Question question) {
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
-		question.setSessionId(session.get_id());
-		question.setTimestamp(System.currentTimeMillis() / 1000L);
+	@PreAuthorize("isAuthenticated() and hasPermission(#content.getSessionKeyword(), 'session', 'owner')")
+	public Content saveQuestion(final Content content) {
+		final Session session = sessionRepository.getSessionFromKeyword(content.getSessionKeyword());
+		content.setSessionId(session.getId());
+		content.setTimestamp(System.currentTimeMillis() / 1000L);
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
-		} else if (question.getPiRound() < 1 || question.getPiRound() > 2) {
-			question.setPiRound(1);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(1);
 		}
 
 		// convert imageurl to base64 if neccessary
-		if ("grid".equals(question.getQuestionType()) && !question.getImage().startsWith("http")) {
+		if ("grid".equals(content.getQuestionType()) && !content.getImage().startsWith("http")) {
 			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
-			final int fileSize = (int) ((question.getImage().length() - 814) / 1.37);
+			final int fileSize = (int) ((content.getImage().length() - 814) / 1.37);
 			if (fileSize > uploadFileSizeByte) {
 				logger.error("Could not save file. File is too large with {} Byte.", fileSize);
 				throw new BadRequestException();
 			}
 		}
 
-		final Question result = databaseDao.saveQuestion(session, question);
+		final Content result = contentRepository.saveQuestion(session, content);
 
 		final NewQuestionEvent event = new NewQuestionEvent(this, session, result);
 		this.publisher.publishEvent(event);
@@ -127,12 +135,12 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public boolean saveQuestion(final InterposedQuestion question) {
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionId());
-		final InterposedQuestion result = databaseDao.saveQuestion(session, question, userService.getCurrentUser());
+	public boolean saveQuestion(final Comment comment) {
+		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
+		final Comment result = commentRepository.saveQuestion(session, comment, userService.getCurrentUser());
 
 		if (null != result) {
-			final NewInterposedQuestionEvent event = new NewInterposedQuestionEvent(this, session, result);
+			final NewCommentEvent event = new NewCommentEvent(this, session, result);
 			this.publisher.publishEvent(event);
 			return true;
 		}
@@ -141,8 +149,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question getQuestion(final String id) {
-		final Question result = databaseDao.getQuestion(id);
+	public Content getQuestion(final String id) {
+		final Content result = contentRepository.getQuestion(id);
 		if (result == null) {
 			return null;
 		}
@@ -155,20 +163,20 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void deleteQuestion(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
 
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (session == null) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.deleteQuestionWithAnswers(question);
+		contentRepository.deleteQuestionWithAnswers(content);
 
-		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, question);
+		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, content);
 		this.publisher.publishEvent(event);
 	}
 
@@ -176,17 +184,17 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionKeyword, 'session', 'owner')")
 	public void deleteAllQuestions(final String sessionKeyword) {
 		final Session session = getSessionWithAuthCheck(sessionKeyword);
-		databaseDao.deleteAllQuestionsWithAnswers(session);
+		contentRepository.deleteAllQuestionsWithAnswers(session);
 
 		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, session);
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void startNewPiRound(final String questionId, User user) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		if (null == user) {
 			user = userService.getCurrentUser();
@@ -194,57 +202,57 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 		cancelDelayedPiRoundChange(questionId);
 
-		question.setPiRoundEndTime(0);
-		question.setVotingDisabled(true);
-		question.updateRoundManagementState();
-		update(question, user);
+		content.setPiRoundEndTime(0);
+		content.setVotingDisabled(true);
+		content.updateRoundManagementState();
+		update(content, user);
 
-		this.publisher.publishEvent(new PiRoundEndEvent(this, session, question));
+		this.publisher.publishEvent(new PiRoundEndEvent(this, session, content));
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void startNewPiRoundDelayed(final String questionId, final int time) {
-		final IQuestionService questionService = this;
+		final IContentService contentService = this;
 		final User user = userService.getCurrentUser();
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		final Date date = new Date();
 		final Timer timer = new Timer();
 		final Date endDate = new Date(date.getTime() + (time * 1000));
-		question.updateRoundStartVariables(date, endDate);
-		update(question);
+		content.updateRoundStartVariables(date, endDate);
+		update(content);
 
-		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, question));
+		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, content));
 		timerList.put(questionId, timer);
 
 		timer.schedule(new TimerTask() {
 			@Override
 			public void run() {
-				questionService.startNewPiRound(questionId, user);
+				contentService.startNewPiRound(questionId, user);
 			}
 		}, endDate);
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void cancelPiRoundChange(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		cancelDelayedPiRoundChange(questionId);
-		question.resetRoundManagementState();
+		content.resetRoundManagementState();
 
-		if (0 == question.getPiRound() || 1 == question.getPiRound()) {
-			question.setPiRoundFinished(false);
+		if (0 == content.getPiRound() || 1 == content.getPiRound()) {
+			content.setPiRoundFinished(false);
 		} else {
-			question.setPiRound(1);
-			question.setPiRoundFinished(true);
+			content.setPiRound(1);
+			content.setPiRoundFinished(true);
 		}
 
-		update(question);
-		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, question));
+		update(content);
+		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, content));
 	}
 
 	@Override
@@ -259,60 +267,60 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void resetPiRoundState(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		cancelDelayedPiRoundChange(questionId);
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
 		} else {
-			question.setPiRound(1);
+			content.setPiRound(1);
 		}
 
-		question.resetRoundManagementState();
-		databaseDao.deleteAnswers(question);
-		update(question);
-		this.publisher.publishEvent(new PiRoundResetEvent(this, session, question));
+		content.resetRoundManagementState();
+		answerRepository.deleteAnswers(content);
+		update(content);
+		this.publisher.publishEvent(new PiRoundResetEvent(this, session, content));
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void setVotingAdmission(final String questionId, final boolean disableVoting) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
-		question.setVotingDisabled(disableVoting);
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		content.setVotingDisabled(disableVoting);
 
-		if (!disableVoting && !question.isActive()) {
-			question.setActive(true);
-			update(question);
+		if (!disableVoting && !content.isActive()) {
+			content.setActive(true);
+			update(content);
 		} else {
-			databaseDao.updateQuestion(question);
+			contentRepository.updateQuestion(content);
 		}
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVoteEvent(this, session, question);
+			event = new LockVoteEvent(this, session, content);
 		} else {
-			event = new UnlockVoteEvent(this, session, question);
+			event = new UnlockVoteEvent(this, session, content);
 		}
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Question> questions) {
+	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Content> contents) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionkey);
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.setVotingAdmissions(session, disableVoting, questions);
+		contentRepository.setVotingAdmissions(session, disableVoting, contents);
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVotesEvent(this, session, questions);
+			event = new LockVotesEvent(this, session, contents);
 		} else {
-			event = new UnlockVotesEvent(this, session, questions);
+			event = new UnlockVotesEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
@@ -325,19 +333,19 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		final List<Question> questions = databaseDao.setVotingAdmissionForAllQuestions(session, disableVoting);
+		final List<Content> contents = contentRepository.setVotingAdmissionForAllQuestions(session, disableVoting);
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVotesEvent(this, session, questions);
+			event = new LockVotesEvent(this, session, contents);
 		} else {
-			event = new UnlockVotesEvent(this, session, questions);
+			event = new UnlockVotesEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
 
 	private Session getSessionWithAuthCheck(final String sessionKeyword) {
 		final User user = userService.getCurrentUser();
-		final Session session = databaseDao.getSessionFromKeyword(sessionKeyword);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
 		if (user == null || session == null || !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
@@ -345,41 +353,41 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'interposedquestion', 'owner')")
-	public void deleteInterposedQuestion(final String questionId) {
-		final InterposedQuestion question = databaseDao.getInterposedQuestion(questionId);
-		if (question == null) {
+	@PreAuthorize("isAuthenticated() and hasPermission(#commentId, 'comment', 'owner')")
+	public void deleteInterposedQuestion(final String commentId) {
+		final Comment comment = commentRepository.getInterposedQuestion(commentId);
+		if (comment == null) {
 			throw new NotFoundException();
 		}
-		databaseDao.deleteInterposedQuestion(question);
+		commentRepository.deleteInterposedQuestion(comment);
 
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionId());
-		final DeleteInterposedQuestionEvent event = new DeleteInterposedQuestionEvent(this, session, question);
+		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
+		final DeleteCommentEvent event = new DeleteCommentEvent(this, session, comment);
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAllInterposedQuestions(final String sessionKeyword) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionKeyword);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
 		if (session == null) {
 			throw new UnauthorizedException();
 		}
 		final User user = getCurrentUser();
 		if (session.isCreator(user)) {
-			databaseDao.deleteAllInterposedQuestions(session);
+			commentRepository.deleteAllInterposedQuestions(session);
 		} else {
-			databaseDao.deleteAllInterposedQuestions(session, user);
+			commentRepository.deleteAllInterposedQuestions(session, user);
 		}
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void deleteAnswers(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		question.resetQuestionState();
-		databaseDao.updateQuestion(question);
-		databaseDao.deleteAnswers(question);
+		final Content content = contentRepository.getQuestion(questionId);
+		content.resetQuestionState();
+		contentRepository.updateQuestion(content);
+		answerRepository.deleteAnswers(content);
 	}
 
 	@Override
@@ -387,7 +395,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	public List<String> getUnAnsweredQuestionIds(final String sessionKey) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionKey);
-		return databaseDao.getUnAnsweredQuestionIds(session, user);
+		return contentRepository.getUnAnsweredQuestionIds(session, user);
 	}
 
 	private User getCurrentUser() {
@@ -401,121 +409,121 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public Answer getMyAnswer(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		return databaseDao.getMyAnswer(userService.getCurrentUser(), questionId, question.getPiRound());
+		return answerRepository.getMyAnswer(userService.getCurrentUser(), questionId, content.getPiRound());
 	}
 
 	@Override
 	public void readFreetextAnswer(final String answerId, final User user) {
-		final Answer answer = databaseDao.getObjectFromId(answerId, Answer.class);
+		final Answer answer = answerRepository.get(answerId);
 		if (answer == null) {
 			throw new NotFoundException();
 		}
 		if (answer.isRead()) {
 			return;
 		}
-		final Session session = databaseDao.getSessionFromId(answer.getSessionId());
+		final Session session = sessionRepository.getSessionFromId(answer.getSessionId());
 		if (session.isCreator(user)) {
 			answer.setRead(true);
-			databaseDao.updateAnswer(answer);
+			answerRepository.updateAnswer(answer);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAnswers(final String questionId, final int piRound, final int offset, final int limit) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		return "freetext".equals(question.getQuestionType())
+		return "freetext".equals(content.getQuestionType())
 				? getFreetextAnswers(questionId, offset, limit)
-						: databaseDao.getAnswers(question, piRound);
+						: answerRepository.getAnswers(content, piRound);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAnswers(final String questionId, final int offset, final int limit) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			return getFreetextAnswers(questionId, offset, limit);
 		} else {
-			return databaseDao.getAnswers(question);
+			return answerRepository.getAnswers(content);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAllAnswers(final String questionId, final int offset, final int limit) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			return getFreetextAnswers(questionId, offset, limit);
 		} else {
-			return databaseDao.getAllAnswers(question);
+			return answerRepository.getAllAnswers(content);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAnswerCount(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		if ("freetext".equals(question.getQuestionType())) {
-			return databaseDao.getTotalAnswerCountByQuestion(question);
+		if ("freetext".equals(content.getQuestionType())) {
+			return answerRepository.getTotalAnswerCountByQuestion(content);
 		} else {
-			return databaseDao.getAnswerCount(question, question.getPiRound());
+			return answerRepository.getAnswerCount(content, content.getPiRound());
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAnswerCount(final String questionId, final int piRound) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		return databaseDao.getAnswerCount(question, piRound);
+		return answerRepository.getAnswerCount(content, piRound);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAbstentionAnswerCount(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		return databaseDao.getAbstentionAnswerCount(questionId);
+		return answerRepository.getAbstentionAnswerCount(questionId);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getTotalAnswerCountByQuestion(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		return databaseDao.getTotalAnswerCountByQuestion(question);
+		return answerRepository.getTotalAnswerCountByQuestion(content);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getFreetextAnswers(final String questionId, final int offset, final int limit) {
-		final List<Answer> answers = databaseDao.getFreetextAnswers(questionId, offset, limit);
+		final List<Answer> answers = answerRepository.getFreetextAnswers(questionId, offset, limit);
 		if (answers == null) {
 			throw new NotFoundException();
 		}
@@ -531,29 +539,29 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getMyAnswers(final String sessionKey) {
 		final Session session = getSession(sessionKey);
-		// Load questions first because we are only interested in answers of the latest piRound.
-		final List<Question> questions = databaseDao.getSkillQuestionsForUsers(session);
-		final Map<String, Question> questionIdToQuestion = new HashMap<>();
-		for (final Question question : questions) {
-			questionIdToQuestion.put(question.get_id(), question);
+		// Load contents first because we are only interested in answers of the latest piRound.
+		final List<Content> contents = contentRepository.getSkillQuestionsForUsers(session);
+		final Map<String, Content> questionIdToQuestion = new HashMap<>();
+		for (final Content content : contents) {
+			questionIdToQuestion.put(content.getId(), content);
 		}
 
 		/* filter answers by active piRound per question */
-		final List<Answer> answers = databaseDao.getMyAnswers(userService.getCurrentUser(), session);
+		final List<Answer> answers = answerRepository.getMyAnswers(userService.getCurrentUser(), session);
 		final List<Answer> filteredAnswers = new ArrayList<>();
 		for (final Answer answer : answers) {
-			final Question question = questionIdToQuestion.get(answer.getQuestionId());
-			if (question == null) {
-				// Question is not present. Most likely it has been locked by the
+			final Content content = questionIdToQuestion.get(answer.getQuestionId());
+			if (content == null) {
+				// Content is not present. Most likely it has been locked by the
 				// Session's creator. Locked Questions do not appear in this list.
 				continue;
 			}
-			if (0 == answer.getPiRound() && !"freetext".equals(question.getQuestionType())) {
+			if (0 == answer.getPiRound() && !"freetext".equals(content.getQuestionType())) {
 				answer.setPiRound(1);
 			}
 
-			// discard all answers that aren't in the same piRound as the question
-			if (answer.getPiRound() == question.getPiRound()) {
+			// discard all answers that aren't in the same piRound as the content
+			if (answer.getPiRound() == content.getPiRound()) {
 				filteredAnswers.add(answer);
 			}
 		}
@@ -564,51 +572,51 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getTotalAnswerCount(final String sessionKey) {
-		return databaseDao.getTotalAnswerCount(sessionKey);
+		return answerRepository.getTotalAnswerCount(sessionKey);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getInterposedCount(final String sessionKey) {
-		return databaseDao.getInterposedCount(sessionKey);
+		return commentRepository.getInterposedCount(sessionKey);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public InterposedReadingCount getInterposedReadingCount(final String sessionKey, String username) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionKey);
+	public CommentReadingCount getInterposedReadingCount(final String sessionKey, String username) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKey);
 		if (session == null) {
 			throw new NotFoundException();
 		}
 		if (username == null) {
-			return databaseDao.getInterposedReadingCount(session);
+			return commentRepository.getInterposedReadingCount(session);
 		} else {
 			User currentUser = userService.getCurrentUser();
 			if (!currentUser.getUsername().equals(username)) {
 				throw new ForbiddenException();
 			}
 
-			return databaseDao.getInterposedReadingCount(session, currentUser);
+			return commentRepository.getInterposedReadingCount(session, currentUser);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<InterposedQuestion> getInterposedQuestions(final String sessionKey, final int offset, final int limit) {
+	public List<Comment> getInterposedQuestions(final String sessionKey, final int offset, final int limit) {
 		final Session session = this.getSession(sessionKey);
 		final User user = getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getInterposedQuestions(session, offset, limit);
+			return commentRepository.getInterposedQuestions(session, offset, limit);
 		} else {
-			return databaseDao.getInterposedQuestions(session, user, offset, limit);
+			return commentRepository.getInterposedQuestions(session, user, offset, limit);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public InterposedQuestion readInterposedQuestion(final String questionId) {
+	public Comment readInterposedQuestion(final String commentId) {
 		final User user = userService.getCurrentUser();
-		return this.readInterposedQuestionInternal(questionId, user);
+		return this.readInterposedQuestionInternal(commentId, user);
 	}
 
 	/*
@@ -616,53 +624,53 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	 * TODO: Find a better way of doing this...
 	 */
 	@Override
-	public InterposedQuestion readInterposedQuestionInternal(final String questionId, User user) {
-		final InterposedQuestion question = databaseDao.getInterposedQuestion(questionId);
-		if (question == null) {
+	public Comment readInterposedQuestionInternal(final String commentId, User user) {
+		final Comment comment = commentRepository.getInterposedQuestion(commentId);
+		if (comment == null) {
 			throw new NotFoundException();
 		}
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionId());
-		if (!question.isCreator(user) && !session.isCreator(user)) {
+		final Session session = sessionRepository.getSessionFromId(comment.getSessionId());
+		if (!comment.isCreator(user) && !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
 		if (session.isCreator(user)) {
-			databaseDao.markInterposedQuestionAsRead(question);
+			commentRepository.markInterposedQuestionAsRead(comment);
 		}
-		return question;
+		return comment;
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question update(final Question question) {
+	public Content update(final Content content) {
 		final User user = userService.getCurrentUser();
-		return update(question, user);
+		return update(content, user);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question update(final Question question, User user) {
-		final Question oldQuestion = databaseDao.getQuestion(question.get_id());
-		if (null == oldQuestion) {
+	public Content update(final Content content, User user) {
+		final Content oldContent = contentRepository.getQuestion(content.getId());
+		if (null == oldContent) {
 			throw new NotFoundException();
 		}
 
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (user == null || session == null || !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
-		} else if (question.getPiRound() < 1 || question.getPiRound() > 2) {
-			question.setPiRound(oldQuestion.getPiRound() > 0 ? oldQuestion.getPiRound() : 1);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(oldContent.getPiRound() > 0 ? oldContent.getPiRound() : 1);
 		}
 
-		final Question result = databaseDao.updateQuestion(question);
+		final Content result = contentRepository.updateQuestion(content);
 
-		if (!oldQuestion.isActive() && question.isActive()) {
+		if (!oldContent.isActive() && content.isActive()) {
 			final UnlockQuestionEvent event = new UnlockQuestionEvent(this, session, result);
 			this.publisher.publishEvent(event);
-		} else if (oldQuestion.isActive() && !question.isActive()) {
+		} else if (oldContent.isActive() && !content.isActive()) {
 			final LockQuestionEvent event = new LockQuestionEvent(this, session, result);
 			this.publisher.publishEvent(event);
 		}
@@ -673,26 +681,30 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated()")
 	public Answer saveAnswer(final String questionId, final de.thm.arsnova.entities.transport.Answer answer) {
 		final User user = getCurrentUser();
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
-		Answer theAnswer = answer.generateAnswerEntity(user, question);
-		if ("freetext".equals(question.getQuestionType())) {
+		Answer theAnswer = answer.generateAnswerEntity(user, content);
+		theAnswer.setUser(user.getUsername());
+		theAnswer.setQuestionId(content.getId());
+		theAnswer.setSessionId(session.getId());
+		if ("freetext".equals(content.getQuestionType())) {
 			imageUtils.generateThumbnailImage(theAnswer);
-			if (question.isFixedAnswer() && question.getText() != null) {
+			if (content.isFixedAnswer() && content.getText() != null) {
 				theAnswer.setAnswerTextRaw(theAnswer.getAnswerText());
 
-				if (question.isStrictMode()) {
-					question.checkTextStrictOptions(theAnswer);
+				if (content.isStrictMode()) {
+					content.checkTextStrictOptions(theAnswer);
 				}
-				theAnswer.setQuestionValue(question.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
-				theAnswer.setSuccessfulFreeTextAnswer(question.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
+				theAnswer.setQuestionValue(content.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
+				theAnswer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
 			}
 		}
 
-		return databaseDao.saveAnswer(theAnswer, user, question, getSession(question.getSessionKeyword()));
+		return answerRepository.saveAnswer(theAnswer, user, content, session);
 	}
 
 	@Override
@@ -704,14 +716,17 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new UnauthorizedException();
 		}
 
-		final Question question = getQuestion(answer.getQuestionId());
-		if ("freetext".equals(question.getQuestionType())) {
+		final Content content = getQuestion(answer.getQuestionId());
+		if ("freetext".equals(content.getQuestionType())) {
 			imageUtils.generateThumbnailImage(realAnswer);
-			question.checkTextStrictOptions(realAnswer);
+			content.checkTextStrictOptions(realAnswer);
 		}
-		final Answer result = databaseDao.updateAnswer(realAnswer);
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
-		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, question));
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		answer.setUser(user.getUsername());
+		answer.setQuestionId(content.getId());
+		answer.setSessionId(session.getId());
+		final Answer result = answerRepository.updateAnswer(realAnswer);
+		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, content));
 
 		return result;
 	}
@@ -719,70 +734,70 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAnswer(final String questionId, final String answerId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
 		final User user = userService.getCurrentUser();
-		final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (user == null || session == null || !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.deleteAnswer(answerId);
+		answerRepository.deleteAnswer(answerId);
 
-		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, question));
+		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, content));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getLectureQuestions(final String sessionkey) {
+	public List<Content> getLectureQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getLectureQuestionsForTeachers(session);
+			return contentRepository.getLectureQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getLectureQuestionsForUsers(session);
+			return contentRepository.getLectureQuestionsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getFlashcards(final String sessionkey) {
+	public List<Content> getFlashcards(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getFlashcardsForTeachers(session);
+			return contentRepository.getFlashcardsForTeachers(session);
 		} else {
-			return databaseDao.getFlashcardsForUsers(session);
+			return contentRepository.getFlashcardsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getPreparationQuestions(final String sessionkey) {
+	public List<Content> getPreparationQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getPreparationQuestionsForTeachers(session);
+			return contentRepository.getPreparationQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getPreparationQuestionsForUsers(session);
+			return contentRepository.getPreparationQuestionsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> replaceImageData(final List<Question> questions) {
-		for (Question q : questions) {
+	public List<Content> replaceImageData(final List<Content> contents) {
+		for (Content q : contents) {
 			if (q.getImage() != null && q.getImage().startsWith("data:image/")) {
 				q.setImage("true");
 			}
 		}
 
-		return questions;
+		return contents;
 	}
 
 	private Session getSession(final String sessionkey) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		if (session == null) {
 			throw new NotFoundException();
 		}
@@ -792,19 +807,19 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getLectureQuestionCount(final String sessionkey) {
-		return databaseDao.getLectureQuestionCount(getSession(sessionkey));
+		return contentRepository.getLectureQuestionCount(getSession(sessionkey));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getFlashcardCount(final String sessionkey) {
-		return databaseDao.getFlashcardCount(getSession(sessionkey));
+		return contentRepository.getFlashcardCount(getSession(sessionkey));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getPreparationQuestionCount(final String sessionkey) {
-		return databaseDao.getPreparationQuestionCount(getSession(sessionkey));
+		return contentRepository.getPreparationQuestionCount(getSession(sessionkey));
 	}
 
 	@Override
@@ -819,21 +834,21 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	 */
 	@Override
 	public int countLectureQuestionAnswersInternal(final String sessionkey) {
-		return databaseDao.countLectureQuestionAnswers(getSession(sessionkey));
+		return answerRepository.countLectureQuestionAnswers(getSession(sessionkey));
 	}
 
 	@Override
 	public Map<String, Object> getAnswerAndAbstentionCountInternal(final String questionId) {
-		final Question question = getQuestion(questionId);
+		final Content content = getQuestion(questionId);
 		HashMap<String, Object> map = new HashMap<>();
 
-		if (question == null) {
+		if (content == null) {
 			return null;
 		}
 
 		map.put("_id", questionId);
-		map.put("answers", databaseDao.getAnswerCount(question, question.getPiRound()));
-		map.put("abstentions", databaseDao.getAbstentionAnswerCount(questionId));
+		map.put("answers", answerRepository.getAnswerCount(content, content.getPiRound()));
+		map.put("abstentions", answerRepository.getAbstentionAnswerCount(questionId));
 
 		return map;
 	}
@@ -850,7 +865,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	 */
 	@Override
 	public int countPreparationQuestionAnswersInternal(final String sessionkey) {
-		return databaseDao.countPreparationQuestionAnswers(getSession(sessionkey));
+		return answerRepository.countPreparationQuestionAnswers(getSession(sessionkey));
 	}
 
 	/*
@@ -859,28 +874,28 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	 */
 	@Override
 	public int countFlashcardsForUserInternal(final String sessionkey) {
-		return databaseDao.getFlashcardsForUsers(getSession(sessionkey)).size();
+		return contentRepository.getFlashcardsForUsers(getSession(sessionkey)).size();
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteLectureQuestions(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllLectureQuestionsWithAnswers(session);
+		contentRepository.deleteAllLectureQuestionsWithAnswers(session);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteFlashcards(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllFlashcardsWithAnswers(session);
+		contentRepository.deleteAllFlashcardsWithAnswers(session);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deletePreparationQuestions(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllPreparationQuestionsWithAnswers(session);
+		contentRepository.deleteAllPreparationQuestionsWithAnswers(session);
 	}
 
 	@Override
@@ -893,7 +908,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey, final User user) {
 		final Session session = getSession(sessionkey);
-		return databaseDao.getUnAnsweredLectureQuestionIds(session, user);
+		return contentRepository.getUnAnsweredLectureQuestionIds(session, user);
 	}
 
 	@Override
@@ -906,7 +921,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey, final User user) {
 		final Session session = getSession(sessionkey);
-		return databaseDao.getUnAnsweredPreparationQuestionIds(session, user);
+		return contentRepository.getUnAnsweredPreparationQuestionIds(session, user);
 	}
 
 	@Override
@@ -917,30 +932,30 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		final List<Question> questions = databaseDao.publishAllQuestions(session, publish);
+		final List<Content> contents = contentRepository.publishAllQuestions(session, publish);
 		NovaEvent event;
 		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, questions);
+			event = new UnlockQuestionsEvent(this, session, contents);
 		} else {
-			event = new LockQuestionsEvent(this, session, questions);
+			event = new LockQuestionsEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void publishQuestions(final String sessionkey, final boolean publish, List<Question> questions) {
+	public void publishQuestions(final String sessionkey, final boolean publish, List<Content> contents) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionkey);
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.publishQuestions(session, publish, questions);
+		contentRepository.publishQuestions(session, publish, contents);
 		NovaEvent event;
 		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, questions);
+			event = new UnlockQuestionsEvent(this, session, contents);
 		} else {
-			event = new LockQuestionsEvent(this, session, questions);
+			event = new LockQuestionsEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
@@ -953,7 +968,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.deleteAllQuestionsAnswers(session);
+		answerRepository.deleteAllQuestionsAnswers(session);
 
 		this.publisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, session));
 	}
@@ -962,7 +977,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public void deleteAllPreparationAnswers(String sessionkey) {
 		final Session session = getSession(sessionkey);
-		databaseDao.deleteAllPreparationAnswers(session);
+		answerRepository.deleteAllPreparationAnswers(session);
 
 		this.publisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, session));
 	}
@@ -971,7 +986,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public void deleteAllLectureAnswers(String sessionkey) {
 		final Session session = getSession(sessionkey);
-		databaseDao.deleteAllLectureAnswers(session);
+		answerRepository.deleteAllLectureAnswers(session);
 
 		this.publisher.publishEvent(new DeleteAllLectureAnswersEvent(this, session));
 	}
@@ -987,7 +1002,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		Answer answer = null;
 
 		for (Answer a : answers) {
-			if (answerId.equals(a.get_id())) {
+			if (answerId.equals(a.getId())) {
 				answer = a;
 				break;
 			}
@@ -1002,8 +1017,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	public String getQuestionImage(String questionId) {
-		Question question = databaseDao.getQuestion(questionId);
-		String imageData = question.getImage();
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getImage();
 
 		if (imageData == null) {
 			imageData = "";
@@ -1014,8 +1029,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	public String getQuestionFcImage(String questionId) {
-		Question question = databaseDao.getQuestion(questionId);
-		String imageData = question.getFcImage();
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getFcImage();
 
 		if (imageData == null) {
 			imageData = "";
diff --git a/src/main/java/de/thm/arsnova/services/FeedbackService.java b/src/main/java/de/thm/arsnova/services/FeedbackService.java
index 1c49f3d5f77e956eead8831775f872ff69d3452b..0a4a69599944a7f9d26df23104dcbda994fbf955 100644
--- a/src/main/java/de/thm/arsnova/services/FeedbackService.java
+++ b/src/main/java/de/thm/arsnova/services/FeedbackService.java
@@ -18,7 +18,6 @@
 package de.thm.arsnova.services;
 
 import de.thm.arsnova.FeedbackStorage;
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
@@ -26,6 +25,7 @@ import de.thm.arsnova.events.DeleteFeedbackForSessionsEvent;
 import de.thm.arsnova.events.NewFeedbackEvent;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.SessionRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.ApplicationEventPublisher;
@@ -56,16 +56,12 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 	private int cleanupFeedbackDelay;
 
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private SessionRepository sessionRepository;
 
 	private FeedbackStorage feedbackStorage;
 
 	private ApplicationEventPublisher publisher;
 
-	public void setDatabaseDao(final IDatabaseDao newDatabaseDao) {
-		databaseDao = newDatabaseDao;
-	}
-
 	@PostConstruct
 	public void init() {
 		feedbackStorage = new FeedbackStorage();
@@ -110,7 +106,7 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 
 	@Override
 	public void cleanFeedbackVotesInSession(final String keyword, final int cleanupFeedbackDelayInMins) {
-		final Session session = databaseDao.getSessionFromKeyword(keyword);
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
 		List<User> affectedUsers = feedbackStorage.cleanFeedbackVotesInSession(session, cleanupFeedbackDelayInMins);
 		Set<Session> sessionSet = new HashSet<>();
 		sessionSet.add(session);
@@ -125,7 +121,7 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 
 	@Override
 	public Feedback getFeedback(final String keyword) {
-		final Session session = databaseDao.getSessionFromKeyword(keyword);
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
 		if (session == null) {
 			throw new NotFoundException();
 		}
@@ -142,7 +138,7 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 
 	@Override
 	public double getAverageFeedback(final String sessionkey) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		if (session == null) {
 			throw new NotFoundException();
 		}
@@ -166,7 +162,7 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 
 	@Override
 	public boolean saveFeedback(final String keyword, final int value, final User user) {
-		final Session session = databaseDao.getSessionFromKeyword(keyword);
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
 		if (session == null) {
 			throw new NotFoundException();
 		}
@@ -178,7 +174,7 @@ public class FeedbackService implements IFeedbackService, ApplicationEventPublis
 
 	@Override
 	public Integer getMyFeedback(final String keyword, final User user) {
-		final Session session = databaseDao.getSessionFromKeyword(keyword);
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
 		if (session == null) {
 			throw new NotFoundException();
 		}
diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IContentService.java
similarity index 78%
rename from src/main/java/de/thm/arsnova/services/IQuestionService.java
rename to src/main/java/de/thm/arsnova/services/IContentService.java
index f03a88e98776f14d0e3f60db8c175e33ab179801..9b4e4f97bf40433a750034b28a3ee53d03ee19ef 100644
--- a/src/main/java/de/thm/arsnova/services/IQuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/IContentService.java
@@ -18,9 +18,9 @@
 package de.thm.arsnova.services;
 
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.InterposedQuestion;
-import de.thm.arsnova.entities.InterposedReadingCount;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.User;
 
 import java.util.List;
@@ -29,14 +29,14 @@ import java.util.Map;
 /**
  * The functionality the question service should provide.
  */
-public interface IQuestionService {
-	Question saveQuestion(Question question);
+public interface IContentService {
+	Content saveQuestion(Content content);
 
-	boolean saveQuestion(InterposedQuestion question);
+	boolean saveQuestion(Comment comment);
 
-	Question getQuestion(String id);
+	Content getQuestion(String id);
 
-	List<Question> getSkillQuestions(String sessionkey);
+	List<Content> getSkillQuestions(String sessionkey);
 
 	int getSkillQuestionCount(String sessionkey);
 
@@ -80,17 +80,17 @@ public interface IQuestionService {
 
 	int getInterposedCount(String sessionKey);
 
-	InterposedReadingCount getInterposedReadingCount(String sessionKey, String username);
+	CommentReadingCount getInterposedReadingCount(String sessionKey, String username);
 
-	List<InterposedQuestion> getInterposedQuestions(String sessionKey, int offset, int limit);
+	List<Comment> getInterposedQuestions(String sessionKey, int offset, int limit);
 
-	InterposedQuestion readInterposedQuestion(String questionId);
+	Comment readInterposedQuestion(String commentId);
 
-	InterposedQuestion readInterposedQuestionInternal(String questionId, User user);
+	Comment readInterposedQuestionInternal(String commentId, User user);
 
-	Question update(Question question);
+	Content update(Content content);
 
-	Question update(Question question, User user);
+	Content update(Content content, User user);
 
 	void deleteAnswers(String questionId);
 
@@ -100,13 +100,13 @@ public interface IQuestionService {
 
 	void deleteAnswer(String questionId, String answerId);
 
-	void deleteInterposedQuestion(String questionId);
+	void deleteInterposedQuestion(String commentId);
 
-	List<Question> getLectureQuestions(String sessionkey);
+	List<Content> getLectureQuestions(String sessionkey);
 
-	List<Question> getFlashcards(String sessionkey);
+	List<Content> getFlashcards(String sessionkey);
 
-	List<Question> getPreparationQuestions(String sessionkey);
+	List<Content> getPreparationQuestions(String sessionkey);
 
 	int getLectureQuestionCount(String sessionkey);
 
@@ -144,7 +144,7 @@ public interface IQuestionService {
 
 	void publishAll(String sessionkey, boolean publish);
 
-	void publishQuestions(String sessionkey, boolean publish, List<Question> questions);
+	void publishQuestions(String sessionkey, boolean publish, List<Content> contents);
 
 	void deleteAllQuestionsAnswers(String sessionkey);
 
@@ -158,7 +158,7 @@ public interface IQuestionService {
 
 	void setVotingAdmission(String questionId, boolean disableVoting);
 
-	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Question> questions);
+	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Content> contents);
 
 	void setVotingAdmissionForAllQuestions(String sessionkey, boolean disableVoting);
 
@@ -166,6 +166,6 @@ public interface IQuestionService {
 
 	String getQuestionFcImage(String questionId);
 
-	List<Question> replaceImageData(List<Question> questions);
+	List<Content> replaceImageData(List<Content> contents);
 
 }
diff --git a/src/main/java/de/thm/arsnova/services/MotdService.java b/src/main/java/de/thm/arsnova/services/MotdService.java
index ecda866383aadd4023b44847a43ed788ef50ff18..674525ec2afe51c9fd917f0d1a992710dc2eb9cf 100644
--- a/src/main/java/de/thm/arsnova/services/MotdService.java
+++ b/src/main/java/de/thm/arsnova/services/MotdService.java
@@ -17,12 +17,13 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Motd;
 import de.thm.arsnova.entities.MotdList;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.persistance.MotdListRepository;
+import de.thm.arsnova.persistance.MotdRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
@@ -37,48 +38,46 @@ import java.util.StringTokenizer;
  */
 @Service
 public class MotdService implements IMotdService {
-
-	@Autowired
-	private IDatabaseDao databaseDao;
-
 	@Autowired
 	private IUserService userService;
 
 	@Autowired
 	private ISessionService sessionService;
 
-	public void setDatabaseDao(final IDatabaseDao databaseDao) {
-		this.databaseDao = databaseDao;
-	}
+	@Autowired
+	private MotdRepository motdRepository;
+
+	@Autowired
+	private MotdListRepository motdListRepository;
 
   @Override
   @PreAuthorize("isAuthenticated()")
   public Motd getMotd(final String key) {
-    return databaseDao.getMotdByKey(key);
+    return motdRepository.getMotdByKey(key);
   }
 
   @Override
   @PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
   public List<Motd> getAdminMotds() {
-    return databaseDao.getAdminMotds();
+    return motdRepository.getAdminMotds();
   }
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public List<Motd> getAllSessionMotds(final String sessionkey) {
-		return databaseDao.getMotdsForSession(sessionkey);
+		return motdRepository.getMotdsForSession(sessionkey);
 	}
 
 	@Override
 	public List<Motd> getCurrentMotds(final Date clientdate, final String audience, final String sessionkey) {
 		final List<Motd> motds;
 		switch (audience) {
-			case "all": motds = databaseDao.getMotdsForAll(); break;
-			case "loggedIn": motds = databaseDao.getMotdsForLoggedIn(); break;
-			case "students": motds = databaseDao.getMotdsForStudents(); break;
-			case "tutors": motds = databaseDao.getMotdsForTutors(); break;
-			case "session": motds = databaseDao.getMotdsForSession(sessionkey); break;
-			default: motds = databaseDao.getMotdsForAll(); break;
+			case "all": motds = motdRepository.getMotdsForAll(); break;
+			case "loggedIn": motds = motdRepository.getMotdsForLoggedIn(); break;
+			case "students": motds = motdRepository.getMotdsForStudents(); break;
+			case "tutors": motds = motdRepository.getMotdsForTutors(); break;
+			case "session": motds = motdRepository.getMotdsForSession(sessionkey); break;
+			default: motds = motdRepository.getMotdsForAll(); break;
 		}
 		return filterMotdsByDate(motds, clientdate);
 	}
@@ -124,7 +123,7 @@ public class MotdService implements IMotdService {
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public Motd saveSessionMotd(final String sessionkey, final Motd motd) {
 		Session session = sessionService.getSession(sessionkey);
-		motd.setSessionId(session.get_id());
+		motd.setSessionId(session.getId());
 
 
 		return createOrUpdateMotd(motd);
@@ -144,26 +143,26 @@ public class MotdService implements IMotdService {
 
 	private Motd createOrUpdateMotd(final Motd motd) {
 		if (motd.getMotdkey() != null) {
-			Motd oldMotd = databaseDao.getMotdByKey(motd.getMotdkey());
-			if (!(motd.get_id().equals(oldMotd.get_id()) && motd.getSessionkey().equals(oldMotd.getSessionkey())
+			Motd oldMotd = motdRepository.getMotdByKey(motd.getMotdkey());
+			if (!(motd.getId().equals(oldMotd.getId()) && motd.getSessionkey().equals(oldMotd.getSessionkey())
 					&& motd.getAudience().equals(oldMotd.getAudience()))) {
 				throw new BadRequestException();
 			}
 		}
 
-		return databaseDao.createOrUpdateMotd(motd);
+		return motdRepository.createOrUpdateMotd(motd);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
 	public void deleteMotd(Motd motd) {
-		databaseDao.deleteMotd(motd);
+		motdRepository.deleteMotd(motd);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public void deleteSessionMotd(final String sessionkey, Motd motd) {
-		databaseDao.deleteMotd(motd);
+		motdRepository.deleteMotd(motd);
 	}
 
 	@Override
@@ -171,7 +170,7 @@ public class MotdService implements IMotdService {
 	public MotdList getMotdListForUser(final String username) {
 		final User user = userService.getCurrentUser();
 		if (username.equals(user.getUsername()) && !"guest".equals(user.getType())) {
-			return databaseDao.getMotdListForUser(username);
+			return motdListRepository.getMotdListForUser(username);
 		}
 		return null;
 	}
@@ -181,7 +180,7 @@ public class MotdService implements IMotdService {
 	public MotdList saveUserMotdList(MotdList motdList) {
 		final User user = userService.getCurrentUser();
 		if (user.getUsername().equals(motdList.getUsername())) {
-			return databaseDao.createOrUpdateMotdList(motdList);
+			return motdListRepository.createOrUpdateMotdList(motdList);
 		}
 		return null;
 	}
@@ -191,7 +190,7 @@ public class MotdService implements IMotdService {
 	public MotdList updateUserMotdList(MotdList motdList) {
 		final User user = userService.getCurrentUser();
 		if (user.getUsername().equals(motdList.getUsername())) {
-			return databaseDao.createOrUpdateMotdList(motdList);
+			return motdListRepository.createOrUpdateMotdList(motdList);
 		}
 		return null;
 	}
diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java
index 77a22e5b3999fb7d0d945a06e1b53c07fc0097a3..0257be71d75230b9d3dcfc2a33ea491d571f3f06 100644
--- a/src/main/java/de/thm/arsnova/services/SessionService.java
+++ b/src/main/java/de/thm/arsnova/services/SessionService.java
@@ -20,7 +20,6 @@ package de.thm.arsnova.services;
 import de.thm.arsnova.ImageUtils;
 import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.domain.ILearningProgressFactory;
 import de.thm.arsnova.domain.LearningProgress;
 import de.thm.arsnova.entities.LearningProgressOptions;
@@ -41,6 +40,8 @@ import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.PayloadTooLargeException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.SessionRepository;
+import de.thm.arsnova.persistance.VisitedSessionRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -62,6 +63,9 @@ import java.util.UUID;
 @Service
 public class SessionService implements ISessionService, ApplicationEventPublisherAware {
 
+	@Autowired
+	private SessionRepository sessionRepository;
+
 	public static class SessionNameComparator implements Comparator<Session>, Serializable {
 		private static final long serialVersionUID = 1L;
 
@@ -101,7 +105,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	private static final long SESSION_INACTIVITY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
 
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private VisitedSessionRepository visitedSessionRepository;
 
 	@Autowired
 	private IUserService userService;
@@ -134,7 +138,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 			logger.info("Delete inactive sessions.");
 			long unixTime = System.currentTimeMillis();
 			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
-			databaseDao.deleteInactiveGuestSessions(lastActivityBefore);
+			sessionRepository.deleteInactiveGuestSessions(lastActivityBefore);
 		}
 	}
 
@@ -144,19 +148,15 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 			logger.info("Delete lists of visited session for inactive users.");
 			long unixTime = System.currentTimeMillis();
 			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
-			databaseDao.deleteInactiveGuestVisitedSessionLists(lastActivityBefore);
+			visitedSessionRepository.deleteInactiveGuestVisitedSessionLists(lastActivityBefore);
 		}
 	}
 
-	public void setDatabaseDao(final IDatabaseDao newDatabaseDao) {
-		databaseDao = newDatabaseDao;
-	}
-
 	@Override
 	public Session joinSession(final String keyword, final UUID socketId) {
 		/* Socket.IO solution */
 
-		Session session = null != keyword ? databaseDao.getSessionFromKeyword(keyword) : null;
+		Session session = null != keyword ? sessionRepository.getSessionFromKeyword(keyword) : null;
 
 		if (null == session) {
 			userService.removeUserFromSessionBySocketId(socketId);
@@ -167,9 +167,9 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 		userService.addUserToSessionBySocketId(socketId, keyword);
 
 		if (session.getCreator().equals(user.getUsername())) {
-			databaseDao.updateSessionOwnerActivity(session);
+			sessionRepository.updateSessionOwnerActivity(session);
 		}
-		databaseDao.registerAsOnlineUser(user, session);
+		sessionRepository.registerAsOnlineUser(user, session);
 
 		if (connectorClient != null && session.isCourseSession()) {
 			final String courseid = session.getCourseId();
@@ -190,7 +190,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public Session getSessionForAdmin(final String keyword) {
-		return databaseDao.getSessionFromKeyword(keyword);
+		return sessionRepository.getSessionFromKeyword(keyword);
 	}
 
 	/*
@@ -199,7 +199,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	 */
 	@Override
 	public Session getSessionInternal(final String keyword, final User user) {
-		final Session session = databaseDao.getSessionFromKeyword(keyword);
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
 		if (session == null) {
 			throw new NotFoundException();
 		}
@@ -222,50 +222,50 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public List<Session> getUserSessions(String username) {
-		return databaseDao.getSessionsForUsername(username, 0, 0);
+		return sessionRepository.getSessionsForUsername(username, 0, 0);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Session> getMySessions(final int offset, final int limit) {
-		return databaseDao.getMySessions(userService.getCurrentUser(), offset, limit);
+		return sessionRepository.getMySessions(userService.getCurrentUser(), offset, limit);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<SessionInfo> getPublicPoolSessionsInfo() {
-		return databaseDao.getPublicPoolSessionsInfo();
+		return sessionRepository.getPublicPoolSessionsInfo();
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<SessionInfo> getMyPublicPoolSessionsInfo() {
-		return databaseDao.getMyPublicPoolSessionsInfo(userService.getCurrentUser());
+		return sessionRepository.getMyPublicPoolSessionsInfo(userService.getCurrentUser());
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<SessionInfo> getMySessionsInfo(final int offset, final int limit) {
 		final User user = userService.getCurrentUser();
-		return databaseDao.getMySessionsInfo(user, offset, limit);
+		return sessionRepository.getMySessionsInfo(user, offset, limit);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Session> getMyVisitedSessions(final int offset, final int limit) {
-		return databaseDao.getMyVisitedSessions(userService.getCurrentUser(), offset, limit);
+		return sessionRepository.getVisitedSessionsForUsername(userService.getCurrentUser().getUsername(), offset, limit);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(1, 'motd', 'admin')")
 	public List<Session> getUserVisitedSessions(String username) {
-		return databaseDao.getVisitedSessionsForUsername(username, 0, 0);
+		return sessionRepository.getVisitedSessionsForUsername(username, 0, 0);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<SessionInfo> getMyVisitedSessionsInfo(final int offset, final int limit) {
-		return databaseDao.getMyVisitedSessionsInfo(userService.getCurrentUser(), offset, limit);
+		return sessionRepository.getMyVisitedSessionsInfo(userService.getCurrentUser(), offset, limit);
 	}
 
 	@Override
@@ -294,14 +294,14 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 		sf.setPi(true);
 		session.setFeatures(sf);
 
-		final Session result = databaseDao.saveSession(userService.getCurrentUser(), session);
+		final Session result = sessionRepository.saveSession(userService.getCurrentUser(), session);
 		this.publisher.publishEvent(new NewSessionEvent(this, result));
 		return result;
 	}
 
 	@Override
 	public boolean sessionKeyAvailable(final String keyword) {
-		return databaseDao.sessionKeyAvailable(keyword);
+		return sessionRepository.sessionKeyAvailable(keyword);
 	}
 
 	@Override
@@ -319,7 +319,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@Override
 	public int countSessions(final List<Course> courses) {
-		final List<Session> sessions = databaseDao.getCourseSessions(courses);
+		final List<Session> sessions = sessionRepository.getCourseSessions(courses);
 		if (sessions == null) {
 			return 0;
 		}
@@ -333,20 +333,20 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@Override
 	public Session setActive(final String sessionkey, final Boolean lock) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (!session.isCreator(user)) {
 			throw new ForbiddenException("User is not session creator.");
 		}
 		session.setActive(lock);
 		this.publisher.publishEvent(new StatusSessionEvent(this, session));
-		return databaseDao.updateSession(session);
+		return sessionRepository.updateSession(session);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#session, 'owner')")
 	public Session updateSession(final String sessionkey, final Session session) {
-		final Session existingSession = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
 
 		existingSession.setActive(session.isActive());
 		existingSession.setShortName(session.getShortName());
@@ -366,17 +366,17 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 		handleLogo(session);
 		existingSession.setPpLogo(session.getPpLogo());
 
-		return databaseDao.updateSession(existingSession);
+		return sessionRepository.updateSession(existingSession);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
 	public Session changeSessionCreator(String sessionkey, String newCreator) {
-		final Session existingSession = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
 		if (existingSession == null) {
 			throw new NullPointerException("Could not load session " + sessionkey + ".");
 		}
-		return databaseDao.changeSessionCreator(existingSession, newCreator);
+		return sessionRepository.changeSessionCreator(existingSession, newCreator);
 	}
 
 	/*
@@ -386,7 +386,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	public Session updateSessionInternal(final Session session, final User user) {
 		if (session.isCreator(user)) {
-			return databaseDao.updateSession(session);
+			return sessionRepository.updateSession(session);
 		}
 		return null;
 	}
@@ -394,9 +394,9 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public void deleteSession(final String sessionkey) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 
-		databaseDao.deleteSession(session);
+		sessionRepository.deleteSession(session);
 
 		this.publisher.publishEvent(new DeleteSessionEvent(this, session));
 	}
@@ -404,7 +404,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public LearningProgressValues getLearningProgress(final String sessionkey, final String progressType, final String questionVariant) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		LearningProgress learningProgress = learningProgressFactory.create(progressType, questionVariant);
 		return learningProgress.getCourseProgress(session);
 	}
@@ -412,7 +412,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public LearningProgressValues getMyLearningProgress(final String sessionkey, final String progressType, final String questionVariant) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		final User user = userService.getCurrentUser();
 		LearningProgress learningProgress = learningProgressFactory.create(progressType, questionVariant);
 		return learningProgress.getMyProgress(session, user);
@@ -422,7 +422,7 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@PreAuthorize("isAuthenticated()")
 	public SessionInfo importSession(ImportExportSession importSession) {
 		final User user = userService.getCurrentUser();
-		final SessionInfo info = databaseDao.importSession(user, importSession);
+		final SessionInfo info = sessionRepository.importSession(user, importSession);
 		if (info == null) {
 			throw new NullPointerException("Could not import session.");
 		}
@@ -432,17 +432,17 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public ImportExportSession exportSession(String sessionkey, Boolean withAnswerStatistics, Boolean withFeedbackQuestions) {
-		return databaseDao.exportSession(sessionkey, withAnswerStatistics, withFeedbackQuestions);
+		return sessionRepository.exportSession(sessionkey, withAnswerStatistics, withFeedbackQuestions);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
 	public SessionInfo copySessionToPublicPool(String sessionkey, de.thm.arsnova.entities.transport.ImportExportSession.PublicPool pp) {
-		ImportExportSession temp = databaseDao.exportSession(sessionkey, false, false);
+		ImportExportSession temp = sessionRepository.exportSession(sessionkey, false, false);
 		temp.getSession().setPublicPool(pp);
 		temp.getSession().setSessionType("public_pool");
 		final User user = userService.getCurrentUser();
-		return databaseDao.importSession(user, temp);
+		return sessionRepository.importSession(user, temp);
 	}
 
 	@Override
@@ -452,24 +452,24 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 	@Override
 	public SessionFeature getSessionFeatures(String sessionkey) {
-		return databaseDao.getSessionFromKeyword(sessionkey).getFeatures();
+		return sessionRepository.getSessionFromKeyword(sessionkey).getFeatures();
 	}
 
 	@Override
 	public SessionFeature changeSessionFeatures(String sessionkey, SessionFeature features) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException("User is not session creator.");
 		}
 		session.setFeatures(features);
 		this.publisher.publishEvent(new FeatureChangeEvent(this, session));
-		return databaseDao.updateSession(session).getFeatures();
+		return sessionRepository.updateSession(session).getFeatures();
 	}
 
 	@Override
 	public boolean lockFeedbackInput(String sessionkey, Boolean lock) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException("User is not session creator.");
@@ -480,19 +480,19 @@ public class SessionService implements ISessionService, ApplicationEventPublishe
 
 		session.setFeedbackLock(lock);
 		this.publisher.publishEvent(new LockFeedbackEvent(this, session));
-		return databaseDao.updateSession(session).getFeedbackLock();
+		return sessionRepository.updateSession(session).getFeedbackLock();
 	}
 
 	@Override
 	public boolean flipFlashcards(String sessionkey, Boolean flip) {
-		final Session session = databaseDao.getSessionFromKeyword(sessionkey);
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException("User is not session creator.");
 		}
 		session.setFlipFlashcards(flip);
 		this.publisher.publishEvent(new FlipFlashcardsEvent(this, session));
-		return databaseDao.updateSession(session).getFlipFlashcards();
+		return sessionRepository.updateSession(session).getFlipFlashcards();
 	}
 
 	private void handleLogo(Session session) {
diff --git a/src/main/java/de/thm/arsnova/services/StatisticsService.java b/src/main/java/de/thm/arsnova/services/StatisticsService.java
index e8e7fff3e2c234b9007811f36490469f46f8cd55..ddf091b7250fcb066dd4661e399c479e69d33f97 100644
--- a/src/main/java/de/thm/arsnova/services/StatisticsService.java
+++ b/src/main/java/de/thm/arsnova/services/StatisticsService.java
@@ -17,8 +17,8 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Statistics;
+import de.thm.arsnova.persistance.StatisticsRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
@@ -29,18 +29,17 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class StatisticsService implements IStatisticsService {
-
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private StatisticsRepository statisticsRepository;
 
 	@Autowired
 	private IUserService userService;
 
 	private Statistics statistics = new Statistics();
 
-	@Scheduled(initialDelay = 0, fixedRate = 300000)
+	@Scheduled(initialDelay = 0, fixedRate = 10000)
 	private void refreshStatistics() {
-		statistics = databaseDao.getStatistics();
+		statistics = statisticsRepository.getStatistics();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java
index 32a4f0837fd0063d364760fb2c8a5a99ab741b3e..fe8fdbc632103524944114306e8539c1c49eb44d 100644
--- a/src/main/java/de/thm/arsnova/services/UserService.java
+++ b/src/main/java/de/thm/arsnova/services/UserService.java
@@ -18,12 +18,12 @@
 package de.thm.arsnova.services;
 
 import com.codahale.metrics.annotation.Gauge;
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.UserRepository;
 import org.apache.commons.lang.RandomStringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.pac4j.oauth.profile.facebook.FacebookProfile;
@@ -96,7 +96,7 @@ public class UserService implements IUserService {
 	private static final ConcurrentHashMap<User, String> user2session = new ConcurrentHashMap<>();
 
 	@Autowired
-	private IDatabaseDao databaseDao;
+	private UserRepository userRepository;
 
 	@Autowired
 	private JavaMailSender mailSender;
@@ -172,7 +172,7 @@ public class UserService implements IUserService {
 		logger.info("Delete inactive users.");
 		long unixTime = System.currentTimeMillis();
 		long lastActivityBefore = unixTime - ACTIVATION_KEY_DURABILITY_MS;
-		databaseDao.deleteInactiveUsers(lastActivityBefore);
+		userRepository.deleteInactiveUsers(lastActivityBefore);
 	}
 
 	@Override
@@ -339,7 +339,7 @@ public class UserService implements IUserService {
 
 	@Override
 	public DbUser getDbUser(String username) {
-		return databaseDao.getUser(username.toLowerCase());
+		return userRepository.findUserByUsername(username.toLowerCase());
 	}
 
 	@Override
@@ -360,7 +360,7 @@ public class UserService implements IUserService {
 			return null;
 		}
 
-		if (null != databaseDao.getUser(lcUsername)) {
+		if (null != userRepository.findUserByUsername(lcUsername)) {
 			logger.info("User registration failed. {} already exists.", lcUsername);
 
 			return null;
@@ -372,7 +372,7 @@ public class UserService implements IUserService {
 		dbUser.setActivationKey(RandomStringUtils.randomAlphanumeric(32));
 		dbUser.setCreation(System.currentTimeMillis());
 
-		DbUser result = databaseDao.createOrUpdateUser(dbUser);
+		DbUser result = userRepository.createOrUpdateUser(dbUser);
 		if (null != result) {
 			sendActivationEmail(result);
 		} else {
@@ -436,7 +436,7 @@ public class UserService implements IUserService {
 	@Override
 	public DbUser updateDbUser(DbUser dbUser) {
 		if (null != dbUser.getId()) {
-			return databaseDao.createOrUpdateUser(dbUser);
+			return userRepository.createOrUpdateUser(dbUser);
 		}
 
 		return null;
@@ -456,7 +456,7 @@ public class UserService implements IUserService {
 			throw new NotFoundException();
 		}
 
-		databaseDao.deleteUser(dbUser);
+		userRepository.deleteUser(dbUser);
 
 		return dbUser;
 	}
@@ -478,7 +478,7 @@ public class UserService implements IUserService {
 		dbUser.setPasswordResetKey(RandomStringUtils.randomAlphanumeric(32));
 		dbUser.setPasswordResetTime(System.currentTimeMillis());
 
-		if (null == databaseDao.createOrUpdateUser(dbUser)) {
+		if (null == userRepository.createOrUpdateUser(dbUser)) {
 			logger.error("Password reset failed. {} could not be updated.", username);
 		}
 
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
index 0b91b686e7ffed245f728c7be80c5965990e7fe5..b348dc32cc48c729bde2d61fbad4c0b28c0d26ff 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
+++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
@@ -28,7 +28,7 @@ import com.corundumstudio.socketio.listener.DataListener;
 import com.corundumstudio.socketio.listener.DisconnectListener;
 import com.corundumstudio.socketio.protocol.Packet;
 import com.corundumstudio.socketio.protocol.PacketType;
-import de.thm.arsnova.entities.InterposedQuestion;
+import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressOptions;
 import de.thm.arsnova.events.*;
@@ -36,11 +36,11 @@ import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.services.IFeedbackService;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.services.ISessionService;
 import de.thm.arsnova.services.IUserService;
 import de.thm.arsnova.socket.message.Feedback;
-import de.thm.arsnova.socket.message.Question;
+import de.thm.arsnova.socket.message.Content;
 import de.thm.arsnova.socket.message.Session;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -76,7 +76,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	private ISessionService sessionService;
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	private static final Logger logger = LoggerFactory.getLogger(ARSnovaSocketIOServer.class);
 
@@ -181,19 +181,19 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 
 		server.addEventListener(
 				"readInterposedQuestion",
-				de.thm.arsnova.entities.transport.InterposedQuestion.class,
-				new DataListener<de.thm.arsnova.entities.transport.InterposedQuestion>() {
+				de.thm.arsnova.entities.transport.Comment.class,
+				new DataListener<de.thm.arsnova.entities.transport.Comment>() {
 			@Override
 			@Timed(name = "readInterposedQuestionEvent.onData")
 			public void onData(
 					SocketIOClient client,
-					de.thm.arsnova.entities.transport.InterposedQuestion question,
+					de.thm.arsnova.entities.transport.Comment comment,
 					AckRequest ackRequest) {
 				final User user = userService.getUser2SocketId(client.getSessionId());
 				try {
-					questionService.readInterposedQuestionInternal(question.getId(), user);
+					contentService.readInterposedQuestionInternal(comment.getId(), user);
 				} catch (NotFoundException | UnauthorizedException e) {
-					logger.error("Loading of question {} failed for user {} with exception {}", question.getId(), user, e.getMessage());
+					logger.error("Loading of comment {} failed for user {} with exception {}", comment.getId(), user, e.getMessage());
 				}
 			}
 		});
@@ -203,7 +203,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 			public void onData(SocketIOClient client, String answerId, AckRequest ackRequest) {
 				final User user = userService.getUser2SocketId(client.getSessionId());
 				try {
-					questionService.readFreetextAnswer(answerId, user);
+					contentService.readFreetextAnswer(answerId, user);
 				} catch (NotFoundException | UnauthorizedException e) {
 					logger.error("Marking answer {} as read failed for user {} with exception {}", answerId, user, e.getMessage());
 				}
@@ -360,17 +360,17 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		final de.thm.arsnova.entities.Session session = sessionService.getSessionInternal(sessionKey, user);
 		final de.thm.arsnova.entities.SessionFeature features = sessionService.getSessionFeatures(sessionKey);
 
-		client.sendEvent("unansweredLecturerQuestions", questionService.getUnAnsweredLectureQuestionIds(sessionKey, user));
-		client.sendEvent("unansweredPreparationQuestions", questionService.getUnAnsweredPreparationQuestionIds(sessionKey, user));
-		client.sendEvent("countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		client.sendEvent("countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
+		client.sendEvent("unansweredLecturerQuestions", contentService.getUnAnsweredLectureQuestionIds(sessionKey, user));
+		client.sendEvent("unansweredPreparationQuestions", contentService.getUnAnsweredPreparationQuestionIds(sessionKey, user));
+		client.sendEvent("countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		client.sendEvent("countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
 		client.sendEvent("activeUserCountData", sessionService.activeUsers(sessionKey));
 		client.sendEvent("learningProgressOptions", session.getLearningProgressOptions());
 		final de.thm.arsnova.entities.Feedback fb = feedbackService.getFeedback(sessionKey);
 		client.sendEvent("feedbackData", fb.getValues());
 
 		if (features.isFlashcard() || features.isFlashcardFeature()) {
-			client.sendEvent("countFlashcards", questionService.countFlashcardsForUserInternal(sessionKey));
+			client.sendEvent("countFlashcards", contentService.countFlashcardsForUserInternal(sessionKey));
 			client.sendEvent("flipFlashcards", session.getFlipFlashcards());
 		}
 
@@ -421,34 +421,34 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		broadcastInSession(sessionKey, "activeUserCountData", count);
 	}
 
-	public void reportAnswersToLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final Question lecturerQuestion) {
-		broadcastInSession(session.getKeyword(), "answersToLecQuestionAvail", lecturerQuestion.get_id());
+	public void reportAnswersToLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final Content content) {
+		broadcastInSession(session.getKeyword(), "answersToLecQuestionAvail", content.get_id());
 	}
 
-	public void reportAudienceQuestionAvailable(final de.thm.arsnova.entities.Session session, final InterposedQuestion audienceQuestion) {
+	public void reportAudienceQuestionAvailable(final de.thm.arsnova.entities.Session session, final Comment audienceQuestion) {
 		/* TODO role handling implementation, send this only to users with role lecturer */
-		broadcastInSession(session.getKeyword(), "audQuestionAvail", audienceQuestion.get_id());
+		broadcastInSession(session.getKeyword(), "audQuestionAvail", audienceQuestion.getId());
 	}
 
-	public void reportLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Question> qs) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : qs) {
-			questions.add(new Question(q));
+	public void reportLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Content> qs) {
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : qs) {
+			contents.add(new Content(q));
 		}
 
 		/* TODO role handling implementation, send this only to users with role audience */
 		if (!qs.isEmpty()) {
-			broadcastInSession(session.getKeyword(), "lecQuestionAvail", questions.get(0).get_id()); // deprecated!
+			broadcastInSession(session.getKeyword(), "lecQuestionAvail", contents.get(0).get_id()); // deprecated!
 		}
-		broadcastInSession(session.getKeyword(), "lecturerQuestionAvailable", questions);
+		broadcastInSession(session.getKeyword(), "lecturerQuestionAvailable", contents);
 	}
 
-	public void reportLecturerQuestionsLocked(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Question> qs) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : qs) {
-			questions.add(new Question(q));
+	public void reportLecturerQuestionsLocked(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Content> qs) {
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : qs) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(session.getKeyword(), "lecturerQuestionLocked", questions);
+		broadcastInSession(session.getKeyword(), "lecturerQuestionLocked", contents);
 	}
 
 	public void reportSessionStatus(final String sessionKey, final boolean active) {
@@ -496,7 +496,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	}
 
 	@Override
-	public void visit(NewInterposedQuestionEvent event) {
+	public void visit(NewCommentEvent event) {
 		this.reportAudienceQuestionAvailable(event.getSession(), event.getQuestion());
 	}
 
@@ -505,17 +505,17 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	@Timed(name = "visit.NewAnswerEvent")
 	public void visit(NewAnswerEvent event) {
 		final String sessionKey = event.getSession().getKeyword();
-		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Question(event.getQuestion()));
-		broadcastInSession(sessionKey, "countQuestionAnswersByQuestionId", questionService.getAnswerAndAbstentionCountInternal(event.getQuestion().get_id()));
-		broadcastInSession(sessionKey, "countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
-
-		// Update the unanswered count for the question variant that was answered.
-		final de.thm.arsnova.entities.Question question = event.getQuestion();
-		if ("lecture".equals(question.getQuestionVariant())) {
-			sendToUser(event.getUser(), "unansweredLecturerQuestions", questionService.getUnAnsweredLectureQuestionIds(sessionKey, event.getUser()));
-		} else if ("preparation".equals(question.getQuestionVariant())) {
-			sendToUser(event.getUser(), "unansweredPreparationQuestions", questionService.getUnAnsweredPreparationQuestionIds(sessionKey, event.getUser()));
+		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Content(event.getContent()));
+		broadcastInSession(sessionKey, "countQuestionAnswersByQuestionId", contentService.getAnswerAndAbstentionCountInternal(event.getContent().getId()));
+		broadcastInSession(sessionKey, "countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
+
+		// Update the unanswered count for the content variant that was answered.
+		final de.thm.arsnova.entities.Content content = event.getContent();
+		if ("lecture".equals(content.getQuestionVariant())) {
+			sendToUser(event.getUser(), "unansweredLecturerQuestions", contentService.getUnAnsweredLectureQuestionIds(sessionKey, event.getUser()));
+		} else if ("preparation".equals(content.getQuestionVariant())) {
+			sendToUser(event.getUser(), "unansweredPreparationQuestions", contentService.getUnAnsweredPreparationQuestionIds(sessionKey, event.getUser()));
 		}
 	}
 
@@ -524,10 +524,10 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	@Timed(name = "visit.DeleteAnswerEvent")
 	public void visit(DeleteAnswerEvent event) {
 		final String sessionKey = event.getSession().getKeyword();
-		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Question(event.getQuestion()));
+		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Content(event.getQuestion()));
 		// We do not know which user's answer was deleted, so we can't update his 'unanswered' list of questions...
-		broadcastInSession(sessionKey, "countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
 	}
 
 	@Async
@@ -574,20 +574,20 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 
 	@Override
 	public void visit(LockVotesEvent event) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : event.getQuestions()) {
-			questions.add(new Question(q));
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : event.getQuestions()) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(event.getSession().getKeyword(), "lockVotes", questions);
+		broadcastInSession(event.getSession().getKeyword(), "lockVotes", contents);
 	}
 
 	@Override
 	public void visit(UnlockVotesEvent event) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : event.getQuestions()) {
-			questions.add(new Question(q));
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : event.getQuestions()) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(event.getSession().getKeyword(), "unlockVotes", questions);
+		broadcastInSession(event.getSession().getKeyword(), "unlockVotes", contents);
 	}
 
 	@Override
@@ -597,7 +597,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		broadcastInSession(sessionKey, "featureChange", features);
 
 		if (features.isFlashcard() || features.isFlashcardFeature()) {
-			broadcastInSession(sessionKey, "countFlashcards", questionService.countFlashcardsForUserInternal(sessionKey));
+			broadcastInSession(sessionKey, "countFlashcards", contentService.countFlashcardsForUserInternal(sessionKey));
 			broadcastInSession(sessionKey, "flipFlashcards", event.getSession().getFlipFlashcards());
 		}
 	}
@@ -643,7 +643,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	}
 
 	@Override
-	public void visit(DeleteInterposedQuestionEvent deleteInterposedQuestionEvent) {
+	public void visit(DeleteCommentEvent deleteCommentEvent) {
 		// TODO Auto-generated method stub
 
 	}
diff --git a/src/main/java/de/thm/arsnova/socket/message/Question.java b/src/main/java/de/thm/arsnova/socket/message/Content.java
similarity index 85%
rename from src/main/java/de/thm/arsnova/socket/message/Question.java
rename to src/main/java/de/thm/arsnova/socket/message/Content.java
index c8660e3dbc80ef7e479d86832f9c053d1e688287..f462baa7bb7e62b81f15e512774f630d9e848d80 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Question.java
+++ b/src/main/java/de/thm/arsnova/socket/message/Content.java
@@ -20,14 +20,14 @@ package de.thm.arsnova.socket.message;
 /**
  * Represents a question.
  */
-public class Question {
+public class Content {
 
 	private final String _id;
 	private final String variant;
 
-	public Question(de.thm.arsnova.entities.Question question) {
-		this._id = question.get_id();
-		this.variant = question.getQuestionVariant();
+	public Content(de.thm.arsnova.entities.Content content) {
+		this._id = content.getId();
+		this.variant = content.getQuestionVariant();
 	}
 
 	public String get_id() {
diff --git a/src/main/resources/couchdb/answer.design.js b/src/main/resources/couchdb/Answer.design.js
similarity index 72%
rename from src/main/resources/couchdb/answer.design.js
rename to src/main/resources/couchdb/Answer.design.js
index a79ac0a403f0d1c97e5aa31faea5d8387333bf37..44d0a92f3b4ecf99a9bcfb878668bb2421f19847 100644
--- a/src/main/resources/couchdb/answer.design.js
+++ b/src/main/resources/couchdb/Answer.design.js
@@ -1,59 +1,58 @@
 var designDoc = {
-	"_id": "_design/answer",
+	"_id": "_design/Answer",
 	"language": "javascript",
 	"views": {
-		"doc_by_questionid_user_piround": {
+		"by_questionid": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit([doc.questionId, doc.user, doc.piRound], doc);
+					emit(doc.questionId, {_rev: doc._rev});
 				}
 			}
 		},
-		"doc_by_questionid_timestamp": {
+		"by_questionid_piround_text_subject": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit([doc.questionId, doc.timestamp], doc);
+					emit([doc.questionId, doc.piRound, doc.abstention, doc.answerText, doc.answerSubject, doc.successfulFreeTextAnswer], {_rev: doc._rev});
 				}
-			}
+			},
+			"reduce": "_count"
 		},
-		"doc_by_user_sessionid": {
+		"by_questionid_timestamp": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit([doc.user, doc.sessionId], doc);
+					emit([doc.questionId, doc.timestamp], {_rev: doc._rev});
 				}
 			}
 		},
-		"by_questionid": {
+		"by_questionid_user_piround": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit(doc.questionId, null);
+					emit([doc.questionId, doc.user, doc.piRound], {_rev: doc._rev});
 				}
 			}
 		},
-		"by_questionid_piround_text_subject": {
+		"by_sessionid": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit([doc.questionId, doc.piRound, doc.abstention, doc.answerText, doc.answerSubject, doc.successfulFreeTextAnswer], null);
+					emit(doc.sessionId, {_rev: doc._rev});
 				}
 			},
 			"reduce": "_count"
 		},
-		"by_sessionid": {
-			/* Redundant view but kept for now to allow simpler queries. */
+		"by_sessionid_variant": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit(doc.sessionId, null);
+					emit([doc.sessionId, doc.questionVariant], {_rev: doc._rev});
 				}
 			},
 			"reduce": "_count"
 		},
-		"by_sessionid_variant": {
+		"by_user_sessionid": {
 			"map": function (doc) {
 				if (doc.type === "skill_question_answer") {
-					emit([doc.sessionId, doc.questionVariant], null);
+					emit([doc.user, doc.sessionId], {_rev: doc._rev});
 				}
-			},
-			"reduce": "_count"
+			}
 		},
 		"questionid_by_user_sessionid_variant": {
 			"map": function (doc) {
diff --git a/src/main/resources/couchdb/comment.design.js b/src/main/resources/couchdb/Comment.design.js
similarity index 61%
rename from src/main/resources/couchdb/comment.design.js
rename to src/main/resources/couchdb/Comment.design.js
index 23936b94eb52f3eddd55b416c2dcd19189c26e18..c407a2eb551f8c45f8f87c8efcdc69443d1affc1 100644
--- a/src/main/resources/couchdb/comment.design.js
+++ b/src/main/resources/couchdb/Comment.design.js
@@ -1,45 +1,44 @@
 var designDoc = {
-	"_id": "_design/comment",
+	"_id": "_design/Comment",
 	"language": "javascript",
 	"views": {
-		"doc_by_sessionid_creator_timestamp": {
+		"by_sessionid": {
 			"map": function (doc) {
 				if (doc.type === "interposed_question") {
-					emit([doc.sessionId, doc.creator, doc.timestamp], doc);
+					emit(doc.sessionId, {_rev: doc._rev});
 				}
-			}
+			},
+			"reduce": "_count"
 		},
-		"doc_by_sessionid_timestamp": {
+		"by_sessionid_creator_read": {
 			"map": function (doc) {
 				if (doc.type === "interposed_question") {
-					emit([doc.sessionId, doc.timestamp], doc);
+					emit([doc.sessionId, doc.creator, doc.read], {_rev: doc._rev})
 				}
-			}
+			},
+			"reduce": "_count"
 		},
-		"by_sessionid": {
-			/* Redundant view but kept for now to allow simpler queries. */
+		"by_sessionid_creator_timestamp": {
 			"map": function (doc) {
 				if (doc.type === "interposed_question") {
-					emit(doc.sessionId, null);
+					emit([doc.sessionId, doc.creator, doc.timestamp], {_rev: doc._rev});
 				}
-			},
-			"reduce": "_count"
+			}
 		},
 		"by_sessionid_read": {
 			"map": function (doc) {
 				if (doc.type === "interposed_question") {
-					emit([doc.sessionId, doc.read], null);
+					emit([doc.sessionId, doc.read], {_rev: doc._rev});
 				}
 			},
 			"reduce": "_count"
 		},
-		"by_sessionid_creator_read": {
+		"by_sessionid_timestamp": {
 			"map": function (doc) {
 				if (doc.type === "interposed_question") {
-					emit([doc.sessionId, doc.creator, doc.read], null);
+					emit([doc.sessionId, doc.timestamp], {_rev: doc._rev});
 				}
-			},
-			"reduce": "_count"
+			}
 		}
 	}
 };
diff --git a/src/main/resources/couchdb/Content.design.js b/src/main/resources/couchdb/Content.design.js
new file mode 100644
index 0000000000000000000000000000000000000000..cb3f2f98208cb83b87d7d5b1183f43c969629c1f
--- /dev/null
+++ b/src/main/resources/couchdb/Content.design.js
@@ -0,0 +1,22 @@
+var designDoc = {
+	"_id": "_design/Content",
+	"language": "javascript",
+	"views": {
+		"by_sessionid": {
+			"map": function (doc) {
+				if (doc.type === "skill_question") {
+					emit(doc.sessionId, {_rev: doc._rev});
+				}
+			},
+			"reduce": "_count"
+		},
+		"by_sessionid_variant_active": {
+			"map": function (doc) {
+				if (doc.type === "skill_question") {
+					emit([doc.sessionId, doc.questionVariant, doc.active, doc.subject, doc.text.substr(0, 16)], {_rev: doc._rev});
+				}
+			},
+			"reduce": "_count"
+		}
+	}
+};
diff --git a/src/main/resources/couchdb/user.design.js b/src/main/resources/couchdb/DbUser.design.js
similarity index 69%
rename from src/main/resources/couchdb/user.design.js
rename to src/main/resources/couchdb/DbUser.design.js
index 9299516f89a56f92017224eb5416e16ab3b9cba8..aa63058d15eb951257a7e076ba2f8cb99a25bb15 100644
--- a/src/main/resources/couchdb/user.design.js
+++ b/src/main/resources/couchdb/DbUser.design.js
@@ -1,20 +1,18 @@
 var designDoc = {
-	"_id": "_design/user",
+	"_id": "_design/DbUser",
 	"language": "javascript",
 	"views": {
-		"doc_by_username": {
-			"map": function (doc) {
-				if (doc.type === "userdetails") {
-					emit(doc.username, doc);
-				}
-			}
-		},
 		"by_creation_for_inactive": {
 			"map": function (doc) {
 				if (doc.type === "userdetails" && doc.activationKey) {
 					emit(doc.creation, {_rev: doc._rev});
 				}
 			}
+		},
+		"by_username": {
+			"map": function (doc) {
+				if (doc.type === "userdetails") emit(doc.username, {_rev: doc._rev});
+			}
 		}
 	}
 };
diff --git a/src/main/resources/couchdb/logged_in.design.js b/src/main/resources/couchdb/LoggedIn.design.js
similarity index 94%
rename from src/main/resources/couchdb/logged_in.design.js
rename to src/main/resources/couchdb/LoggedIn.design.js
index 1b196b4f5aecb0e8516d638de58da599941c5913..aadec2d1e364f8ef9b15defa43c75dae29f4ba68 100644
--- a/src/main/resources/couchdb/logged_in.design.js
+++ b/src/main/resources/couchdb/LoggedIn.design.js
@@ -1,14 +1,7 @@
 var designDoc = {
-	"_id": "_design/logged_in",
+	"_id": "_design/LoggedIn",
 	"language": "javascript",
 	"views": {
-		"visited_sessions_by_user": {
-			"map": function (doc) {
-				if (doc.type === "logged_in") {
-					emit(doc.user, doc.visitedSessions);
-				}
-			}
-		},
 		"all": {
 			"map": function (doc) {
 				if (doc.type === "logged_in"){
@@ -22,6 +15,13 @@ var designDoc = {
 					emit(doc.timestamp || 0, {_rev: doc._rev});
 				}
 			}
+		},
+		"visited_sessions_by_user": {
+			"map": function (doc) {
+				if (doc.type === "logged_in") {
+					emit(doc.user, doc.visitedSessions);
+				}
+			}
 		}
 	}
 };
diff --git a/src/main/resources/couchdb/motd.design.js b/src/main/resources/couchdb/Motd.design.js
similarity index 63%
rename from src/main/resources/couchdb/motd.design.js
rename to src/main/resources/couchdb/Motd.design.js
index 33b111ee3f0765829ca9b4731cbeec47f1efbb8f..8e932510dc5ee7fddcae328058b636d1ae7ca2ac 100644
--- a/src/main/resources/couchdb/motd.design.js
+++ b/src/main/resources/couchdb/Motd.design.js
@@ -1,25 +1,25 @@
 var designDoc = {
-	"_id": "_design/motd",
+	"_id": "_design/Motd",
 	"language": "javascript",
 	"views": {
-		"doc_by_sessionkey": {
+		"by_audience_for_global": {
 			"map": function (doc) {
-				if (doc.type === "motd" && doc.audience === "session") {
-					emit(doc.sessionkey, doc);
+				if (doc.type === "motd" && doc.audience !== "session") {
+					emit(doc.audience, {_rev: doc._rev});
 				}
 			}
 		},
-		"doc_by_audience_for_global": {
+		"by_motdkey": {
 			"map": function (doc) {
-				if (doc.type === "motd" && doc.audience !== "session") {
-					emit(doc.audience, doc);
+				if (doc.type === "motd") {
+					emit(doc.motdkey, {_rev: doc._rev});
 				}
 			}
 		},
-		"by_motdkey": {
+		"by_sessionkey": {
 			"map": function (doc) {
-				if (doc.type === "motd") {
-					emit(doc.motdkey, doc);
+				if (doc.type === "motd" && doc.audience === "session") {
+					emit(doc.sessionkey, {_rev: doc._rev});
 				}
 			}
 		}
diff --git a/src/main/resources/couchdb/motdlist.design.js b/src/main/resources/couchdb/MotdList.design.js
similarity index 87%
rename from src/main/resources/couchdb/motdlist.design.js
rename to src/main/resources/couchdb/MotdList.design.js
index 37712e4ea35821de5b9b50d4a2e7b9e703589890..e7ddbe7621d93c89cb4b5ea2784369756452afe6 100644
--- a/src/main/resources/couchdb/motdlist.design.js
+++ b/src/main/resources/couchdb/MotdList.design.js
@@ -1,5 +1,5 @@
 var designDoc = {
-	"_id": "_design/motdlist",
+	"_id": "_design/MotdList",
 	"language": "javascript",
 	"views": {
 		"doc_by_username": {
diff --git a/src/main/resources/couchdb/session.design.js b/src/main/resources/couchdb/Session.design.js
similarity index 76%
rename from src/main/resources/couchdb/session.design.js
rename to src/main/resources/couchdb/Session.design.js
index b8b1346b0a026b32d79c18be68ed55602b1a0357..790e3af6afe0829abf1c2d177a6baa94acb2d390 100644
--- a/src/main/resources/couchdb/session.design.js
+++ b/src/main/resources/couchdb/Session.design.js
@@ -1,18 +1,25 @@
 var designDoc = {
-	"_id": "_design/session",
+	"_id": "_design/Session",
 	"language": "javascript",
 	"views": {
 		"by_courseid": {
 			"map": function (doc) {
-				if (doc.type === "session" && doc.courseId && doc.sessionType !== "public_pool") {
-					emit(doc.courseId, null);
+				if (doc.type === "session" && doc.courseId  && doc.sessionType !== "public_pool") {
+					emit(doc.courseId, {_rev: doc._rev});
 				}
 			}
 		},
 		"by_keyword": {
 			"map": function (doc) {
 				if (doc.type === "session") {
-					emit(doc.keyword, null);
+					emit(doc.keyword, {_rev: doc._rev});
+				}
+			}
+		},
+		"by_lastactivity_for_guests": {
+			"map": function (doc) {
+				if (doc.type === "session" && doc.sessionType !== "public_pool" && doc.creator.indexOf("Guest") === 0) {
+					emit(doc.lastOwnerActivity || doc.creationTime, {_rev: doc._rev});
 				}
 			}
 		},
@@ -29,9 +36,9 @@ var designDoc = {
 				}
 			}
 		},
-		"partial_by_ppsubject_name_for_publicpool": {
+		"partial_by_subject_name_for_publicpool": {
 			"map": function (doc) {
-				if (doc.type === "session" && doc.sessionType === "public_pool") {
+				if (doc.type === "session" && doc.sessiontype === "public_pool") {
 					emit([doc.ppSubject, doc.name], {
 						ppSubject: doc.ppSubject,
 						name: doc.name,
@@ -40,13 +47,6 @@ var designDoc = {
 					});
 				}
 			}
-		},
-		"by_lastactivity_for_guests": {
-			"map": function (doc) {
-				if (doc.type === "session" && doc.sessionType !== "public_pool" && doc.creator.indexOf("Guest") === 0) {
-					emit(doc.lastOwnerActivity || doc.creationTime, {_rev: doc._rev});
-				}
-			}
 		}
 	}
 };
diff --git a/src/main/resources/couchdb/content.design.js b/src/main/resources/couchdb/content.design.js
deleted file mode 100644
index 74518a473fed5b92a06d9059ccaccad10f097e6d..0000000000000000000000000000000000000000
--- a/src/main/resources/couchdb/content.design.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var designDoc = {
-	"_id": "_design/content",
-	"language": "javascript",
-	"views": {
-		"doc_by_sessionid_variant_active": {
-			"map": function (doc) {
-				if (doc.type === "skill_question") {
-					emit([doc.sessionId, doc.questionVariant, doc.active, doc.subject, doc.text.substr(0, 16)], doc);
-				}
-			},
-			"reduce": "_count"
-		},
-		"by_sessionid": {
-			/* Redundant view but kept for now to allow simpler queries. */
-			"map": function (doc) {
-				if (doc.type === "skill_question") {
-					emit(doc.sessionId, null);
-				}
-			},
-			"reduce": "_count"
-		},
-		"by_sessionid_variant_active": {
-			"map": function (doc) {
-				if (doc.type === "skill_question") {
-					emit([doc.sessionId, doc.questionVariant, doc.active, doc.subject, doc.text.substr(0, 16)], null);
-				}
-			},
-			"reduce": "_count"
-		}
-	}
-};
diff --git a/src/main/resources/couchdb/jsToJson.js b/src/main/resources/couchdb/jsToJson.js
new file mode 100644
index 0000000000000000000000000000000000000000..849ac560a9ac6084f979ab265f43bcc9def3688e
--- /dev/null
+++ b/src/main/resources/couchdb/jsToJson.js
@@ -0,0 +1,15 @@
+/**
+ * Transforms JS map functions of the passed object to strings. JS functions are
+ * not valid JSON. Additionally, redundant indentation is removed from the
+ * function string.
+ */
+function jsToJson(designDoc) {
+	var views = designDoc.views;
+	Object.keys(views).forEach(function (viewName) {
+		views[viewName].map = views[viewName].map.toString().replace(/\n\t{3}(\t*)/g, function (m, p1) {
+			return "\n" + p1.replace(/\t/g, "  ");
+		});
+	});
+
+	return designDoc;
+}
diff --git a/src/main/resources/couchdb/lerning_progress.design.js b/src/main/resources/couchdb/lerning_progress.design.js
index 2b35eec84bae6275100ed85f70f95b94d0c2c671..45c27e113fcb2581141d2bcbaf405b926f9eecd1 100644
--- a/src/main/resources/couchdb/lerning_progress.design.js
+++ b/src/main/resources/couchdb/lerning_progress.design.js
@@ -30,7 +30,7 @@ var designDoc = {
 				 var value = 0, answers = [], positiveAnswers = [], score = 0;
 				 if (doc.type === "skill_question" && ["school", "flashcard"].indexOf(doc.questionType) === -1) {
 				 	if ("freetext" === doc.questionType && !doc.fixedAnswer) { return; }
-					answers = doc.possibleAnswers.map(function(answer) { return answer.value || 0; });
+					answers = doc.possibleAnswers.map(function (answer) { return answer.value || 0; });
 					/* find the maximum value */
 					if (doc.fixedAnswer) { value = doc.rating; }
 					else { value = Math.max.apply(null, [0].concat(answers)); }
@@ -38,9 +38,9 @@ var designDoc = {
 					if (doc.questionType === "vote" && value === 0) { return; }
 					/* special case for mc and grid questions: add up all positive answers. */
 					if (["grid", "mc"].indexOf(doc.questionType) !== -1) {
-						positiveAnswers = answers.filter(function(val) { return val >= 0; });
+						positiveAnswers = answers.filter(function (val) { return val >= 0; });
 						if (positiveAnswers.length > 0) {
-							value = positiveAnswers.reduce(function(prev, cur) { return prev + cur; }, 0);
+							value = positiveAnswers.reduce(function (prev, cur) { return prev + cur; }, 0);
 						}
 					}
 					emit([doc.sessionId, doc._id], {
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
index 4e7f0b83335831889672427c0547478cda89c690..03878657246d76d7580eeb9cb346974fe2ef0177 100644
--- a/src/main/webapp/WEB-INF/web.xml
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -11,6 +11,7 @@
 		<param-name>contextConfigLocation</param-name>
 		<param-value>
 			de.thm.arsnova.config.AppConfig
+			de.thm.arsnova.config.PersistanceConfig
 			de.thm.arsnova.config.SecurityConfig
 		</param-value>
 	</context-param>
diff --git a/src/test/java/de/thm/arsnova/ImageUtilsTest.java b/src/test/java/de/thm/arsnova/ImageUtilsTest.java
index 5a2090d6d47b72a48666ebf508121a93be15cf59..5c27e1eb596eaf7dbd2809fd3154dd6c797bb232 100644
--- a/src/test/java/de/thm/arsnova/ImageUtilsTest.java
+++ b/src/test/java/de/thm/arsnova/ImageUtilsTest.java
@@ -19,6 +19,7 @@ package de.thm.arsnova;
 
 import de.thm.arsnova.config.AppConfig;
 import de.thm.arsnova.config.TestAppConfig;
+import de.thm.arsnova.config.TestPersistanceConfig;
 import de.thm.arsnova.config.TestSecurityConfig;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@ import static org.junit.Assert.*;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
+@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestPersistanceConfig.class, TestSecurityConfig.class})
 @ActiveProfiles("test")
 public class ImageUtilsTest {
 
diff --git a/src/test/java/de/thm/arsnova/config/AppConfigTest.java b/src/test/java/de/thm/arsnova/config/AppConfigTest.java
index 8ad3630ccfb52dc086113f1219623e720a6b5871..355cd7f0720cfc8b2f906408e10bdb85cbee02e2 100644
--- a/src/test/java/de/thm/arsnova/config/AppConfigTest.java
+++ b/src/test/java/de/thm/arsnova/config/AppConfigTest.java
@@ -31,7 +31,7 @@ import static org.junit.Assert.assertNull;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
+@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestPersistanceConfig.class, TestSecurityConfig.class})
 @ActiveProfiles("test")
 public class AppConfigTest extends AbstractJUnit4SpringContextTests {
 
diff --git a/src/test/java/de/thm/arsnova/config/TestAppConfig.java b/src/test/java/de/thm/arsnova/config/TestAppConfig.java
index ff715cd5c31e2e28a4247152a9511d9ce7e33371..f2759881edb031f59b10e8070fe79f706b9400ad 100644
--- a/src/test/java/de/thm/arsnova/config/TestAppConfig.java
+++ b/src/test/java/de/thm/arsnova/config/TestAppConfig.java
@@ -1,7 +1,5 @@
 package de.thm.arsnova.config;
 
-import de.thm.arsnova.dao.IDatabaseDao;
-import de.thm.arsnova.dao.StubDatabaseDao;
 import de.thm.arsnova.services.StubUserService;
 import de.thm.arsnova.socket.ARSnovaSocket;
 import de.thm.arsnova.socket.ARSnovaSocketIOServer;
@@ -60,11 +58,6 @@ public class TestAppConfig {
 		return configurer;
 	}
 
-	@Bean
-	public IDatabaseDao databaseDao() {
-		return new StubDatabaseDao();
-	}
-
 	@Bean(name = "socketServer", initMethod = "startServer", destroyMethod = "stopServer")
 	public ARSnovaSocket socketTestServer() {
 		final int testSocketPort = 1234 + testPortOffset++ % 10;
diff --git a/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java b/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..4801a85488368fff06279def60b4d0cc18230e65
--- /dev/null
+++ b/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java
@@ -0,0 +1,66 @@
+package de.thm.arsnova.config;
+
+import de.thm.arsnova.persistance.*;
+import org.mockito.Mockito;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+
+@Profile("test")
+@Configuration
+public class TestPersistanceConfig {
+	@Bean
+	public LogEntryRepository logEntryRepository() {
+		return Mockito.mock(LogEntryRepository.class);
+	}
+
+	@Bean
+	public UserRepository userRepository() {
+		return Mockito.mock(UserRepository.class);
+	}
+
+	@Bean
+	public SessionRepository sessionRepository() {
+		return Mockito.mock(SessionRepository.class);
+	}
+
+	@Bean
+	public CommentRepository commentRepository() {
+		return Mockito.mock(CommentRepository.class);
+	}
+
+	@Bean
+	public ContentRepository contentRepository() {
+		return Mockito.mock(ContentRepository.class);
+	}
+
+	@Bean
+	public AnswerRepository answerRepository() {
+		return Mockito.mock(AnswerRepository.class);
+	}
+
+	@Bean
+	public MotdRepository motdRepository() {
+		return Mockito.mock(MotdRepository.class);
+	}
+
+	@Bean
+	public MotdListRepository motdListRepository() {
+		return Mockito.mock(MotdListRepository.class);
+	}
+
+	@Bean
+	public VisitedSessionRepository visitedSessionRepository() {
+		return Mockito.mock(VisitedSessionRepository.class);
+	}
+
+	@Bean
+	public StatisticsRepository statisticsRepository() {
+		return Mockito.mock(StatisticsRepository.class);
+	}
+
+	@Bean
+	public SessionStatisticsRepository sessionStatisticsRepository() {
+		return Mockito.mock(SessionStatisticsRepository.class);
+	}
+}
diff --git a/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java b/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java
index c10aecac79c2e4135af085d55e3bca0a2b7d021c..09ee64b6a4cfbe803b57aab8c3352c327741254f 100644
--- a/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java
+++ b/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java
@@ -19,6 +19,7 @@ package de.thm.arsnova.controller;
 
 import de.thm.arsnova.config.AppConfig;
 import de.thm.arsnova.config.TestAppConfig;
+import de.thm.arsnova.config.TestPersistanceConfig;
 import de.thm.arsnova.config.TestSecurityConfig;
 import de.thm.arsnova.services.StubUserService;
 import org.junit.After;
@@ -38,7 +39,7 @@ import java.util.List;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
+@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestPersistanceConfig.class, TestSecurityConfig.class})
 @ActiveProfiles("test")
 public abstract class AbstractControllerTest extends AbstractJUnit4SpringContextTests {
 
diff --git a/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java b/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java
deleted file mode 100644
index 6824a3706a063b96f8421b413d0ee3d6a5207488..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.controller;
-
-import de.thm.arsnova.services.StubUserService;
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-public class FeedbackControllerTest extends AbstractControllerTest {
-
-	@Autowired
-	private StubUserService userService;
-
-	private MockMvc mockMvc;
-
-	@Autowired
-	private WebApplicationContext webApplicationContext;
-
-	@Before
-	public void setup() {
-		mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
-	}
-
-	@Test
-	public void testShouldNotGetFeedbackForUnknownSession() throws Exception {
-		userService.setUserAuthenticated(true);
-		mockMvc.perform(get("/session/00000000/feedback").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isNotFound());
-	}
-
-	@Test
-	public void testShouldNotGetAverageFeedbackContentForSessionWithoutFeedback() throws Exception {
-		userService.setUserAuthenticated(true);
-		mockMvc.perform(get("/session/12345678/averagefeedback").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isNoContent());
-	}
-
-	@Test
-	public void testShouldNotGetCorrectFeedbackCountForSessionWithoutFeedback() throws Exception {
-		userService.setUserAuthenticated(true);
-		mockMvc.perform(get("/session/12345678/feedbackcount").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(content().string("0"));
-	}
-
-	@Test
-	public void testShouldReturnFeedback() throws Exception {
-		userService.setUserAuthenticated(true);
-		mockMvc.perform(get("/session/87654321/feedback").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
-		.andExpect(jsonPath("$.values").isArray());
-	}
-
-	@Test
-	public void testShouldReturnXDeprecatedApiHeaderForFeedback() throws Exception {
-		userService.setUserAuthenticated(true);
-		mockMvc.perform(get("/session/87654321/feedback").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(header().string(AbstractController.X_DEPRECATED_API, "1"));
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java b/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java
deleted file mode 100644
index 47a9b29c0e1d35b9ede8046a0562e4384f41bad9..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.controller;
-
-import de.thm.arsnova.services.StubUserService;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-public class LecturerQuestionControllerTest extends AbstractControllerTest {
-
-	@Autowired
-	private StubUserService userService;
-
-	@Autowired
-	private LecturerQuestionController questionController;
-
-	private MockMvc mockMvc;
-
-	@Autowired
-	private WebApplicationContext webApplicationContext;
-
-	@Before
-	public void startup() {
-		mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
-		SecurityContextHolder.clearContext();
-	}
-
-	@After
-	public void cleanup() {
-		SecurityContextHolder.clearContext();
-		userService.setUserAuthenticated(false);
-	}
-
-	@Test
-	public void testShouldNotGetLecturerQuestionsIfUnauthorized() throws Exception {
-		setAuthenticated(false, "nobody");
-
-		mockMvc.perform(
-				get("/lecturerquestion/")
-				.param("sessionkey", "12345678").param("lecturequestionsonly", "true")
-				.accept(MediaType.APPLICATION_JSON)
-				).andExpect(status().isUnauthorized()
-						);
-	}
-
-	@Test
-	public void testShouldNotGetPreparationQuestionsIfUnauthorized() throws Exception {
-		setAuthenticated(false, "nobody");
-
-		mockMvc.perform(
-				get("/lecturerquestion/")
-				.param("sessionkey", "12345678").param("preparationquestionsonly", "true")
-				.accept(MediaType.APPLICATION_JSON)
-				).andExpect(status().isUnauthorized()
-						);
-	}
-
-	@Test
-	public void testShouldReturnQuestionCount() throws Exception {
-		setAuthenticated(true, "somebody");
-
-		mockMvc.perform(
-				get("/lecturerquestion/count")
-				.param("sessionkey", "12345678").param("lecturequestionsonly", "true")
-				.accept(MediaType.APPLICATION_JSON)
-				).andExpect(status().isOk())
-				.andExpect(content().string("0")
-						);
-	}
-
-	@Test
-	public void testShouldReturnXDeprecatedApiHeaderForQuestionCount() throws Exception {
-		setAuthenticated(true, "somebody");
-
-		mockMvc.perform(
-				get("/lecturerquestion/count")
-				.param("sessionkey", "12345678").param("lecturequestionsonly", "true")
-				.accept(MediaType.APPLICATION_JSON)
-				).andExpect(status().isOk())
-				.andExpect(header().string(AbstractController.X_DEPRECATED_API, "1")
-						);
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java b/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java
deleted file mode 100644
index 3f219d532f5ed1b9f961c463d68563275095e338..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.controller;
-
-import de.thm.arsnova.services.StubUserService;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-public class SessionControllerTest extends AbstractControllerTest {
-
-	@Autowired
-	private StubUserService userService;
-
-	@Autowired
-	private SessionController sessionController;
-
-	private MockMvc mockMvc;
-
-	@Autowired
-	private WebApplicationContext webApplicationContext;
-
-	@Before
-	public void startup() {
-		mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
-		SecurityContextHolder.clearContext();
-	}
-
-	@After
-	public void cleanup() {
-		SecurityContextHolder.clearContext();
-		userService.setUserAuthenticated(false);
-	}
-
-	@Test
-	public void testShouldNotGetUnknownSession() throws Exception {
-		setAuthenticated(true, "ptsr00");
-
-		mockMvc.perform(get("/session/00000000").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isNotFound());
-	}
-
-	@Test
-	public void testShouldNotGetUnknownSessionIfUnauthorized() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/00000000").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isUnauthorized());
-	}
-
-	@Test
-	public void testShouldCreateSessionIfUnauthorized() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(
-				post("/session/")
-				.accept(MediaType.APPLICATION_JSON)
-				.contentType(MediaType.APPLICATION_JSON)
-				.content("{\"keyword\":12345678}")
-				)
-				.andExpect(status().isUnauthorized());
-	}
-
-	@Test
-	public void testShouldNotReturnMySessionsIfUnauthorized() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/").param("ownedonly", "true").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isUnauthorized());
-	}
-
-	@Test
-	public void testShouldNotReturnMyVisitedSessionsIfUnauthorized() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/").param("visitedonly", "true").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isUnauthorized());
-	}
-
-	@Test
-	public void testShouldShowUnimplementedIfNoFlagIsSet() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isNotImplemented());
-	}
-
-	@Test
-	public void testShouldReturnActiveUserCount() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/12345678/activeusercount").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
-		.andExpect(content().string("0"));
-	}
-
-	@Test
-	public void testShouldReturnXDeprecatedApiHeaderForActiveUserCount() throws Exception {
-		setAuthenticated(false, "ptsr00");
-
-		mockMvc.perform(get("/session/12345678/activeusercount").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(header().string(AbstractController.X_DEPRECATED_API, "1"));
-	}
-
-	@Test
-	public void testShouldEndInForbidden() throws Exception {
-		setAuthenticated(true, "ptsr00");
-
-		mockMvc.perform(
-				put("/session/12345678")
-				.content("{\"keyword\":\"12345678\", \"name\":\"Testsession\"}, \"shortName\":\"TS\", \"creator\":\"ptsr00\", \"active\":true")
-				.contentType(MediaType.APPLICATION_JSON)
-				.accept(MediaType.APPLICATION_JSON))
-				.andExpect(status().isOk());
-
-		setAuthenticated(true, "other");
-
-		mockMvc.perform(delete("/session/12345678")).andExpect(status().isForbidden());
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java b/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java
deleted file mode 100644
index b692083fa666a552fcaf568ba5f040b210b441ed..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.controller;
-
-import org.hamcrest.Matchers;
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.MediaType;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.WebApplicationContext;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-public class StatisticsControllerTest extends AbstractControllerTest {
-
-	@Autowired
-	private StatisticsController statisticsController;
-
-	private MockMvc mockMvc;
-
-	@Autowired
-	private WebApplicationContext webApplicationContext;
-
-	@Before
-	public void setup() {
-		mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
-	}
-
-	@Test
-	public void testShouldGetCurrentOnlineUsers() throws Exception {
-		mockMvc.perform(get("/statistics/activeusercount").accept(MediaType.TEXT_PLAIN))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith("text/plain"));
-	}
-
-	@Test
-	public void testShouldSendXDeprecatedApiForGetCurrentOnlineUsers() throws Exception {
-		mockMvc.perform(get("/statistics/activeusercount").accept(MediaType.TEXT_PLAIN))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith("text/plain"))
-		.andExpect(header().string(AbstractController.X_DEPRECATED_API,"1"));
-	}
-
-	@Test
-	public void testShouldGetSessionCount() throws Exception {
-		mockMvc.perform(get("/statistics/sessioncount").accept(MediaType.TEXT_PLAIN))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith("text/plain"))
-		.andExpect(content().string(Matchers.greaterThanOrEqualTo("0")));
-	}
-
-	@Test
-	public void testShouldSendXDeprecatedApiForGetSessionCount() throws Exception {
-		mockMvc.perform(get("/statistics/sessioncount").accept(MediaType.TEXT_PLAIN))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith("text/plain"))
-		.andExpect(header().string(AbstractController.X_DEPRECATED_API,"1"));
-	}
-
-	@Test
-	public void testShouldGetStatistics() throws Exception {
-		mockMvc.perform(get("/statistics").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
-		.andExpect(jsonPath("$.answers").exists())
-		.andExpect(jsonPath("$.questions").exists())
-		.andExpect(jsonPath("$.openSessions").exists())
-		.andExpect(jsonPath("$.closedSessions").exists())
-		.andExpect(jsonPath("$.activeUsers").exists())
-		.andExpect(jsonPath("$.interposedQuestions").exists());
-	}
-
-	@Test
-	public void testShouldGetCacheControlHeaderForStatistics() throws Exception {
-		mockMvc.perform(get("/statistics").accept(MediaType.APPLICATION_JSON))
-		.andExpect(status().isOk())
-		.andExpect(header().string("cache-control", "public, max-age=60"));
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
deleted file mode 100644
index aedcf8d648bae20965845bfe2712ec0808c5825a..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
+++ /dev/null
@@ -1,748 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.dao;
-
-import com.fourspaces.couchdb.View;
-import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.domain.CourseScore;
-import de.thm.arsnova.entities.*;
-import de.thm.arsnova.entities.transport.ImportExportSession;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-@Profile("test")
-@Service("databaseDao")
-public class StubDatabaseDao implements IDatabaseDao {
-
-	private static Map<String, Session> stubSessions = new ConcurrentHashMap<>();
-	private static Map<String, Feedback> stubFeedbacks = new ConcurrentHashMap<>();
-	private static Map<String, List<Question>> stubQuestions = new ConcurrentHashMap<>();
-	private static Map<String, User> stubUsers = new ConcurrentHashMap<>();
-
-	public InterposedQuestion interposedQuestion;
-
-	public StubDatabaseDao() {
-		fillWithDummySessions();
-		fillWithDummyFeedbacks();
-		fillWithDummyQuestions();
-	}
-
-	public void cleanupTestData() {
-		stubSessions.clear();
-		stubFeedbacks.clear();
-		stubQuestions.clear();
-		stubUsers.clear();
-
-		fillWithDummySessions();
-		fillWithDummyFeedbacks();
-		fillWithDummyQuestions();
-	}
-
-	private void fillWithDummySessions() {
-		Session session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("12345678");
-		session.setName("TestSession1");
-		session.setShortName("TS1");
-
-		stubSessions.put("12345678", session);
-
-		session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("87654321");
-		session.setName("TestSession2");
-		session.setShortName("TS2");
-
-		stubSessions.put("87654321", session);
-
-		session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("18273645");
-		session.setName("TestSession2");
-		session.setShortName("TS3");
-
-		stubSessions.put("18273645", session);
-	}
-
-	private void fillWithDummyFeedbacks() {
-		stubFeedbacks.put("12345678", new Feedback(0, 0, 0, 0));
-		stubFeedbacks.put("87654321", new Feedback(2, 3, 5, 7));
-		stubFeedbacks.put("18273645", new Feedback(2, 3, 5, 11));
-	}
-
-	private void fillWithDummyQuestions() {
-		List<Question> questions = new ArrayList<>();
-		questions.add(new Question());
-		stubQuestions.put("12345678", questions);
-	}
-
-	@Override
-	public Session saveSession(User user, Session session) {
-		stubSessions.put(session.getKeyword(), session);
-		return session;
-	}
-
-	@Override
-	public boolean sessionKeyAvailable(String keyword) {
-		return (stubSessions.get(keyword) == null);
-	}
-
-	@Override
-	public void log(String event, Map<String, Object> payload, LogEntry.LogLevel level) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void log(String event, Map<String, Object> payload) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void log(String event, LogEntry.LogLevel level, Object... rawPayload) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void log(String event, Object... rawPayload) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public Session getSessionFromKeyword(String keyword) {
-		return stubSessions.get(keyword);
-	}
-
-	@Override
-	public Question saveQuestion(Session session, Question question) {
-		List<Question> questions = stubQuestions.get(session.getKeyword());
-		questions.add(question);
-		stubQuestions.put(session.get_id(), questions);
-
-		return question;
-	}
-
-	@Override
-	public Question getQuestion(String id) {
-		// Simply ... no such question ;-)
-		return null;
-	}
-
-	@Override
-	public int getSkillQuestionCount(Session session) {
-		return stubQuestions.get(session.getKeyword()).size();
-	}
-
-	@Override
-	public List<Session> getMySessions(User user, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Session> getSessionsForUsername(String username, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Session> getPublicPoolSessions() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<SessionInfo> getPublicPoolSessionsInfo() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Session> getMyPublicPoolSessions(User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<SessionInfo> getMyPublicPoolSessionsInfo(final User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public LoggedIn registerAsOnlineUser(User u, Session s) {
-		stubUsers.put(s.getKeyword(), u);
-		return new LoggedIn();
-	}
-
-	@Override
-	public Session updateSessionOwnerActivity(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Answer getMyAnswer(User user, String questionId, int piRound) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int getAnswerCount(Question question, int piRound) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public List<Answer> getFreetextAnswers(String questionId, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getMyAnswers(User user, Session session) {
-		return new ArrayList<>();
-	}
-
-	@Override
-	public int getTotalAnswerCount(String sessionKey) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getInterposedCount(String sessionKey) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public List<InterposedQuestion> getInterposedQuestions(Session session, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public InterposedQuestion saveQuestion(Session session, InterposedQuestion question, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public InterposedQuestion getInterposedQuestion(String questionId) {
-		return this.interposedQuestion;
-	}
-
-	@Override
-	public void markInterposedQuestionAsRead(InterposedQuestion question) {
-		this.interposedQuestion.setRead(true);
-	}
-
-	@Override
-	public List<Session> getMyVisitedSessions(User user, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public InterposedReadingCount getInterposedReadingCount(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getUnAnsweredQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Question updateQuestion(Question question) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteQuestionWithAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int deleteAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public Answer updateAnswer(Answer answer) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Session getSessionFromId(String sessionId) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public void deleteAnswer(String answerId) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public void deleteInterposedQuestion(InterposedQuestion question) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public List<Session> getCourseSessions(List<Course> courses) {
-		return null;
-	}
-
-	@Override
-	public Session updateSession(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Session changeSessionCreator(Session session, final String newCreator) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int[] deleteSession(Session session) {
-		return new int[] { 0,0 };
-	}
-
-	@Override
-	public int[] deleteInactiveGuestSessions(long lastActivityBefore) {
-		return new int[] { 0,0 };
-	}
-
-	@Override
-	public int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int[] deleteAllQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int getLectureQuestionCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getFlashcardCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getPreparationQuestionCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int countLectureQuestionAnswers(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int countPreparationQuestionAnswers(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int[] deleteAllLectureQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int[] deleteAllFlashcardsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int[] deleteAllPreparationQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public List<String> getUnAnsweredLectureQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getUnAnsweredPreparationQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteAllInterposedQuestions(Session session) {
-		return 0;
-	}
-
-	@Override
-	public void publishQuestions(Session session, boolean publish, List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public List<Question> publishAllQuestions(Session session, boolean publish) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteAllQuestionsAnswers(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public DbUser createOrUpdateUser(DbUser user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public DbUser getUser(String username) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public CourseScore getLearningProgress(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public boolean deleteUser(DbUser dbUser) {
-		// TODO Auto-generated method stub
-		return false;
-	}
-
-	@Override
-	public int deleteInactiveUsers(long lastActivityBefore) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public List<InterposedQuestion> getInterposedQuestions(Session session, User user, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteAllInterposedQuestions(Session session, User user) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public InterposedReadingCount getInterposedReadingCount(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteAllPreparationAnswers(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int deleteAllLectureAnswers(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getAbstentionAnswerCount(String questionId) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public SessionInfo importSession(User user, ImportExportSession importSession) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public ImportExportSession exportSession(String sessionkey, Boolean withAnswer, Boolean withFeedbackQuestions) {
-		// TODO Auto.generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getSkillQuestionsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getSkillQuestionsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getLectureQuestionsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getLectureQuestionsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getFlashcardsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getFlashcardsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getAllSkillQuestions(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAnswers(Question question, int piRound) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Answer saveAnswer(Answer answer, User user, Question question, Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Statistics getStatistics() {
-		final Statistics stats = new Statistics();
-		stats.setOpenSessions(3);
-		stats.setClosedSessions(0);
-		stats.setLectureQuestions(0);
-		stats.setAnswers(0);
-		stats.setInterposedQuestions(0);
-		return stats;
-	}
-
-	@Override
-	public List<String> getSubjects(Session session, String questionVariant) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getQuestionsByIds(List<String> ids, Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAllAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int getTotalAnswerCountByQuestion(Question question) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public void resetQuestionsRoundState(Session session,
-			List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public void setVotingAdmissions(Session session, boolean disableVoting, List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public List<Question> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public <T> T getObjectFromId(String documentId, Class<T> klass) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getAdminMotds() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotdsForAll() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotdsForLoggedIn() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotdsForTutors() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotdsForStudents() {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotdsForSession(final String sessionkey) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Motd> getMotds(View view) {
-		return null;
-	}
-
-	@Override
-	public Motd getMotdByKey(String key) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Motd createOrUpdateMotd(Motd motd) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public void deleteMotd(Motd motd) {
-		// TODO Auto-generated method stub
-	}
-
-	@Override
-	public MotdList getMotdListForUser(final String username) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public MotdList createOrUpdateMotdList(MotdList motdlist) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java b/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java
index bfad95b362dba4174ad3cd34c26b014dec1fa4b8..6f42527dc61881ea06d533e29c7932e85c464317 100644
--- a/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java
+++ b/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java
@@ -17,10 +17,10 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.TestUser;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,7 +50,7 @@ public class PointBasedLearningProgressTest {
 	@Before
 	public void setUp() {
 		this.courseScore = new CourseScore();
-		IDatabaseDao db = mock(IDatabaseDao.class);
+		SessionStatisticsRepository db = mock(SessionStatisticsRepository.class);
 		when(db.getLearningProgress(null)).thenReturn(courseScore);
 		this.lp = new PointBasedLearningProgress(db);
 	}
diff --git a/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java b/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java
index 7e029a6cb9a78144a76644ec018daca03f37b2c7..8adf79257cd12f28dafe08d6f6e91f6524628e3e 100644
--- a/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java
+++ b/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java
@@ -17,10 +17,10 @@
  */
 package de.thm.arsnova.domain;
 
-import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.TestUser;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.persistance.SessionStatisticsRepository;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -50,7 +50,7 @@ public class QuestionBasedLearningProgressTest {
 	@Before
 	public void setUp() {
 		this.courseScore = new CourseScore();
-		IDatabaseDao db = mock(IDatabaseDao.class);
+		SessionStatisticsRepository db = mock(SessionStatisticsRepository.class);
 		when(db.getLearningProgress(null)).thenReturn(courseScore);
 		this.lp = new QuestionBasedLearningProgress(db);
 	}
diff --git a/src/test/java/de/thm/arsnova/entities/QuestionTest.java b/src/test/java/de/thm/arsnova/entities/ContentTest.java
similarity index 96%
rename from src/test/java/de/thm/arsnova/entities/QuestionTest.java
rename to src/test/java/de/thm/arsnova/entities/ContentTest.java
index 57569e6f220f4b8463bd62eb11e6e9f336069864..fc6297c8d5668b825f563e1cae5550dae3d38189 100644
--- a/src/test/java/de/thm/arsnova/entities/QuestionTest.java
+++ b/src/test/java/de/thm/arsnova/entities/ContentTest.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
 
 import static org.junit.Assert.assertEquals;
 
-public class QuestionTest {
+public class ContentTest {
 
 	@SuppressWarnings("serial")
 	@Test
@@ -36,7 +36,7 @@ public class QuestionTest {
 		p2.setText("No");
 		p2.setCorrect(false);
 		p2.setValue(-10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("yesno");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -62,7 +62,7 @@ public class QuestionTest {
 		p2.setText("No");
 		p2.setCorrect(false);
 		p2.setValue(-10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setAbstention(true);
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -89,7 +89,7 @@ public class QuestionTest {
 		p3.setText("Maybe");
 		p3.setCorrect(true);
 		p3.setValue(10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("mc");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -134,7 +134,7 @@ public class QuestionTest {
 		p4.setText("1;1");
 		p4.setCorrect(true);
 		p4.setValue(10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("grid");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
diff --git a/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java b/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java
deleted file mode 100644
index b751c9f9fbeaee564cd5d9160442c1c435a26d9c..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.config.AppConfig;
-import de.thm.arsnova.config.TestAppConfig;
-import de.thm.arsnova.config.TestSecurityConfig;
-import de.thm.arsnova.dao.StubDatabaseDao;
-import de.thm.arsnova.entities.TestUser;
-import de.thm.arsnova.exceptions.NoContentException;
-import de.thm.arsnova.exceptions.NotFoundException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
-@ActiveProfiles("test")
-public class FeedbackServiceTest {
-
-	@Autowired
-	private IFeedbackService feedbackService;
-
-	@Autowired
-	private StubUserService userService;
-
-	@Autowired
-	private StubDatabaseDao databaseDao;
-
-	@Before
-	public void setup() {
-		userService.setUserAuthenticated(false);
-
-		feedbackService.saveFeedback("87654321", 0, new TestUser("testuser01"));
-		feedbackService.saveFeedback("87654321", 0, new TestUser("testuser02"));
-		feedbackService.saveFeedback("87654321", 1, new TestUser("testuser11"));
-		feedbackService.saveFeedback("87654321", 1, new TestUser("testuser12"));
-		feedbackService.saveFeedback("87654321", 1, new TestUser("testuser13"));
-		feedbackService.saveFeedback("87654321", 2, new TestUser("testuser21"));
-		feedbackService.saveFeedback("87654321", 2, new TestUser("testuser22"));
-		feedbackService.saveFeedback("87654321", 2, new TestUser("testuser23"));
-		feedbackService.saveFeedback("87654321", 2, new TestUser("testuser24"));
-		feedbackService.saveFeedback("87654321", 2, new TestUser("testuser25"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser31"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser32"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser33"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser34"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser35"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser36"));
-		feedbackService.saveFeedback("87654321", 3, new TestUser("testuser37"));
-
-		feedbackService.saveFeedback("18273645", 0, new TestUser("testuser01"));
-		feedbackService.saveFeedback("18273645", 0, new TestUser("testuser02"));
-		feedbackService.saveFeedback("18273645", 1, new TestUser("testuser11"));
-		feedbackService.saveFeedback("18273645", 1, new TestUser("testuser12"));
-		feedbackService.saveFeedback("18273645", 1, new TestUser("testuser13"));
-		feedbackService.saveFeedback("18273645", 2, new TestUser("testuser21"));
-		feedbackService.saveFeedback("18273645", 2, new TestUser("testuser22"));
-		feedbackService.saveFeedback("18273645", 2, new TestUser("testuser23"));
-		feedbackService.saveFeedback("18273645", 2, new TestUser("testuser24"));
-		feedbackService.saveFeedback("18273645", 2, new TestUser("testuser25"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser31"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser32"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser33"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser34"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser35"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser36"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser37"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser38"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser39"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser310"));
-		feedbackService.saveFeedback("18273645", 3, new TestUser("testuser311"));
-
-	}
-
-	@After
-	public void cleanup() {
-		databaseDao.cleanupTestData();
-		userService.setUserAuthenticated(false);
-	}
-
-	@Test(expected = NotFoundException.class)
-	public void testShouldFindFeedbackForNonExistantSession() {
-		userService.setUserAuthenticated(true);
-		feedbackService.getFeedback("00000000");
-	}
-
-	@Test
-	public void testShouldReturnFeedback() {
-		userService.setUserAuthenticated(true);
-		assertNotNull(feedbackService.getFeedback("87654321"));
-		assertEquals(2, (int) feedbackService.getFeedback("87654321").getValues().get(0));
-		assertEquals(3, (int) feedbackService.getFeedback("87654321").getValues().get(1));
-		assertEquals(5, (int) feedbackService.getFeedback("87654321").getValues().get(2));
-		assertEquals(7, (int) feedbackService.getFeedback("87654321").getValues().get(3));
-	}
-
-	@Test(expected = NotFoundException.class)
-	public void testShouldFindFeedbackCountForNonExistantSession() {
-		userService.setUserAuthenticated(true);
-		feedbackService.getFeedbackCount("00000000");
-	}
-
-	@Test
-	public void testShouldReturnFeedbackCount() {
-		userService.setUserAuthenticated(true);
-		assertEquals(17, feedbackService.getFeedbackCount("87654321"));
-	}
-
-	@Test(expected = NotFoundException.class)
-	public void testShouldFindAverageFeedbackForNonExistantSession() {
-		userService.setUserAuthenticated(true);
-		feedbackService.getAverageFeedback("00000000");
-	}
-
-	@Test
-	public void testShouldReturnZeroFeedbackCountForNoFeedbackAtAll() {
-		userService.setUserAuthenticated(true);
-		assertEquals(0, feedbackService.getFeedbackCount("12345678"));
-	}
-
-	@Test(expected = NoContentException.class)
-	public void testShouldReturnAverageFeedbackForNoFeedbackAtAll() {
-		userService.setUserAuthenticated(true);
-		feedbackService.getAverageFeedback("12345678");
-	}
-
-	@Test
-	public void testShouldReturnAverageFeedbackRounded() {
-		userService.setUserAuthenticated(true);
-		assertEquals(2, feedbackService.getAverageFeedbackRounded("18273645"));
-	}
-
-	@Test
-	public void testShouldReturnAverageFeedbackNotRounded() {
-		userService.setUserAuthenticated(true);
-		assertEquals(2.1904, feedbackService.getAverageFeedback("18273645"), 0.001);
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java b/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
deleted file mode 100644
index 5482ba33d035b60292329dec2fc83c8192c0a8ff..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.config.AppConfig;
-import de.thm.arsnova.config.TestAppConfig;
-import de.thm.arsnova.config.TestSecurityConfig;
-import de.thm.arsnova.dao.StubDatabaseDao;
-import de.thm.arsnova.entities.InterposedQuestion;
-import de.thm.arsnova.entities.Question;
-import de.thm.arsnova.exceptions.NotFoundException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.Assert.*;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
-@ActiveProfiles("test")
-public class QuestionServiceTest {
-
-	@Autowired
-	private IQuestionService questionService;
-
-	@Autowired
-	private StubUserService userService;
-
-	@Autowired
-	private StubDatabaseDao databaseDao;
-
-	private void setAuthenticated(final boolean isAuthenticated, final String username) {
-		if (isAuthenticated) {
-			final List<GrantedAuthority> ga = new ArrayList<>();
-			final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, "secret", ga);
-			SecurityContextHolder.getContext().setAuthentication(token);
-			userService.setUserAuthenticated(isAuthenticated, username);
-		} else {
-			userService.setUserAuthenticated(isAuthenticated);
-		}
-	}
-
-	@Before
-	public void startup() {
-		SecurityContextHolder.clearContext();
-	}
-
-	@After
-	public void cleanup() {
-		SecurityContextHolder.clearContext();
-	}
-
-	@Test(expected = AuthenticationCredentialsNotFoundException.class)
-	public void testShouldNotReturnQuestionsIfNotAuthenticated() {
-		setAuthenticated(false, "nobody");
-		questionService.getSkillQuestions("12345678");
-	}
-
-	@Test(expected = NotFoundException.class)
-	public void testShouldFindQuestionsForNonExistantSession() {
-		setAuthenticated(true, "ptsr00");
-		questionService.getSkillQuestions("00000000");
-	}
-
-	@Test
-	public void testShouldFindQuestions() {
-		setAuthenticated(true, "ptsr00");
-		assertEquals(1, questionService.getSkillQuestionCount("12345678"));
-	}
-
-	@Test
-	public void testShouldMarkInterposedQuestionAsReadIfSessionCreator() throws Exception {
-		setAuthenticated(true, "ptsr00");
-		final InterposedQuestion theQ = new InterposedQuestion();
-		theQ.setRead(false);
-		theQ.set_id("the internal id");
-		theQ.setSessionId("12345678");
-		databaseDao.interposedQuestion = theQ;
-
-		questionService.readInterposedQuestion(theQ.get_id());
-
-		assertTrue(theQ.isRead());
-	}
-
-	@Test
-	public void testShouldNotMarkInterposedQuestionAsReadIfRegularUser() throws Exception {
-		setAuthenticated(true, "regular user");
-		final InterposedQuestion theQ = new InterposedQuestion();
-		theQ.setRead(false);
-		theQ.set_id("the internal id");
-		theQ.setSessionId("12345678");
-		theQ.setCreator("regular user");
-		databaseDao.interposedQuestion = theQ;
-
-		questionService.readInterposedQuestion(theQ.get_id());
-
-		assertFalse(theQ.isRead());
-	}
-
-	@Test(expected = AccessDeniedException.class)
-	public void testShouldSaveQuestion() throws Exception{
-		setAuthenticated(true, "regular user");
-		final Question question = new Question();
-		question.setSessionKeyword("12345678");
-		question.setQuestionVariant("freetext");
-		questionService.saveQuestion(question);
-	}
-
-	@Test(expected = AccessDeniedException.class)
-	public void testShouldNotDeleteQuestion() throws Exception{
-		setAuthenticated(true, "otheruser");
-		questionService.deleteQuestion("a1a2a3a4a5a6a7a8a9a");
-	}
-
-	@Test(expected = AccessDeniedException.class)
-	public void testShouldNotDeleteInterposedQuestion() throws Exception{
-		setAuthenticated(true, "otheruser");
-		questionService.deleteInterposedQuestion("a1a2a3a4a5a6a7a8a9a");
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java b/src/test/java/de/thm/arsnova/services/SessionServiceTest.java
deleted file mode 100644
index de86f5c5a0821da44c3e856db049749ec89b99b8..0000000000000000000000000000000000000000
--- a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.config.AppConfig;
-import de.thm.arsnova.config.TestAppConfig;
-import de.thm.arsnova.config.TestSecurityConfig;
-import de.thm.arsnova.dao.IDatabaseDao;
-import de.thm.arsnova.dao.StubDatabaseDao;
-import de.thm.arsnova.entities.Session;
-import de.thm.arsnova.exceptions.NotFoundException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.aop.framework.Advised;
-import org.springframework.aop.support.AopUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.test.context.web.WebAppConfiguration;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.*;
-
-@RunWith(SpringJUnit4ClassRunner.class)
-@WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
-@ActiveProfiles("test")
-public class SessionServiceTest {
-
-	@Autowired
-	private ISessionService sessionService;
-
-	@Autowired
-	private StubUserService userService;
-
-	@Autowired
-	private StubDatabaseDao databaseDao;
-
-	private void setAuthenticated(final boolean isAuthenticated, final String username) {
-		if (isAuthenticated) {
-			final List<GrantedAuthority> ga = new ArrayList<>();
-			final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, "secret", ga);
-			SecurityContextHolder.getContext().setAuthentication(token);
-			userService.setUserAuthenticated(isAuthenticated, username);
-		} else {
-			userService.setUserAuthenticated(isAuthenticated);
-		}
-	}
-
-	@Before
-	public void startup() {
-		SecurityContextHolder.clearContext();
-	}
-
-	@After
-	public void cleanup() {
-		databaseDao.cleanupTestData();
-		SecurityContextHolder.clearContext();
-		userService.setUserAuthenticated(false);
-	}
-
-	@Test
-	public void testShouldGenerateSessionKeyword() {
-		assertTrue(sessionService.generateKeyword().matches("^[0-9]{8}$"));
-	}
-
-	@Test(expected = NotFoundException.class)
-	public void testShouldNotFindNonExistantSession() {
-		setAuthenticated(true, "ptsr00");
-		sessionService.getSession("00000000");
-	}
-
-	@Test(expected = AuthenticationCredentialsNotFoundException.class)
-	public void testShouldNotReturnSessionIfUnauthorized() {
-		setAuthenticated(false, null);
-		sessionService.getSession("12345678");
-	}
-
-	@Test(expected = AuthenticationCredentialsNotFoundException.class)
-	public void testShouldNotSaveSessionIfUnauthorized() {
-		setAuthenticated(false, null);
-
-		final Session session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("11111111");
-		session.setName("TestSessionX");
-		session.setShortName("TSX");
-		sessionService.saveSession(session);
-
-		setAuthenticated(true, "ptsr00");
-
-		assertNull(sessionService.getSession("11111111"));
-	}
-
-	@Test
-	public void testShouldSaveSession() {
-		setAuthenticated(true, "ptsr00");
-
-		final Session session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("11111111");
-		session.setName("TestSessionX");
-		session.setShortName("TSX");
-		sessionService.saveSession(session);
-		assertNotNull(sessionService.getSession("11111111"));
-	}
-
-	@Test(expected = AccessDeniedException.class)
-	public void testShouldUpdateSession() {
-		setAuthenticated(true, "ptsr00");
-
-		final Session session = new Session();
-		session.setActive(true);
-		session.setCreator("ptsr00");
-		session.setKeyword("11111111");
-		session.setName("TestSessionX");
-		session.setShortName("TSX");
-		sessionService.saveSession(session);
-
-		setAuthenticated(true, "other");
-		sessionService.updateSession(session.getKeyword(), session);
-	}
-
-	@Test
-	@Ignore("Test fails on JDK 8 (ClassCastException)")
-	public void testShouldDeleteAllSessionData() {
-		/* FIXME: fails with ClassCastException on JDK 8 */
-		final IDatabaseDao tempDatabase = (IDatabaseDao) ReflectionTestUtils.getField(getTargetObject(sessionService), "databaseDao");
-		try {
-			setAuthenticated(true, "ptsr00");
-
-			final Session session = new Session();
-			session.setKeyword("12345678");
-			session.setCreator(userService.getCurrentUser().getUsername());
-
-			final IDatabaseDao mockDatabase = mock(IDatabaseDao.class);
-			when(mockDatabase.getSessionFromKeyword(anyString())).thenReturn(session);
-			ReflectionTestUtils.setField(getTargetObject(sessionService), "databaseDao", mockDatabase);
-
-			sessionService.deleteSession(session.getKeyword());
-
-			verify(mockDatabase).deleteSession(session);
-		} finally {
-			ReflectionTestUtils.setField(getTargetObject(sessionService), "databaseDao", tempDatabase);
-		}
-	}
-
-	@Test(expected = AuthenticationCredentialsNotFoundException.class)
-	public void testShouldNotDeleteSessionIfUnauthorized() {
-		setAuthenticated(false, "nobody");
-		sessionService.deleteSession("12345678");
-	}
-
-	@Test(expected = AccessDeniedException.class)
-	public void testShouldNotDeleteSessionIfNotOwner() {
-		setAuthenticated(true, "anybody");
-		sessionService.deleteSession("12345678");
-	}
-
-	@Test
-	public void testShouldCompareSessionByName() {
-		final Session sessionA = new Session();
-		sessionA.setName("TestSessionA");
-		sessionA.setShortName("TSA");
-
-		final Session sessionB = new Session();
-		sessionB.setName("TestSessionB");
-		sessionB.setShortName("TSB");
-
-		final Comparator<Session> comp = new SessionService.SessionNameComparator();
-		assertTrue(comp.compare(sessionA, sessionB) < 0);
-	}
-
-	@Test
-	public void testShouldCompareSessionByShortName() {
-		final Session sessionA = new Session();
-		sessionA.setName("TestSessionA");
-		sessionA.setShortName("TSA");
-
-		final Session sessionB = new Session();
-		sessionB.setName("TestSessionB");
-		sessionB.setShortName("TSB");
-
-		final Comparator<Session> comp = new SessionService.SessionShortNameComparator();
-		assertTrue(comp.compare(sessionA, sessionB) < 0);
-	}
-
-	@SuppressWarnings("unchecked")
-	public static <T> T getTargetObject(final Object proxy) {
-		if (AopUtils.isJdkDynamicProxy(proxy)) {
-			try {
-				return (T) getTargetObject(((Advised) proxy).getTargetSource().getTarget());
-			} catch (final Exception e) {
-				throw new RuntimeException("Failed to unproxy target.", e);
-			}
-		}
-		return (T) proxy;
-	}
-}
diff --git a/src/test/java/de/thm/arsnova/services/UserServiceTest.java b/src/test/java/de/thm/arsnova/services/UserServiceTest.java
index 78eb90c346a65bbb0be58dbbde44f31bca816e4a..1d2cb8ec70e5105001bf4336432d6c5ec0ee18bb 100644
--- a/src/test/java/de/thm/arsnova/services/UserServiceTest.java
+++ b/src/test/java/de/thm/arsnova/services/UserServiceTest.java
@@ -19,6 +19,7 @@ package de.thm.arsnova.services;
 
 import de.thm.arsnova.config.AppConfig;
 import de.thm.arsnova.config.TestAppConfig;
+import de.thm.arsnova.config.TestPersistanceConfig;
 import de.thm.arsnova.config.TestSecurityConfig;
 import de.thm.arsnova.entities.User;
 import org.jasig.cas.client.authentication.AttributePrincipalImpl;
@@ -52,7 +53,7 @@ import static org.junit.Assert.assertEquals;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @WebAppConfiguration
-@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
+@ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestPersistanceConfig.class, TestSecurityConfig.class})
 @ActiveProfiles("test")
 public class UserServiceTest {