diff --git a/src/main/java/de/thm/arsnova/controller/AnswerController.java b/src/main/java/de/thm/arsnova/controller/AnswerController.java
new file mode 100644
index 0000000000000000000000000000000000000000..8eeb8e736c996e99fb3539308b76a9714c986883
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/controller/AnswerController.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2018 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.entities.Answer;
+import de.thm.arsnova.services.AnswerService;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/answer")
+public class AnswerController extends AbstractEntityController<Answer> {
+	private AnswerService answerService;
+
+	public AnswerController(final AnswerService answerService) {
+		super(answerService);
+		this.answerService = answerService;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
index be4e5c7b5d8caaa533805517fe4decabcddeaba7..1467f0755e6807cd6fbdb4365afac210a3bef680 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
@@ -30,6 +30,7 @@ import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.NotImplementedException;
+import de.thm.arsnova.services.AnswerService;
 import de.thm.arsnova.services.ContentService;
 import de.thm.arsnova.services.RoomService;
 import de.thm.arsnova.services.TimerService;
@@ -69,6 +70,9 @@ public class ContentController extends PaginationController {
 	@Autowired
 	private ContentService contentService;
 
+	@Autowired
+	private AnswerService answerService;
+
 	@Autowired
 	private RoomService roomService;
 
@@ -444,7 +448,7 @@ public class ContentController extends PaginationController {
 			final HttpServletResponse response
 			) {
 		final de.thm.arsnova.entities.Content content = contentService.get(contentId);
-		final de.thm.arsnova.entities.Answer answer = contentService.getMyAnswer(contentId);
+		final de.thm.arsnova.entities.Answer answer = answerService.getMyAnswer(contentId);
 		if (answer == null) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
 			return null;
@@ -481,21 +485,21 @@ public class ContentController extends PaginationController {
 			final HttpServletResponse response) {
 		final de.thm.arsnova.entities.Content content = contentService.get(contentId);
 		if (content instanceof ChoiceQuestionContent) {
-			return toV2Migrator.migrate(contentService.getAllStatistics(contentId),
+			return toV2Migrator.migrate(answerService.getAllStatistics(contentId),
 					(ChoiceQuestionContent) content, content.getState().getRound());
 		} else {
 			List<de.thm.arsnova.entities.TextAnswer> answers;
 			if (allAnswers) {
-				answers = contentService.getAllTextAnswers(contentId, -1, -1);
+				answers = answerService.getAllTextAnswers(contentId, -1, -1);
 			} else if (null == piRound) {
-				answers = contentService.getTextAnswers(contentId, offset, limit);
+				answers = answerService.getTextAnswers(contentId, offset, limit);
 			} else {
 				if (piRound < 1 || piRound > 2) {
 					response.setStatus(HttpStatus.BAD_REQUEST.value());
 
 					return null;
 				}
-				answers = contentService.getTextAnswers(contentId, piRound, offset, limit);
+				answers = answerService.getTextAnswers(contentId, piRound, offset, limit);
 			}
 			if (answers == null) {
 				return new ArrayList<>();
@@ -517,9 +521,9 @@ public class ContentController extends PaginationController {
 		final de.thm.arsnova.entities.Answer answerV3 = fromV2Migrator.migrate(answer, contentV2);
 
 		if (answerV3 instanceof TextAnswer) {
-			return toV2Migrator.migrate((TextAnswer) contentService.saveAnswer(contentId, answerV3));
+			return toV2Migrator.migrate((TextAnswer) answerService.saveAnswer(contentId, answerV3));
 		} else {
-			return  toV2Migrator.migrate((ChoiceAnswer) contentService.saveAnswer(contentId, answerV3), (ChoiceQuestionContent) content);
+			return  toV2Migrator.migrate((ChoiceAnswer) answerService.saveAnswer(contentId, answerV3), (ChoiceQuestionContent) content);
 		}
 	}
 
@@ -537,9 +541,9 @@ public class ContentController extends PaginationController {
 		final de.thm.arsnova.entities.Answer answerV3 = fromV2Migrator.migrate(answer, contentV2);
 
 		if (answerV3 instanceof TextAnswer) {
-			return toV2Migrator.migrate((TextAnswer) contentService.updateAnswer(answerV3));
+			return toV2Migrator.migrate((TextAnswer) answerService.updateAnswer(answerV3));
 		} else {
-			return  toV2Migrator.migrate((ChoiceAnswer) contentService.updateAnswer(answerV3), (ChoiceQuestionContent) content);
+			return  toV2Migrator.migrate((ChoiceAnswer) answerService.updateAnswer(answerV3), (ChoiceQuestionContent) content);
 		}
 	}
 
@@ -563,7 +567,7 @@ public class ContentController extends PaginationController {
 			@PathVariable final String answerId,
 			final HttpServletResponse response
 			) {
-		contentService.deleteAnswer(contentId, answerId);
+		answerService.deleteAnswer(contentId, answerId);
 	}
 
 	@ApiOperation(value = "Delete answers from a content, identified by content ID",
@@ -573,7 +577,7 @@ public class ContentController extends PaginationController {
 			@PathVariable final String contentId,
 			final HttpServletResponse response
 			) {
-		contentService.deleteAnswers(contentId);
+		answerService.deleteAnswers(contentId);
 	}
 
 	@ApiOperation(value = "Delete all answers and contents from a room, identified by room short ID",
@@ -614,7 +618,7 @@ public class ContentController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/{contentId}/answercount", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
 	public String getAnswerCount(@PathVariable final String contentId) {
-		return String.valueOf(contentService.countAnswersByContentIdAndRound(contentId));
+		return String.valueOf(answerService.countAnswersByContentIdAndRound(contentId));
 	}
 
 	@ApiOperation(value = "Get the amount of answers for a content, identified by the content ID",
@@ -622,8 +626,8 @@ public class ContentController extends PaginationController {
 	@RequestMapping(value = "/{contentId}/allroundanswercount", method = RequestMethod.GET)
 	public List<Integer> getAllAnswerCount(@PathVariable final String contentId) {
 		return Arrays.asList(
-			contentService.countAnswersByContentIdAndRound(contentId, 1),
-			contentService.countAnswersByContentIdAndRound(contentId, 2)
+				answerService.countAnswersByContentIdAndRound(contentId, 1),
+				answerService.countAnswersByContentIdAndRound(contentId, 2)
 		);
 	}
 
@@ -631,7 +635,7 @@ public class ContentController extends PaginationController {
 			nickname = "getTotalAnswerCountByContent")
 	@RequestMapping(value = "/{contentId}/totalanswercount", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
 	public String getTotalAnswerCountByContent(@PathVariable final String contentId) {
-		return String.valueOf(contentService.countTotalAnswersByContentId(contentId));
+		return String.valueOf(answerService.countTotalAnswersByContentId(contentId));
 	}
 
 	@ApiOperation(value = "Get the amount of answers and abstention answers by a content, identified by the content ID",
@@ -639,8 +643,8 @@ public class ContentController extends PaginationController {
 	@RequestMapping(value = "/{contentId}/answerandabstentioncount", method = RequestMethod.GET)
 	public List<Integer> getAnswerAndAbstentionCount(@PathVariable final String contentId) {
 		return Arrays.asList(
-			contentService.countAnswersByContentIdAndRound(contentId),
-			contentService.countTotalAbstentionsByContentId(contentId)
+				answerService.countAnswersByContentIdAndRound(contentId),
+				answerService.countTotalAbstentionsByContentId(contentId)
 		);
 	}
 
@@ -649,7 +653,7 @@ public class ContentController extends PaginationController {
 	@RequestMapping(value = "/{contentId}/freetextanswer/", method = RequestMethod.GET)
 	@Pagination
 	public List<Answer> getFreetextAnswers(@PathVariable final String contentId) {
-		return contentService.getTextAnswersByContentId(contentId, offset, limit).stream()
+		return answerService.getTextAnswersByContentId(contentId, offset, limit).stream()
 				.map(toV2Migrator::migrate).collect(Collectors.toList());
 	}
 
@@ -659,7 +663,7 @@ public class ContentController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/myanswers", method = RequestMethod.GET)
 	public List<Answer> getMyAnswers(@RequestParam(value = "sessionkey") final String roomShortId) throws OperationNotSupportedException {
-		return contentService.getMyAnswersByRoomId(roomService.getIdByShortId(roomShortId)).stream()
+		return answerService.getMyAnswersByRoomId(roomService.getIdByShortId(roomShortId)).stream()
 				.map(a -> {
 					if (a instanceof ChoiceAnswer) {
 						return toV2Migrator.migrate(
@@ -685,11 +689,11 @@ public class ContentController extends PaginationController {
 		/* FIXME: Content variant is ignored for now */
 		lectureContentsOnly = preparationContentsOnly = false;
 		if (lectureContentsOnly) {
-			count = contentService.countLectureContentAnswers(roomId);
+			count = answerService.countLectureContentAnswers(roomId);
 		} else if (preparationContentsOnly) {
-			count = contentService.countPreparationContentAnswers(roomId);
+			count = answerService.countPreparationContentAnswers(roomId);
 		} else {
-			count = contentService.countTotalAnswersByRoomId(roomId);
+			count = answerService.countTotalAnswersByRoomId(roomId);
 		}
 
 		return String.valueOf(count);
diff --git a/src/main/java/de/thm/arsnova/services/AnswerService.java b/src/main/java/de/thm/arsnova/services/AnswerService.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea1769e10953f0c170b927915b340dfaef4b88c4
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/AnswerService.java
@@ -0,0 +1,59 @@
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.AnswerStatistics;
+import de.thm.arsnova.entities.TextAnswer;
+import de.thm.arsnova.entities.migration.v2.ClientAuthentication;
+
+import java.util.List;
+import java.util.Map;
+
+public interface AnswerService extends EntityService<Answer> {
+	Answer getMyAnswer(String contentId);
+
+	void getFreetextAnswerAndMarkRead(String answerId, ClientAuthentication user);
+
+	AnswerStatistics getStatistics(String contentId, int piRound);
+
+	AnswerStatistics getStatistics(String contentId);
+
+	AnswerStatistics getAllStatistics(String contentId);
+
+	List<TextAnswer> getTextAnswers(String contentId, int piRound, int offset, int limit);
+
+	List<TextAnswer> getTextAnswers(String contentId, int offset, int limit);
+
+	List<TextAnswer> getAllTextAnswers(String contentId, int offset, int limit);
+
+	int countAnswersByContentIdAndRound(String contentId);
+
+	int countAnswersByContentIdAndRound(String contentId, int piRound);
+
+	List<TextAnswer> getTextAnswersByContentId(String contentId, int offset, int limit);
+
+	List<Answer> getMyAnswersByRoomId(String roomId);
+
+	int countTotalAnswersByRoomId(String roomId);
+
+	int countTotalAnswersByContentId(String contentId);
+
+	void deleteAnswers(String contentId);
+
+	Answer saveAnswer(String contentId, Answer answer);
+
+	Answer updateAnswer(Answer answer);
+
+	void deleteAnswer(String contentId, String answerId);
+
+	Map<String, Object> countAnswersAndAbstentionsInternal(String contentId);
+
+	int countLectureContentAnswers(String roomId);
+
+	int countLectureQuestionAnswersInternal(String roomId);
+
+	int countPreparationContentAnswers(String roomId);
+
+	int countPreparationQuestionAnswersInternal(String roomId);
+
+	int countTotalAbstentionsByContentId(String contentId);
+}
diff --git a/src/main/java/de/thm/arsnova/services/AnswerServiceImpl.java b/src/main/java/de/thm/arsnova/services/AnswerServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..00eae4abcce98786e731c1a7385b1e56e1c7d62a
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/AnswerServiceImpl.java
@@ -0,0 +1,455 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2018 The ARSnova Team and Contributors
+ *
+ * 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.entities.Answer;
+import de.thm.arsnova.entities.AnswerStatistics;
+import de.thm.arsnova.entities.ChoiceQuestionContent;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Room;
+import de.thm.arsnova.entities.TextAnswer;
+import de.thm.arsnova.entities.migration.v2.ClientAuthentication;
+import de.thm.arsnova.entities.transport.AnswerQueueElement;
+import de.thm.arsnova.events.DeleteAnswerEvent;
+import de.thm.arsnova.events.NewAnswerEvent;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.AnswerRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.RoomRepository;
+import org.ektorp.DbAccessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Performs all answer related operations.
+ */
+@Service
+public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer>
+		implements AnswerService, ApplicationEventPublisherAware {
+	private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
+
+	private final Queue<AnswerQueueElement> answerQueue = new ConcurrentLinkedQueue<>();
+
+	private ApplicationEventPublisher publisher;
+
+	private RoomRepository roomRepository;
+	private ContentRepository contentRepository;
+	private AnswerRepository answerRepository;
+	private ContentService contentService;
+	private UserService userService;
+
+	public AnswerServiceImpl(
+			AnswerRepository repository,
+			ContentRepository contentRepository,
+			RoomRepository roomRepository,
+			ContentService contentService,
+			UserService userService,
+			@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
+		super(Answer.class, repository, jackson2HttpMessageConverter.getObjectMapper());
+		this.answerRepository = repository;
+		this.contentRepository = contentRepository;
+		this.roomRepository = roomRepository;
+		this.contentService = contentService;
+		this.userService = userService;
+	}
+
+	@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 {
+			answerRepository.save(answerList);
+
+			// Send NewAnswerEvents ...
+			for (AnswerQueueElement e : elements) {
+				this.publisher.publishEvent(new NewAnswerEvent(this, e.getRoom(), e.getAnswer(), e.getUser(), e.getQuestion()));
+			}
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk save answers from queue.", e);
+		}
+	}
+
+	@Override
+	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
+	public void deleteAnswers(final String contentId) {
+		final Content content = contentRepository.findOne(contentId);
+		content.resetState();
+		/* FIXME: cancel timer */
+		contentService.update(content);
+		answerRepository.deleteByContentId(content.getId());
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Answer getMyAnswer(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		return answerRepository.findByContentIdUserPiRound(contentId, Answer.class, userService.getCurrentUser(), content.getState().getRound());
+	}
+
+	@Override
+	public void getFreetextAnswerAndMarkRead(final String answerId, final ClientAuthentication user) {
+		final Answer answer = answerRepository.findOne(answerId);
+		if (!(answer instanceof TextAnswer)) {
+			throw new NotFoundException();
+		}
+		final TextAnswer textAnswer = (TextAnswer) answer;
+		if (textAnswer.isRead()) {
+			return;
+		}
+		final Room room = roomRepository.findOne(textAnswer.getRoomId());
+		if (room.getOwnerId().equals(user.getId())) {
+			textAnswer.setRead(true);
+			answerRepository.save(textAnswer);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public AnswerStatistics getStatistics(final String contentId, final int round) {
+		final ChoiceQuestionContent content = (ChoiceQuestionContent) contentRepository.findOne(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		AnswerStatistics stats = answerRepository.findByContentIdRound(
+				content.getId(), round, content.getOptions().size());
+		/* Fill list with zeros to prevent IndexOutOfBoundsExceptions */
+		List<Integer> independentCounts = stats.getRoundStatistics().get(round - 1).getIndependentCounts();
+		while (independentCounts.size() < content.getOptions().size()) {
+			independentCounts.add(0);
+		}
+
+		return stats;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public AnswerStatistics getStatistics(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+
+		return getStatistics(content.getId(), content.getState().getRound());
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public AnswerStatistics getAllStatistics(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		AnswerStatistics stats = getStatistics(content.getId(), 1);
+		AnswerStatistics stats2 = getStatistics(content.getId(), 2);
+		stats.getRoundStatistics().add(stats2.getRoundStatistics().get(1));
+
+		return stats;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<TextAnswer> getTextAnswers(final String contentId, final int piRound, final int offset, final int limit) {
+		/* FIXME: round support not implemented */
+		final Content content = contentRepository.findOne(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+
+		return getTextAnswersByContentId(contentId, offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<TextAnswer> getTextAnswers(final String contentId, final int offset, final int limit) {
+		return getTextAnswers(contentId, 0, offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<TextAnswer> getAllTextAnswers(final String contentId, final int offset, final int limit) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+
+		return getTextAnswersByContentId(contentId, offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countAnswersByContentIdAndRound(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			return 0;
+		}
+
+		if (content.getFormat() == Content.Format.TEXT) {
+			return answerRepository.countByContentId(content.getId());
+		} else {
+			return answerRepository.countByContentIdRound(content.getId(), content.getState().getRound());
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countAnswersByContentIdAndRound(final String contentId, final int piRound) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.countByContentIdRound(content.getId(), piRound);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countTotalAbstentionsByContentId(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.countByContentId(contentId);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countTotalAnswersByContentId(final String contentId) {
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.countByContentId(content.getId());
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<TextAnswer> getTextAnswersByContentId(final String contentId, final int offset, final int limit) {
+		final List<TextAnswer> answers = answerRepository.findByContentId(contentId, TextAnswer.class, offset, limit);
+		if (answers == null) {
+			throw new NotFoundException();
+		}
+
+		return answers;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getMyAnswersByRoomId(final String roomId) {
+		// Load contents first because we are only interested in answers of the latest piRound.
+		final List<Content> contents = contentService.getByRoomId(roomId);
+		final Map<String, Content> contentIdToContent = new HashMap<>();
+		for (final Content content : contents) {
+			contentIdToContent.put(content.getId(), content);
+		}
+
+		/* filter answers by active piRound per content */
+		final List<Answer> answers = answerRepository.findByUserRoomId(userService.getCurrentUser(), roomId);
+		final List<Answer> filteredAnswers = new ArrayList<>();
+		for (final Answer answer : answers) {
+			final Content content = contentIdToContent.get(answer.getContentId());
+			if (content == null) {
+				// Content is not present. Most likely it has been locked by the
+				// Room's creator. Locked Questions do not appear in this list.
+				continue;
+			}
+
+			// discard all answers that aren't in the same piRound as the content
+			if (answer.getRound() == content.getState().getRound()) {
+				filteredAnswers.add(answer);
+			}
+		}
+
+		return filteredAnswers;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countTotalAnswersByRoomId(final String roomId) {
+		return answerRepository.countByRoomId(roomId);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	@CacheEvict(value = "answerlists", key = "#contentId")
+	public Answer saveAnswer(final String contentId, final Answer answer) {
+		final ClientAuthentication user = userService.getCurrentUser();
+		final Content content = contentService.get(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		final Room room = roomRepository.findOne(content.getRoomId());
+
+		answer.setCreatorId(user.getId());
+		answer.setContentId(content.getId());
+		answer.setRoomId(room.getId());
+
+		/* FIXME: migrate
+		answer.setQuestionValue(content.calculateValue(answer));
+		*/
+
+		if (content.getFormat() == Content.Format.TEXT) {
+			answer.setRound(0);
+			/* FIXME: migrate
+			imageUtils.generateThumbnailImage(answer);
+			if (content.isFixedAnswer() && content.getBody() != null) {
+				answer.setAnswerTextRaw(answer.getAnswerText());
+
+				if (content.isStrictMode()) {
+					content.checkTextStrictOptions(answer);
+				}
+				answer.setQuestionValue(content.evaluateCorrectAnswerFixedText(answer.getAnswerTextRaw()));
+				answer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(answer.getAnswerTextRaw()));
+			}
+			*/
+		} else {
+			answer.setRound(content.getState().getRound());
+		}
+
+		this.answerQueue.offer(new AnswerQueueElement(room, content, answer, user));
+
+		return answer;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	@CacheEvict(value = "answerlists", allEntries = true)
+	public Answer updateAnswer(final Answer answer) {
+		final ClientAuthentication user = userService.getCurrentUser();
+		final Answer realAnswer = this.getMyAnswer(answer.getContentId());
+		if (user == null || realAnswer == null || !user.getId().equals(realAnswer.getCreatorId())) {
+			throw new UnauthorizedException();
+		}
+
+		final Content content = contentService.get(answer.getContentId());
+		/* FIXME: migrate
+		if (content.getFormat() == Content.Format.TEXT) {
+			imageUtils.generateThumbnailImage(realAnswer);
+			content.checkTextStrictOptions(realAnswer);
+		}
+		*/
+		final Room room = roomRepository.findOne(content.getRoomId());
+		answer.setCreatorId(user.getId());
+		answer.setContentId(content.getId());
+		answer.setRoomId(room.getId());
+		answerRepository.save(realAnswer);
+		this.publisher.publishEvent(new NewAnswerEvent(this, room, answer, user, content));
+
+		return answer;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	@CacheEvict(value = "answerlists", allEntries = true)
+	public void deleteAnswer(final String contentId, final String answerId) {
+		final Content content = contentRepository.findOne(contentId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		final ClientAuthentication user = userService.getCurrentUser();
+		final Room room = roomRepository.findOne(content.getRoomId());
+		if (user == null || room == null || !room.getOwnerId().equals(user.getId())) {
+			throw new UnauthorizedException();
+		}
+		answerRepository.delete(answerId);
+
+		this.publisher.publishEvent(new DeleteAnswerEvent(this, room, content));
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public int countLectureQuestionAnswersInternal(final String roomId) {
+		return answerRepository.countByRoomIdOnlyLectureVariant(roomRepository.findOne(roomId).getId());
+	}
+
+	@Override
+	public Map<String, Object> countAnswersAndAbstentionsInternal(final String contentId) {
+		final Content content = contentService.get(contentId);
+		HashMap<String, Object> map = new HashMap<>();
+
+		if (content == null) {
+			return null;
+		}
+
+		map.put("_id", contentId);
+		map.put("answers", answerRepository.countByContentIdRound(content.getId(), content.getState().getRound()));
+		map.put("abstentions", answerRepository.countByContentId(contentId));
+
+		return map;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countLectureContentAnswers(final String roomId) {
+		return this.countLectureQuestionAnswersInternal(roomId);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countPreparationContentAnswers(final String roomId) {
+		return this.countPreparationQuestionAnswersInternal(roomId);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public int countPreparationQuestionAnswersInternal(final String roomId) {
+		return answerRepository.countByRoomIdOnlyPreparationVariant(roomRepository.findOne(roomId).getId());
+	}
+
+	@Override
+	public void setApplicationEventPublisher(final ApplicationEventPublisher applicationEventPublisher) {
+		this.publisher = applicationEventPublisher;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/ContentService.java b/src/main/java/de/thm/arsnova/services/ContentService.java
index 0d7ea8679c20ad56fd253f46c43fd664ea5abd87..aad4dfab8caebdaeb15e76740ee82389b9110da6 100644
--- a/src/main/java/de/thm/arsnova/services/ContentService.java
+++ b/src/main/java/de/thm/arsnova/services/ContentService.java
@@ -1,3 +1,20 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2018 The ARSnova Team and Contributors
+ *
+ * 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/>.
+ */
 /*
  * This file is part of ARSnova Backend.
  * Copyright (C) 2012-2018 The ARSnova Team and Contributors
@@ -17,14 +34,10 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.AnswerStatistics;
 import de.thm.arsnova.entities.Content;
-import de.thm.arsnova.entities.TextAnswer;
 import de.thm.arsnova.entities.migration.v2.ClientAuthentication;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * The functionality the question service should provide.
@@ -42,46 +55,10 @@ public interface ContentService extends EntityService<Content> {
 
 	List<String> getUnAnsweredContentIds(String roomId);
 
-	Answer getMyAnswer(String contentId);
-
-	void getFreetextAnswerAndMarkRead(String answerId, ClientAuthentication user);
-
-	AnswerStatistics getStatistics(String contentId, int piRound);
-
-	AnswerStatistics getStatistics(String contentId);
-
-	AnswerStatistics getAllStatistics(String contentId);
-
-	List<TextAnswer> getTextAnswers(String contentId, int piRound, int offset, int limit);
-
-	List<TextAnswer> getTextAnswers(String contentId, int offset, int limit);
-
-	List<TextAnswer> getAllTextAnswers(String contentId, int offset, int limit);
-
-	int countAnswersByContentIdAndRound(String contentId);
-
-	int countAnswersByContentIdAndRound(String contentId, int piRound);
-
-	List<TextAnswer> getTextAnswersByContentId(String contentId, int offset, int limit);
-
-	List<Answer> getMyAnswersByRoomId(String roomId);
-
-	int countTotalAnswersByRoomId(String roomId);
-
-	int countTotalAnswersByContentId(String contentId);
-
 	Content save(final String roomId, final Content content);
 
 	Content update(Content content);
 
-	void deleteAnswers(String contentId);
-
-	Answer saveAnswer(String contentId, Answer answer);
-
-	Answer updateAnswer(Answer answer);
-
-	void deleteAnswer(String contentId, String answerId);
-
 	List<Content> getLectureContents(String roomId);
 
 	List<Content> getFlashcards(String roomId);
@@ -94,16 +71,6 @@ public interface ContentService extends EntityService<Content> {
 
 	int countPreparationContents(String roomId);
 
-	Map<String, Object> countAnswersAndAbstentionsInternal(String contentId);
-
-	int countLectureContentAnswers(String roomId);
-
-	int countLectureQuestionAnswersInternal(String roomId);
-
-	int countPreparationContentAnswers(String roomId);
-
-	int countPreparationQuestionAnswersInternal(String roomId);
-
 	int countFlashcardsForUserInternal(String roomId);
 
 	void deleteAllContents(String roomId);
@@ -132,8 +99,6 @@ public interface ContentService extends EntityService<Content> {
 
 	void deleteAllLectureAnswers(String roomId);
 
-	int countTotalAbstentionsByContentId(String contentId);
-
 	void setVotingAdmission(String contentId, boolean disableVoting);
 
 	void setVotingAdmissions(String roomId, boolean disableVoting, List<Content> contents);
diff --git a/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java
index d1673162e85633a964dfa7c0820447bde76bc94a..046d2e8bda0d4e6367d1f4783c562bde8ca72b30 100644
--- a/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java
@@ -17,14 +17,9 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.AnswerStatistics;
-import de.thm.arsnova.entities.ChoiceQuestionContent;
 import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Room;
-import de.thm.arsnova.entities.TextAnswer;
 import de.thm.arsnova.entities.migration.v2.ClientAuthentication;
-import de.thm.arsnova.entities.transport.AnswerQueueElement;
 import de.thm.arsnova.events.*;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
@@ -32,7 +27,6 @@ import de.thm.arsnova.persistance.AnswerRepository;
 import de.thm.arsnova.persistance.ContentRepository;
 import de.thm.arsnova.persistance.LogEntryRepository;
 import de.thm.arsnova.persistance.RoomRepository;
-import org.ektorp.DbAccessException;
 import org.ektorp.DocumentNotFoundException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,22 +38,18 @@ import org.springframework.cache.annotation.Caching;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.stream.Collectors;
 
 /**
- * Performs all content and answer related operations.
+ * Performs all content related operations.
  */
 @Service
 public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implements ContentService, ApplicationEventPublisherAware {
@@ -77,8 +67,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
 
-	private final Queue<AnswerQueueElement> answerQueue = new ConcurrentLinkedQueue<>();
-
 	public ContentServiceImpl(
 			ContentRepository repository,
 			AnswerRepository answerRepository,
@@ -94,33 +82,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		this.userService = userService;
 	}
 
-	@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 {
-			answerRepository.save(answerList);
-
-			// Send NewAnswerEvents ...
-			for (AnswerQueueElement e : elements) {
-				this.publisher.publishEvent(new NewAnswerEvent(this, e.getRoom(), e.getAnswer(), e.getUser(), e.getQuestion()));
-			}
-		} catch (final DbAccessException e) {
-			logger.error("Could not bulk save answers from queue.", e);
-		}
-	}
-
 	@Cacheable("contents")
 	@Override
 	public Content get(final String id) {
@@ -412,16 +373,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		return room;
 	}
 
-	@Override
-	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
-	public void deleteAnswers(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
-		content.resetState();
-		/* FIXME: cancel timer */
-		update(content);
-		answerRepository.deleteByContentId(content.getId());
-	}
-
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<String> getUnAnsweredContentIds(final String roomId) {
@@ -437,287 +388,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		return user;
 	}
 
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Answer getMyAnswer(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		return answerRepository.findByContentIdUserPiRound(contentId, Answer.class, userService.getCurrentUser(), content.getState().getRound());
-	}
-
-	@Override
-	public void getFreetextAnswerAndMarkRead(final String answerId, final ClientAuthentication user) {
-		final Answer answer = answerRepository.findOne(answerId);
-		if (!(answer instanceof TextAnswer)) {
-			throw new NotFoundException();
-		}
-		final TextAnswer textAnswer = (TextAnswer) answer;
-		if (textAnswer.isRead()) {
-			return;
-		}
-		final Room room = roomRepository.findOne(textAnswer.getRoomId());
-		if (room.getOwnerId().equals(user.getId())) {
-			textAnswer.setRead(true);
-			answerRepository.save(textAnswer);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public AnswerStatistics getStatistics(final String contentId, final int round) {
-		final ChoiceQuestionContent content = (ChoiceQuestionContent) contentRepository.findOne(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		AnswerStatistics stats = answerRepository.findByContentIdRound(
-				content.getId(), round, content.getOptions().size());
-		/* Fill list with zeros to prevent IndexOutOfBoundsExceptions */
-		List<Integer> independentCounts = stats.getRoundStatistics().get(round - 1).getIndependentCounts();
-		while (independentCounts.size() < content.getOptions().size()) {
-			independentCounts.add(0);
-		}
-
-		return stats;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public AnswerStatistics getStatistics(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-
-		return getStatistics(content.getId(), content.getState().getRound());
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public AnswerStatistics getAllStatistics(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		AnswerStatistics stats = getStatistics(content.getId(), 1);
-		AnswerStatistics stats2 = getStatistics(content.getId(), 2);
-		stats.getRoundStatistics().add(stats2.getRoundStatistics().get(1));
-
-		return stats;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<TextAnswer> getTextAnswers(final String contentId, final int piRound, final int offset, final int limit) {
-		/* FIXME: round support not implemented */
-		final Content content = contentRepository.findOne(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-
-		return getTextAnswersByContentId(contentId, offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<TextAnswer> getTextAnswers(final String contentId, final int offset, final int limit) {
-		return getTextAnswers(contentId, 0, offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<TextAnswer> getAllTextAnswers(final String contentId, final int offset, final int limit) {
-		final Content content = get(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-
-		return getTextAnswersByContentId(contentId, offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countAnswersByContentIdAndRound(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			return 0;
-		}
-
-		if (content.getFormat() == Content.Format.TEXT) {
-			return answerRepository.countByContentId(content.getId());
-		} else {
-			return answerRepository.countByContentIdRound(content.getId(), content.getState().getRound());
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countAnswersByContentIdAndRound(final String contentId, final int piRound) {
-		final Content content = get(contentId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.countByContentIdRound(content.getId(), piRound);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countTotalAbstentionsByContentId(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.countByContentId(contentId);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countTotalAnswersByContentId(final String contentId) {
-		final Content content = get(contentId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.countByContentId(content.getId());
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<TextAnswer> getTextAnswersByContentId(final String contentId, final int offset, final int limit) {
-		final List<TextAnswer> answers = answerRepository.findByContentId(contentId, TextAnswer.class, offset, limit);
-		if (answers == null) {
-			throw new NotFoundException();
-		}
-
-		return answers;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getMyAnswersByRoomId(final String roomId) {
-		// Load contents first because we are only interested in answers of the latest piRound.
-		final List<Content> contents = getByRoomId(roomId);
-		final Map<String, Content> contentIdToContent = new HashMap<>();
-		for (final Content content : contents) {
-			contentIdToContent.put(content.getId(), content);
-		}
-
-		/* filter answers by active piRound per content */
-		final List<Answer> answers = answerRepository.findByUserRoomId(userService.getCurrentUser(), roomId);
-		final List<Answer> filteredAnswers = new ArrayList<>();
-		for (final Answer answer : answers) {
-			final Content content = contentIdToContent.get(answer.getContentId());
-			if (content == null) {
-				// Content is not present. Most likely it has been locked by the
-				// Room's creator. Locked Questions do not appear in this list.
-				continue;
-			}
-
-			// discard all answers that aren't in the same piRound as the content
-			if (answer.getRound() == content.getState().getRound()) {
-				filteredAnswers.add(answer);
-			}
-		}
-
-		return filteredAnswers;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countTotalAnswersByRoomId(final String roomId) {
-		return answerRepository.countByRoomId(roomId);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	@CacheEvict(value = "answerlists", key = "#contentId")
-	public Answer saveAnswer(final String contentId, final Answer answer) {
-		final ClientAuthentication user = getCurrentUser();
-		final Content content = get(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		final Room room = roomRepository.findOne(content.getRoomId());
-
-		answer.setCreatorId(user.getId());
-		answer.setContentId(content.getId());
-		answer.setRoomId(room.getId());
-
-		/* FIXME: migrate
-		answer.setQuestionValue(content.calculateValue(answer));
-		*/
-
-		if (content.getFormat() == Content.Format.TEXT) {
-			answer.setRound(0);
-			/* FIXME: migrate
-			imageUtils.generateThumbnailImage(answer);
-			if (content.isFixedAnswer() && content.getBody() != null) {
-				answer.setAnswerTextRaw(answer.getAnswerText());
-
-				if (content.isStrictMode()) {
-					content.checkTextStrictOptions(answer);
-				}
-				answer.setQuestionValue(content.evaluateCorrectAnswerFixedText(answer.getAnswerTextRaw()));
-				answer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(answer.getAnswerTextRaw()));
-			}
-			*/
-		} else {
-			answer.setRound(content.getState().getRound());
-		}
-
-		this.answerQueue.offer(new AnswerQueueElement(room, content, answer, user));
-
-		return answer;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	@CacheEvict(value = "answerlists", allEntries = true)
-	public Answer updateAnswer(final Answer answer) {
-		final ClientAuthentication user = userService.getCurrentUser();
-		final Answer realAnswer = this.getMyAnswer(answer.getContentId());
-		if (user == null || realAnswer == null || !user.getId().equals(realAnswer.getCreatorId())) {
-			throw new UnauthorizedException();
-		}
-
-		final Content content = get(answer.getContentId());
-		/* FIXME: migrate
-		if (content.getFormat() == Content.Format.TEXT) {
-			imageUtils.generateThumbnailImage(realAnswer);
-			content.checkTextStrictOptions(realAnswer);
-		}
-		*/
-		final Room room = roomRepository.findOne(content.getRoomId());
-		answer.setCreatorId(user.getId());
-		answer.setContentId(content.getId());
-		answer.setRoomId(room.getId());
-		answerRepository.save(realAnswer);
-		this.publisher.publishEvent(new NewAnswerEvent(this, room, answer, user, content));
-
-		return answer;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	@CacheEvict(value = "answerlists", allEntries = true)
-	public void deleteAnswer(final String contentId, final String answerId) {
-		final Content content = contentRepository.findOne(contentId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		final ClientAuthentication user = userService.getCurrentUser();
-		final Room room = roomRepository.findOne(content.getRoomId());
-		if (user == null || room == null || !room.getOwnerId().equals(user.getId())) {
-			throw new UnauthorizedException();
-		}
-		answerRepository.delete(answerId);
-
-		this.publisher.publishEvent(new DeleteAnswerEvent(this, room, content));
-	}
-
 	/* FIXME: caching */
 	@Override
 	@PreAuthorize("isAuthenticated()")
@@ -778,52 +448,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		return contentRepository.countPreparationVariantByRoomId(roomRepository.findOne(roomId).getId());
 	}
 
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countLectureContentAnswers(final String roomId) {
-		return this.countLectureQuestionAnswersInternal(roomId);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public int countLectureQuestionAnswersInternal(final String roomId) {
-		return answerRepository.countByRoomIdOnlyLectureVariant(roomRepository.findOne(roomId).getId());
-	}
-
-	@Override
-	public Map<String, Object> countAnswersAndAbstentionsInternal(final String contentId) {
-		final Content content = get(contentId);
-		HashMap<String, Object> map = new HashMap<>();
-
-		if (content == null) {
-			return null;
-		}
-
-		map.put("_id", contentId);
-		map.put("answers", answerRepository.countByContentIdRound(content.getId(), content.getState().getRound()));
-		map.put("abstentions", answerRepository.countByContentId(contentId));
-
-		return map;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countPreparationContentAnswers(final String roomId) {
-		return this.countPreparationQuestionAnswersInternal(roomId);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public int countPreparationQuestionAnswersInternal(final String roomId) {
-		return answerRepository.countByRoomIdOnlyPreparationVariant(roomRepository.findOne(roomId).getId());
-	}
-
 	/*
 	 * The "internal" suffix means it is called by internal services that have no authentication!
 	 * TODO: Find a better way of doing this...
@@ -896,6 +520,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		this.publisher.publishEvent(event);
 	}
 
+	/* TODO: Split and move answer part to AnswerService */
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	@CacheEvict(value = "answerlists", allEntries = true)
@@ -914,6 +539,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		this.publisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, room));
 	}
 
+	/* TODO: Split and move answer part to AnswerService */
 	/* TODO: Only evict cache entry for the answer's content. This requires some refactoring. */
 	@Override
 	@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
@@ -929,6 +555,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		this.publisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, room));
 	}
 
+	/* TODO: Split and move answer part to AnswerService */
 	/* TODO: Only evict cache entry for the answer's content. This requires some refactoring. */
 	@Override
 	@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
diff --git a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
index 456673b334e8c6ab705f8b970aa0460cbf073dfb..8ade69539251772a94af708a31329b1493d4cedb 100644
--- a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
@@ -36,6 +36,7 @@ import de.thm.arsnova.events.*;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.services.AnswerService;
 import de.thm.arsnova.services.CommentService;
 import de.thm.arsnova.services.FeedbackService;
 import de.thm.arsnova.services.ContentService;
@@ -81,6 +82,9 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova
 	@Autowired
 	private ContentService contentService;
 
+	@Autowired
+	private AnswerService answerService;
+
 	@Autowired
 	private CommentService commentService;
 
@@ -216,7 +220,7 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova
 			public void onData(SocketIOClient client, String answerId, AckRequest ackRequest) {
 				final ClientAuthentication user = userService.getUserToSocketId(client.getSessionId());
 				try {
-					contentService.getFreetextAnswerAndMarkRead(answerId, user);
+					answerService.getFreetextAnswerAndMarkRead(answerId, user);
 				} catch (NotFoundException | UnauthorizedException e) {
 					logger.error("Marking answer {} as read failed for user {} with exception {}", answerId, user, e.getMessage());
 				}
@@ -378,8 +382,8 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova
 		client.sendEvent("unansweredLecturerQuestions", contentService.getUnAnsweredLectureContentIds(roomId, user));
 		client.sendEvent("unansweredPreparationQuestions", contentService.getUnAnsweredPreparationContentIds(roomId, user));
 		/* FIXME: Content variant is ignored for now */
-		client.sendEvent("countLectureQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
-		client.sendEvent("countPreparationQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
+		client.sendEvent("countLectureQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
+		client.sendEvent("countPreparationQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
 		client.sendEvent("activeUserCountData", roomService.activeUsers(roomId));
 //		client.sendEvent("learningProgressOptions", room.getLearningProgressOptions());
 		final de.thm.arsnova.entities.Feedback fb = feedbackService.getByRoomId(roomId);
@@ -522,10 +526,10 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova
 	public void visit(NewAnswerEvent event) {
 		final String roomId = event.getRoom().getId();
 		this.reportAnswersToContentAvailable(event.getRoom(), new Content(event.getContent()));
-		broadcastInRoom(roomId, "countQuestionAnswersByQuestionId", contentService.countAnswersAndAbstentionsInternal(event.getContent().getId()));
+		broadcastInRoom(roomId, "countQuestionAnswersByQuestionId", answerService.countAnswersAndAbstentionsInternal(event.getContent().getId()));
 		/* FIXME: Content variant is ignored for now */
-		broadcastInRoom(roomId, "countLectureQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
-		broadcastInRoom(roomId, "countPreparationQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
+		broadcastInRoom(roomId, "countLectureQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
+		broadcastInRoom(roomId, "countPreparationQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
 
 		// Update the unanswered count for the content variant that was answered.
 		final de.thm.arsnova.entities.Content content = event.getContent();
@@ -544,8 +548,8 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova
 		this.reportAnswersToContentAvailable(event.getRoom(), new Content(event.getQuestion()));
 		// We do not know which user's answer was deleted, so we can't update his 'unanswered' list of questions...
 		/* FIXME: Content variant is ignored for now */
-		broadcastInRoom(roomId, "countLectureQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
-		broadcastInRoom(roomId, "countPreparationQuestionAnswers", contentService.countTotalAnswersByRoomId(roomId));
+		broadcastInRoom(roomId, "countLectureQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
+		broadcastInRoom(roomId, "countPreparationQuestionAnswers", answerService.countTotalAnswersByRoomId(roomId));
 	}
 
 	@Async