diff --git a/src/main/java/de/thm/arsnova/controller/v2/CommentController.java b/src/main/java/de/thm/arsnova/controller/v2/CommentController.java
index dab81e01c66ac7c6fe96c0cd3319224892597779..83811330c90e535f838e0e9d9aaed2b6991b624d 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/CommentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/CommentController.java
@@ -126,6 +126,6 @@ public class CommentController extends PaginationController {
 			nickname = "deleteComment")
 	@RequestMapping(value = "/{commentId}", method = RequestMethod.DELETE)
 	public void deleteComment(@ApiParam(value = "ID of the comment that needs to be deleted", required = true) @PathVariable final String commentId) {
-		commentService.delete(commentId);
+		commentService.delete(commentService.get(commentId));
 	}
 }
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 e097c27a0b0d01e9360269dd8bca8e10f2e96681..cc1a20422db636341d4fe152722291151a183262 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
@@ -53,6 +53,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.naming.OperationNotSupportedException;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -250,7 +251,7 @@ public class ContentController extends PaginationController {
 			@RequestParam(required = false) final Boolean publish,
 			@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) boolean lectureContentsOnly,
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) boolean preparationContentsOnly
-			) {
+			) throws IOException {
 		String roomId = roomService.getIdByShortId(roomShortId);
 		boolean p = publish == null || publish;
 		Iterable<de.thm.arsnova.model.Content> contents;
@@ -555,7 +556,7 @@ public class ContentController extends PaginationController {
 			@PathVariable final String answerId,
 			final HttpServletResponse response
 			) {
-		answerService.deleteAnswer(contentId, answerId);
+		answerService.delete(answerService.get(answerId));
 	}
 
 	@ApiOperation(value = "Delete answers from a content, identified by content ID",
diff --git a/src/main/java/de/thm/arsnova/controller/v2/MotdController.java b/src/main/java/de/thm/arsnova/controller/v2/MotdController.java
index 812f9ab18d95fa03b4171c5449ede78204e45267..7e65c4400a55610e6aa4acdb8dbb5962465ad796 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/MotdController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/MotdController.java
@@ -148,11 +148,7 @@ public class MotdController extends AbstractController {
 	@RequestMapping(value = "/{motdId}", method = RequestMethod.DELETE)
 	public void deleteMotd(@ApiParam(value = "Motd-key from the message that shall be deleted", required = true) @PathVariable final String motdId) {
 		de.thm.arsnova.model.Motd motd = motdService.get(motdId);
-		if (motd.getAudience() == de.thm.arsnova.model.Motd.Audience.ROOM) {
-			motdService.deleteByRoomId(motd.getRoomId(), motd);
-		} else {
-			motdService.delete(motd);
-		}
+		motdService.delete(motd);
 	}
 
 	@RequestMapping(value = "/userlist", method =  RequestMethod.GET)
diff --git a/src/main/java/de/thm/arsnova/controller/v2/RoomController.java b/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
index baef51c6f0e9d3b30b0c8cc427dee350291183c4..981d13b73791a773cfa1b4821bf096ac7e88bbd1 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
@@ -51,6 +51,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -307,7 +308,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "wether statistics shall be exported", required = true) @RequestParam(value = "withAnswerStatistics", defaultValue = "false") final Boolean withAnswerStatistics,
 			@ApiParam(value = "wether comments shall be exported", required = true) @RequestParam(value = "withFeedbackQuestions", defaultValue = "false") final Boolean withFeedbackQuestions,
 			final HttpServletResponse response
-		) {
+		) throws IOException {
 		List<ImportExportContainer> rooms = new ArrayList<>();
 		ImportExportContainer temp;
 		for (String shortId : shortIds) {
@@ -327,7 +328,7 @@ public class RoomController extends PaginationController {
 	public Room copyToPublicPool(
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "public pool attributes for Room", required = true) @RequestBody final ImportExportContainer.PublicPool publicPool
-			) {
+			) throws IOException {
 		String id = roomService.getIdByShortId(shortId);
 		roomService.setActive(id, false);
 		de.thm.arsnova.model.Room roomInfo = roomService.copyRoomToPublicPool(shortId, publicPool);
@@ -347,7 +348,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "lock", required = true) @RequestParam(required = false) final Boolean lock,
 			final HttpServletResponse response
-			) {
+			) throws IOException {
 		if (lock != null) {
 			return toV2Migrator.migrate(roomService.setActive(roomService.getIdByShortId(shortId), lock));
 		}
@@ -412,7 +413,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "lock", required = true) @RequestParam(required = true) final Boolean lock,
 			final HttpServletResponse response
-			) {
+			) throws IOException {
 		return String.valueOf(roomService.lockFeedbackInput(roomService.getIdByShortId(shortId), lock));
 	}
 
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java
deleted file mode 100644
index 0ad169a83f106857f86abfcde7e31bceed8600ab..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever all answers of all lecture questions of a session are deleted.
- */
-public class DeleteAllLectureAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllLectureAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java
deleted file mode 100644
index 49a8d9342797071f7ebd37b6c75afcdcb9f47f78..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever all answers of all preparation questions of a session are deleted.
- */
-public class DeleteAllPreparationAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllPreparationAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java
deleted file mode 100644
index aa671783fd1f776fa34607ad8827a497102149b7..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever all answers of all questions of a session are deleted.
- */
-public class DeleteAllQuestionsAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllQuestionsAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java
deleted file mode 100644
index 2bf5bac225a909d0076af155c12c37a03123e581..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever all questions of a session are deleted. Note that this implies that all answers are deleted as well,
- * even though the specific answer events are not fired.
- */
-public class DeleteAllQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllQuestionsEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java b/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java
deleted file mode 100644
index 790eaea277ef8862e588493afcf92f45d842c5ac..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever a new session is created.
- */
-public class FeatureChangeEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public FeatureChangeEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java b/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java
deleted file mode 100644
index 6c50fc4b8cad526d36ed7f5c30b68390515c7a2b..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever voting on a question is disabled.
- */
-public class LockFeedbackEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public LockFeedbackEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java b/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java
deleted file mode 100644
index 58d5b6fbf60e9fc0dd3d53591e1f36c2bc6c4f4a..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a content is disabled, i.e., it is hidden from students.
- */
-public class LockQuestionEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public LockQuestionEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public Content getQuestion() {
-		return this.content;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java
deleted file mode 100644
index 869d0c1a0071a765140c5b277ecbb0e8947c5cbb..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever a set of contents are disabled, i.e., they are hidden from students.
- */
-public class LockQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public LockQuestionsEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockVoteEvent.java b/src/main/java/de/thm/arsnova/event/LockVoteEvent.java
deleted file mode 100644
index bf3ba29733e2fde71e0da5b6b701ef34f5847721..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/LockVoteEvent.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever voting on a content is disabled.
- */
-public class LockVoteEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public LockVoteEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public String getQuestionId() {
-		return this.content.getId();
-	}
-
-	public String getGroup() {
-		/* FIXME: Event does not support content with multiple groups */
-		return content.getGroups().toArray(new String[1])[0];
-	}
-
-	public Boolean getVotingDisabled() {
-		/* FIXME: migrate */
-		return false;
-		//return this.content.isVotingDisabled();
-	}
-
-	public Map<String, Object> getVotingAdmission() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("variant", getGroup());
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockVotesEvent.java b/src/main/java/de/thm/arsnova/event/LockVotesEvent.java
deleted file mode 100644
index 7595c19a2ebc14b46bdb4429fd7e011548e9dc04..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/LockVotesEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever voting of multiple contents is disabled.
- */
-public class LockVotesEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public LockVotesEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java
deleted file mode 100644
index 73fadca6db1e3a9379dea4ebc691cbb59097d01b..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a peer instruction round is canceled.
- */
-public class PiRoundCancelEvent extends PiRoundEndEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public PiRoundCancelEvent(Object source, String roomId, Content content) {
-		super(source, roomId, content);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java
deleted file mode 100644
index c12cc8782616ad96191c46ab3fe592a2b06f79cd..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a delayed peer instruction round is initiated. The delayed part denotes that this round might not
- * have been started yet.
- */
-public class PiRoundDelayedStartEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-	private final String questionId;
-	private final Date startTime;
-	private final Date endTime;
-	private final String group;
-	private int piRound;
-
-	public PiRoundDelayedStartEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.questionId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-		this.piRound = content.getState().getRound();
-		this.endTime = content.getState().getRoundEndTimestamp();
-		this.startTime = new Date();
-	}
-
-	public String getQuestionId() {
-		return questionId;
-	}
-
-	public Date getStartTime() {
-		return startTime;
-	}
-
-	public Date getEndTime() {
-		return endTime;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Integer getPiRound() {
-		return piRound;
-	}
-
-	public Map<String, Object> getPiRoundInformations() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("endTime", getEndTime().getTime());
-		map.put("startTime", getStartTime().getTime());
-		map.put("variant", getGroup());
-		map.put("round", getPiRound());
-
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java
deleted file mode 100644
index 8d56a813950b1292eff2818bb6b5a54b2cc6d024..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a peer instruction round has ended.
- */
-public class PiRoundEndEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final String contentId;
-	private final String group;
-
-	public PiRoundEndEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		contentId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-	}
-
-	public String getContentId() {
-		return contentId;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Map<String, String> getPiRoundEndInformations() {
-		Map<String, String> map = new HashMap<>();
-
-		map.put("_id", getContentId());
-		map.put("variant", getGroup());
-
-		return map;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java
deleted file mode 100644
index 2b4cd53254f022f33241402c0e13cf595b455eb5..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a peer instruction round is reset.
- */
-public class PiRoundResetEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final String contentId;
-	private final String group;
-
-	public PiRoundResetEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		contentId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-	}
-
-	public String getContentId() {
-		return contentId;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Map<String, String> getPiRoundResetInformations() {
-		Map<String, String> map = new HashMap<>();
-
-		map.put("_id", getContentId());
-		map.put("variant", getGroup());
-
-		return map;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java b/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java
deleted file mode 100644
index d7307cd7919c8fd161e5e1fd3522dc31b101338b..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.event;
-
-/**
- * Fires whenever the status of a session changes, i.e., it is enabled or disabled.
- */
-public class StatusRoomEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public StatusRoomEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java b/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java
deleted file mode 100644
index deddf45f1f18a762ae3650fd6e8ebd817d967db3..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a content is enabled, i.e., it becomes visible to students.
- */
-public class UnlockQuestionEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public UnlockQuestionEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public Content getQuestion() {
-		return this.content;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java
deleted file mode 100644
index 81f38cc60c40641ccd22223e26d0f4a6b80fdc47..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever a set of contents are enabled, i.e., they become visible to students.
- */
-public class UnlockQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public UnlockQuestionsEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java b/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java
deleted file mode 100644
index 3dd6996e3fd8cad2810ce3fd887088864d4a056f..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever voting on a content is enabled.
- */
-public class UnlockVoteEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public UnlockVoteEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public String getQuestionId() {
-		return this.content.getId();
-	}
-
-	public String getGroup() {
-		/* FIXME: Event does not support content with multiple groups */
-		return content.getGroups().toArray(new String[1])[0];
-	}
-
-	public Boolean getVotingDisabled() {
-		return !this.content.getState().isResponsesEnabled();
-	}
-
-	public Map<String, Object> getVotingAdmission() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("variant", getGroup());
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java b/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java
deleted file mode 100644
index 85cf1d06f9a8ce5b55c3fd48b9cc6a0c4dc005bf..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever voting of multiple contents is enabled.
- */
-public class UnlockVotesEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public UnlockVotesEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/service/AnswerService.java b/src/main/java/de/thm/arsnova/service/AnswerService.java
index 759815ccf0dd77f8afccf504940dc9c6c2794c6d..01a1a6fc37fdfa5cffd3875ab94a9bff4983168f 100644
--- a/src/main/java/de/thm/arsnova/service/AnswerService.java
+++ b/src/main/java/de/thm/arsnova/service/AnswerService.java
@@ -42,8 +42,6 @@ public interface AnswerService extends EntityService<Answer> {
 
 	Answer updateAnswer(Answer answer);
 
-	void deleteAnswer(String contentId, String answerId);
-
 	Map<String, Object> countAnswersAndAbstentionsInternal(String contentId);
 
 	int countLectureContentAnswers(String roomId);
diff --git a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
index bc0b146cb937bee6a1dca389b4c634e046b42a5c..8bf36e4fccaa8d8c390a02ed6a59b3e429831164 100644
--- a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
@@ -27,8 +27,6 @@ import de.thm.arsnova.model.Room;
 import de.thm.arsnova.model.TextAnswer;
 import de.thm.arsnova.model.transport.AnswerQueueElement;
 import de.thm.arsnova.persistence.AnswerRepository;
-import de.thm.arsnova.persistence.ContentRepository;
-import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.security.User;
 import de.thm.arsnova.web.exceptions.NotFoundException;
 import de.thm.arsnova.web.exceptions.UnauthorizedException;
@@ -58,23 +56,21 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 
 	private final Queue<AnswerQueueElement> answerQueue = new ConcurrentLinkedQueue<>();
 
-	private RoomRepository roomRepository;
-	private ContentRepository contentRepository;
-	private AnswerRepository answerRepository;
+	private RoomService roomService;
 	private ContentService contentService;
+	private AnswerRepository answerRepository;
 	private UserService userService;
 
 	public AnswerServiceImpl(
 			AnswerRepository repository,
-			ContentRepository contentRepository,
-			RoomRepository roomRepository,
+			RoomService roomService,
 			ContentService contentService,
 			UserService userService,
 			@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
 		super(Answer.class, repository, jackson2HttpMessageConverter.getObjectMapper());
 		this.answerRepository = repository;
-		this.contentRepository = contentRepository;
-		this.roomRepository = roomRepository;
+		this.roomService = roomService;
+		this.contentService = contentService;
 		this.contentService = contentService;
 		this.userService = userService;
 	}
@@ -110,7 +106,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void deleteAnswers(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = contentService.get(contentId);
 		content.resetState();
 		/* FIXME: cancel timer */
 		contentService.update(content);
@@ -129,7 +125,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 
 	@Override
 	public void getFreetextAnswerAndMarkRead(final String answerId, final String userId) {
-		final Answer answer = answerRepository.findOne(answerId);
+		final Answer answer = get(answerId);
 		if (!(answer instanceof TextAnswer)) {
 			throw new NotFoundException();
 		}
@@ -137,17 +133,17 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 		if (textAnswer.isRead()) {
 			return;
 		}
-		final Room room = roomRepository.findOne(textAnswer.getRoomId());
+		final Room room = roomService.get(textAnswer.getRoomId());
 		if (room.getOwnerId().equals(userId)) {
 			textAnswer.setRead(true);
-			answerRepository.save(textAnswer);
+			update(textAnswer);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public AnswerStatistics getStatistics(final String contentId, final int round) {
-		final ChoiceQuestionContent content = (ChoiceQuestionContent) contentRepository.findOne(contentId);
+		final ChoiceQuestionContent content = (ChoiceQuestionContent) contentService.get(contentId);
 		if (content == null) {
 			throw new NotFoundException();
 		}
@@ -191,7 +187,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 	@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);
+		final Content content = contentService.get(contentId);
 		if (content == null) {
 			throw new NotFoundException();
 		}
@@ -320,7 +316,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 		if (content == null) {
 			throw new NotFoundException();
 		}
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Room room = roomService.get(content.getRoomId());
 
 		answer.setCreatorId(user.getId());
 		answer.setContentId(content.getId());
@@ -371,43 +367,22 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 			content.checkTextStrictOptions(realAnswer);
 		}
 		*/
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Room room = roomService.get(content.getRoomId());
 		answer.setCreatorId(user.getId());
 		answer.setContentId(content.getId());
 		answer.setRoomId(room.getId());
-		this.eventPublisher.publishEvent(new BeforeCreationEvent<>(this, realAnswer));
-		answerRepository.save(realAnswer);
-		this.eventPublisher.publishEvent(new AfterCreationEvent<>(this, realAnswer));
+		update(realAnswer);
 
 		return answer;
 	}
 
-	/* FIXME: Remove, this should be handled by EntityService! */
-	@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 User user = userService.getCurrentUser();
-		final Room room = roomRepository.findOne(content.getRoomId());
-		if (user == null || room == null || !room.getOwnerId().equals(user.getId())) {
-			throw new UnauthorizedException();
-		}
-		//this.eventPublisher.publishEvent(new BeforeDeletionEvent<>(answer));
-		answerRepository.deleteById(answerId);
-		//this.eventPublisher.publishEvent(new AfterDeletionEvent<>(answer));
-	}
-
 	/*
 	 * 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());
+		return answerRepository.countByRoomIdOnlyLectureVariant(roomService.get(roomId).getId());
 	}
 
 	@Override
@@ -444,6 +419,6 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 	 */
 	@Override
 	public int countPreparationQuestionAnswersInternal(final String roomId) {
-		return answerRepository.countByRoomIdOnlyPreparationVariant(roomRepository.findOne(roomId).getId());
+		return answerRepository.countByRoomIdOnlyPreparationVariant(roomService.get(roomId).getId());
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/CommentService.java b/src/main/java/de/thm/arsnova/service/CommentService.java
index a359bf881312b0c1f99846ade9140444c125e1be..5c0202e3210517b5349185075ce7431fc1498995 100644
--- a/src/main/java/de/thm/arsnova/service/CommentService.java
+++ b/src/main/java/de/thm/arsnova/service/CommentService.java
@@ -15,7 +15,5 @@ public interface CommentService extends EntityService<Comment> {
 
 	Comment getAndMarkRead(String commentId) throws IOException;
 
-	void delete(String commentId);
-
 	void deleteByRoomId(String roomId);
 }
diff --git a/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java b/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
index 91e7708ef82922b67b44d8a33183f1af1c6d83f8..69c1241f46d801983ed2bf70d58793a6605cb6ab 100644
--- a/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/CommentServiceImpl.java
@@ -1,12 +1,9 @@
 package de.thm.arsnova.service;
 
-import de.thm.arsnova.event.AfterDeletionEvent;
-import de.thm.arsnova.event.BeforeDeletionEvent;
 import de.thm.arsnova.model.Comment;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.model.migration.v2.CommentReadingCount;
 import de.thm.arsnova.persistence.CommentRepository;
-import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.security.User;
 import de.thm.arsnova.web.exceptions.ForbiddenException;
 import de.thm.arsnova.web.exceptions.NotFoundException;
@@ -29,25 +26,25 @@ import java.util.Map;
 public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implements CommentService {
 	private UserService userService;
 
-	private CommentRepository commentRepository;
+	private RoomService roomService;
 
-	private RoomRepository roomRepository;
+	private CommentRepository commentRepository;
 
 	public CommentServiceImpl(
 			CommentRepository repository,
-			RoomRepository roomRepository,
+			RoomService roomService,
 			UserService userService,
 			@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
 		super(Comment.class, repository, jackson2HttpMessageConverter.getObjectMapper());
 		this.commentRepository = repository;
-		this.roomRepository = roomRepository;
+		this.roomService = roomService;
 		this.userService = userService;
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void prepareCreate(final Comment comment) {
-		final Room room = roomRepository.findOne(comment.getRoomId());
+		final Room room = roomService.get(comment.getRoomId());
 		final User user = userService.getCurrentUser();
 		comment.setCreatorId(user.getId());
 		comment.setRead(false);
@@ -57,23 +54,10 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
 		/* TODO: fire event */
 	}
 
-	/* FIXME: Remove, EntityService should handle this! */
-	@Override
-	@PreAuthorize("hasPermission(#commentId, 'comment', 'owner')")
-	public void delete(final String commentId) {
-		final Comment comment = commentRepository.findOne(commentId);
-		if (comment == null) {
-			throw new NotFoundException();
-		}
-		eventPublisher.publishEvent(new BeforeDeletionEvent<>(this, comment));
-		commentRepository.delete(comment);
-		eventPublisher.publishEvent(new AfterDeletionEvent<>(this, comment));
-	}
-
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteByRoomId(final String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (room == null) {
 			throw new UnauthorizedException();
 		}
@@ -109,7 +93,7 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Comment> getByRoomId(final String roomId, final int offset, final int limit) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		final User user = getCurrentUser();
 		if (room.getOwnerId().equals(user.getId())) {
 			return commentRepository.findByRoomId(room.getId(), offset, limit);
@@ -121,7 +105,7 @@ public class CommentServiceImpl extends DefaultEntityServiceImpl<Comment> implem
 	@Override
 	@PreAuthorize("hasPermission(#commentId, 'comment', 'update')")
 	public Comment getAndMarkRead(final String commentId) throws IOException {
-		final Comment comment = commentRepository.findOne(commentId);
+		final Comment comment = get(commentId);
 		if (comment == null) {
 			throw new NotFoundException();
 		}
diff --git a/src/main/java/de/thm/arsnova/service/ContentService.java b/src/main/java/de/thm/arsnova/service/ContentService.java
index dbb3d8b5a9806e42c94aacbbded01fa8d3eb687e..355377a440aa1f33c293cc174663a3e66229e66f 100644
--- a/src/main/java/de/thm/arsnova/service/ContentService.java
+++ b/src/main/java/de/thm/arsnova/service/ContentService.java
@@ -36,6 +36,7 @@ package de.thm.arsnova.service;
 
 import de.thm.arsnova.model.Content;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -74,9 +75,9 @@ public interface ContentService extends EntityService<Content> {
 
 	List<String> getUnAnsweredPreparationContentIds(String roomId, String userId);
 
-	void publishAll(String roomId, boolean publish);
+	void publishAll(String roomId, boolean publish) throws IOException;
 
-	void publishContents(String roomId, boolean publish, Iterable<Content> contents);
+	void publishContents(String roomId, boolean publish, Iterable<Content> contents) throws IOException;
 
 	void deleteAllContentsAnswers(String roomId);
 
diff --git a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
index eec4adbd4c1b07258eccf1daaa43aaecf5177551..1f31288f168518873d2d5ce184c80e54efde0d15 100644
--- a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
@@ -17,13 +17,11 @@
  */
 package de.thm.arsnova.service;
 
-import de.thm.arsnova.event.*;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.persistence.AnswerRepository;
 import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.LogEntryRepository;
-import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.security.User;
 import de.thm.arsnova.web.exceptions.NotFoundException;
 import de.thm.arsnova.web.exceptions.UnauthorizedException;
@@ -39,7 +37,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -55,9 +53,9 @@ import java.util.stream.Collectors;
 public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implements ContentService {
 	private UserService userService;
 
-	private LogEntryRepository dbLogger;
+	private RoomService roomService;
 
-	private RoomRepository roomRepository;
+	private LogEntryRepository dbLogger;
 
 	private ContentRepository contentRepository;
 
@@ -67,15 +65,15 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	public ContentServiceImpl(
 			ContentRepository repository,
+			RoomService roomService,
 			AnswerRepository answerRepository,
-			RoomRepository roomRepository,
 			LogEntryRepository dbLogger,
 			UserService userService,
 			@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
 		super(Content.class, repository, jackson2HttpMessageConverter.getObjectMapper());
 		this.contentRepository = repository;
+		this.roomService = roomService;
 		this.answerRepository = answerRepository;
-		this.roomRepository = roomRepository;
 		this.dbLogger = dbLogger;
 		this.userService = userService;
 	}
@@ -91,7 +89,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			}
 			//content.setSessionKeyword(roomRepository.getSessionFromId(content.getRoomId()).getKeyword());
 
-			Room room = roomRepository.findOne(content.getRoomId());
+			Room room = roomService.get(content.getRoomId());
 			content.setGroups(room.getContentGroups().stream()
 					.map(Room.ContentGroup::getName).collect(Collectors.toSet()));
 
@@ -108,7 +106,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("isAuthenticated()")
 	//@Cacheable("contentlists")
 	public List<Content> getByRoomId(final String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		final User user = userService.getCurrentUser();
 		if (room.getOwnerId().equals(user.getId())) {
 			return contentRepository.findByRoomIdForSpeaker(roomId);
@@ -119,7 +117,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public Iterable<Content> getByRoomIdAndGroup(final String roomId, final String group) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		Room.ContentGroup contentGroup = null;
 		for (Room.ContentGroup cg : room.getContentGroups()) {
 			if (cg.getName().equals(group)) {
@@ -141,7 +139,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public int countByRoomIdAndGroup(final String roomId, final String group) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		Room.ContentGroup contentGroup = null;
 		for (Room.ContentGroup cg : room.getContentGroups()) {
 			if (cg.getName().equals(group)) {
@@ -181,7 +179,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@Override
 	public void finalizeCreate(final Content content) {
 		/* Update content groups of room */
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Room room = roomService.get(content.getRoomId());
 		final Set<Room.ContentGroup> contentGroups = room.getContentGroups();
 		for (final Room.ContentGroup cg : contentGroups) {
 			if (content.getGroups().contains(cg.getName())) {
@@ -198,20 +196,18 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			newGroup.setContentIds(newContentIds);
 			room.getContentGroups().add(newGroup);
 		}
-		eventPublisher.publishEvent(new BeforeCreationEvent<>(this, content));
-		roomRepository.save(room);
-		eventPublisher.publishEvent(new AfterCreationEvent<>(this, content));
+		roomService.update(room);
 	}
 
 	@Override
 	public void prepareUpdate(final Content content) {
 		final User user = userService.getCurrentUser();
-		final Content oldContent = contentRepository.findOne(content.getId());
+		final Content oldContent = get(content.getId());
 		if (null == oldContent) {
 			throw new NotFoundException();
 		}
 
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Room room = roomService.get(content.getRoomId());
 		if (user == null || room == null || !room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
@@ -229,7 +225,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@Override
 	public void finalizeUpdate(final Content content) {
 		/* Update content groups of room */
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Room room = roomService.get(content.getRoomId());
 		for (final Room.ContentGroup cg : room.getContentGroups()) {
 			if (content.getGroups().contains(cg.getName())) {
 				cg.getContentIds().add(content.getId());
@@ -250,7 +246,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			newGroup.setContentIds(newContentIds);
 			room.getContentGroups().add(newGroup);
 		}
-		roomRepository.save(room);
+		roomService.update(room);
 
 		/* TODO: not sure yet how to refactor this code - we need access to the old and new entity
 		if (!oldContent.getState().isVisible() && content.getState().isVisible()) {
@@ -274,28 +270,14 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			@CacheEvict(value = "preparationcontentlists", allEntries = true /*, condition = "#content.getGroups().contains('preparation')"*/),
 			@CacheEvict(value = "flashcardcontentlists", allEntries = true /*, condition = "#content.getGroups().contains('flashcard')"*/) })
 	public void delete(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = get(contentId);
 		if (content == null) {
 			throw new NotFoundException();
 		}
 
-		final Room room = roomRepository.findOne(content.getRoomId());
-		if (room == null) {
-			throw new UnauthorizedException();
-		}
-
-		for (final Room.ContentGroup contentGroup : room.getContentGroups()) {
-			if (content.getGroups().contains(contentGroup.getName())) {
-				contentGroup.getContentIds().remove(contentId);
-			}
-		}
-		roomRepository.save(room);
-
 		try {
 			final int count = answerRepository.deleteByContentId(contentId);
-			eventPublisher.publishEvent(new BeforeDeletionEvent<>(this, content));
-			contentRepository.deleteById(contentId);
-			eventPublisher.publishEvent(new AfterDeletionEvent<>(this, content));
+			delete(content);
 			dbLogger.log("delete", "type", "content", "answerCount", count);
 		} catch (final IllegalArgumentException e) {
 			logger.error("Could not delete content {}.", contentId, e);
@@ -317,13 +299,11 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			contentIds = contentRepository.findIdsByRoomIdAndVariant(room.getId(), variant);
 		}
 
+		/* TODO: use EntityService! */
 		final int answerCount = answerRepository.deleteByContentIds(contentIds);
 		final int contentCount = contentRepository.deleteByRoomId(room.getId());
 		dbLogger.log("delete", "type", "question", "questionCount", contentCount);
 		dbLogger.log("delete", "type", "answer", "answerCount", answerCount);
-
-		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, room.getId());
-		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -357,8 +337,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void setVotingAdmission(final String contentId, final boolean disableVoting) {
-		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Content content = get(contentId);
 		content.getState().setResponsesEnabled(!disableVoting);
 
 		if (!disableVoting && !content.getState().isVisible()) {
@@ -367,13 +346,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		} else {
 			update(content);
 		}
-		ArsnovaEvent event;
-		if (disableVoting) {
-			event = new LockVoteEvent(this, room.getId(), content);
-		} else {
-			event = new UnlockVoteEvent(this, room.getId(), content);
-		}
-		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -385,7 +357,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			@CacheEvict(value = "flashcardcontentlists", key = "#roomId") })
 	public void setVotingAdmissions(final String roomId, final boolean disableVoting, Iterable<Content> contents) {
 		final User user = getCurrentUser();
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (!room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
@@ -395,15 +367,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		patches.put("responsesEnabled", !disableVoting);
 		try {
 			patch(contents, patches, Content::getState);
-			ArsnovaEvent event;
-			List<Content> list = new ArrayList<>();
-			contents.forEach(list::add);
-			if (disableVoting) {
-				event = new LockVotesEvent(this, room.getId(), list);
-			} else {
-				event = new UnlockVotesEvent(this, room.getId(), list);
-			}
-			this.eventPublisher.publishEvent(event);
 		} catch (IOException e) {
 			logger.error("Patching of contents failed", e);
 		}
@@ -411,7 +374,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	private Room getRoomWithAuthCheck(final String roomId) {
 		final User user = userService.getCurrentUser();
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (user == null || room == null || !room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
@@ -468,10 +431,10 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void publishAll(final String roomId, final boolean publish) {
+	public void publishAll(final String roomId, final boolean publish) throws IOException {
 		/* TODO: resolve redundancies */
 		final User user = getCurrentUser();
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (!room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
@@ -486,25 +449,13 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			@CacheEvict(value = "lecturecontentlists", key = "#roomId"),
 			@CacheEvict(value = "preparationcontentlists", key = "#roomId"),
 			@CacheEvict(value = "flashcardcontentlists", key = "#roomId") })
-	public void publishContents(final String roomId, final boolean publish, Iterable<Content> contents) {
+	public void publishContents(final String roomId, final boolean publish, Iterable<Content> contents) throws IOException {
 		final User user = getCurrentUser();
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (!room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
-		for (final Content content : contents) {
-			content.getState().setVisible(publish);
-		}
-		contentRepository.saveAll(contents);
-		ArsnovaEvent event;
-		List<Content> list = new ArrayList<>();
-		contents.forEach(list::add);
-		if (publish) {
-			event = new UnlockQuestionsEvent(this, room.getId(), list);
-		} else {
-			event = new LockQuestionsEvent(this, room.getId(), list);
-		}
-		this.eventPublisher.publishEvent(event);
+		patch(contents, Collections.singletonMap("visible", publish), Content::getState);
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -513,7 +464,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@CacheEvict(value = "answerlists", allEntries = true)
 	public void deleteAllContentsAnswers(final String roomId) {
 		final User user = getCurrentUser();
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (!room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
@@ -521,9 +472,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId());
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, room.getId()));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -532,14 +482,13 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
 	@CacheEvict(value = "answerlists", allEntries = true)
 	public void deleteAllPreparationAnswers(String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "preparation");
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, room.getId()));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -548,14 +497,13 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
 	@CacheEvict(value = "answerlists", allEntries = true)
 	public void deleteAllLectureAnswers(String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "lecture");
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllLectureAnswersEvent(this, room.getId()));
 	}
 
 	@Caching(evict = {
diff --git a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
index 1cd54312953fe8c3ff5677acfadae915d6daa3e6..194822867c9ed71650d7d63f0e50de47bfa4b4c0 100644
--- a/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/DefaultEntityServiceImpl.java
@@ -101,7 +101,7 @@ public class DefaultEntityServiceImpl<T extends Entity> implements EntityService
 		eventPublisher.publishEvent(new BeforeCreationEvent<>(this, entity));
 		final T createdEntity = repository.save(entity);
 		eventPublisher.publishEvent(new AfterCreationEvent<>(this, createdEntity));
-		finalizeCreate(entity);
+		finalizeCreate(createdEntity);
 		modifyRetrieved(entity);
 
 		return createdEntity;
diff --git a/src/main/java/de/thm/arsnova/service/FeedbackServiceImpl.java b/src/main/java/de/thm/arsnova/service/FeedbackServiceImpl.java
index ea4b87312625056348c3d7d38f5bdc3c9916b396..b542ec44c93b21d52664c224b71e7af12b7adbe7 100644
--- a/src/main/java/de/thm/arsnova/service/FeedbackServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/FeedbackServiceImpl.java
@@ -21,7 +21,6 @@ import de.thm.arsnova.event.DeleteFeedbackForRoomsEvent;
 import de.thm.arsnova.event.NewFeedbackEvent;
 import de.thm.arsnova.model.Feedback;
 import de.thm.arsnova.model.Room;
-import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.web.exceptions.NoContentException;
 import de.thm.arsnova.web.exceptions.NotFoundException;
 import org.springframework.beans.factory.annotation.Value;
@@ -51,15 +50,15 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 	@Value("${feedback.cleanup}")
 	private int cleanupFeedbackDelay;
 
-	private RoomRepository roomRepository;
+	private RoomService roomService;
 
 	private FeedbackStorageService feedbackStorage;
 
 	private ApplicationEventPublisher publisher;
 
-	public FeedbackServiceImpl(FeedbackStorageService feedbackStorage, RoomRepository roomRepository) {
+	public FeedbackServiceImpl(FeedbackStorageService feedbackStorage, RoomService roomService) {
 		this.feedbackStorage = feedbackStorage;
-		this.roomRepository = roomRepository;
+		this.roomService = roomService;
 	}
 
 	@Override
@@ -101,7 +100,7 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 
 	@Override
 	public void cleanFeedbackVotesByRoomId(final String roomId, final int cleanupFeedbackDelayInMins) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		List<String> affectedUserIds = feedbackStorage.cleanVotesByRoom(room, cleanupFeedbackDelayInMins);
 		Set<Room> sessionSet = new HashSet<>();
 		sessionSet.add(room);
@@ -116,7 +115,7 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 
 	@Override
 	public Feedback getByRoomId(final String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (room == null) {
 			throw new NotFoundException();
 		}
@@ -133,7 +132,7 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 
 	@Override
 	public double calculateAverageFeedback(final String roomId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (room == null) {
 			throw new NotFoundException();
 		}
@@ -157,7 +156,7 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 
 	@Override
 	public boolean save(final String roomId, final int value, final String userId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (room == null) {
 			throw new NotFoundException();
 		}
@@ -169,7 +168,7 @@ public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPub
 
 	@Override
 	public Integer getByRoomIdAndUserId(final String roomId, final String userId) {
-		final Room room = roomRepository.findOne(roomId);
+		final Room room = roomService.get(roomId);
 		if (room == null) {
 			throw new NotFoundException();
 		}
diff --git a/src/main/java/de/thm/arsnova/service/MotdService.java b/src/main/java/de/thm/arsnova/service/MotdService.java
index 6de4cc1d24135a3bec0015c6dfca451ae92683fc..c0ce86e000ce5b6274bd3863afb5813fa06b7432 100644
--- a/src/main/java/de/thm/arsnova/service/MotdService.java
+++ b/src/main/java/de/thm/arsnova/service/MotdService.java
@@ -38,10 +38,6 @@ public interface MotdService extends EntityService<Motd> {
 
 	List<Motd> filterMotdsByList(List<Motd> list, List<String> ids);
 
-	void delete(Motd motd);
-
-	void deleteByRoomId(final String roomId, Motd motd);
-
 	Motd save(Motd motd);
 
 	Motd save(final String roomId, final Motd motd);
diff --git a/src/main/java/de/thm/arsnova/service/MotdServiceImpl.java b/src/main/java/de/thm/arsnova/service/MotdServiceImpl.java
index 6a2b09d40b421e1b9d3a5ddb98f1351dbdc0c97e..53dcf4fb6198b5e9fe1151ef72be5ce4350208e2 100644
--- a/src/main/java/de/thm/arsnova/service/MotdServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/MotdServiceImpl.java
@@ -135,7 +135,7 @@ public class MotdServiceImpl extends DefaultEntityServiceImpl<Motd> implements M
 	@CacheEvict(cacheNames = "motds", key = "#motd.audience + #motd.roomId")
 	private Motd createOrUpdateMotd(final Motd motd) {
 		if (motd.getId() != null) {
-			Motd oldMotd = motdRepository.findOne(motd.getId());
+			Motd oldMotd = get(motd.getId());
 			if (!(motd.getId().equals(oldMotd.getId()) && motd.getRoomId().equals(oldMotd.getRoomId())
 					&& motd.getAudience().equals(oldMotd.getAudience()))) {
 				throw new BadRequestException();
@@ -151,17 +151,4 @@ public class MotdServiceImpl extends DefaultEntityServiceImpl<Motd> implements M
 
 		return super.create(motd);
 	}
-
-	@Override
-	@PreAuthorize("hasPermission('', 'motd', 'admin')")
-	@CacheEvict(cacheNames = "motds", key = "#motd.audience + #motd.roomId")
-	public void delete(Motd motd) {
-		motdRepository.delete(motd);
-	}
-
-	@Override
-	@PreAuthorize("hasPermission(#roomId, 'room', 'owner')")
-	public void deleteByRoomId(final String roomId, Motd motd) {
-		motdRepository.delete(motd);
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/RoomService.java b/src/main/java/de/thm/arsnova/service/RoomService.java
index 2c30896d2c5b137d3de79d8c1870c6ae9f493308..1660058b6a2cba8e95e855b64f78fae471692696 100644
--- a/src/main/java/de/thm/arsnova/service/RoomService.java
+++ b/src/main/java/de/thm/arsnova/service/RoomService.java
@@ -19,10 +19,10 @@ package de.thm.arsnova.service;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.model.Room;
-import de.thm.arsnova.model.migration.v2.ClientAuthentication;
 import de.thm.arsnova.model.transport.ImportExportContainer;
 import de.thm.arsnova.model.transport.ScoreStatistics;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.UUID;
 
@@ -56,14 +56,12 @@ public interface RoomService extends EntityService<Room> {
 
 	int activeUsers(String id);
 
-	Room setActive(String id, Boolean lock);
+	Room setActive(String id, Boolean lock) throws IOException;
 
 	Room join(String id, UUID socketId);
 
 	Room updateCreator(String id, String newCreator);
 
-	Room updateInternal(Room room, ClientAuthentication user);
-
 	int[] deleteCascading(Room room);
 
 	ScoreStatistics getLearningProgress(String id, String type, String questionVariant);
@@ -88,7 +86,7 @@ public interface RoomService extends EntityService<Room> {
 
 	Room.Settings updateFeatures(String id, Room.Settings settings);
 
-	boolean lockFeedbackInput(String id, Boolean lock);
+	boolean lockFeedbackInput(String id, Boolean lock) throws IOException;
 
 	boolean flipFlashcards(String id, Boolean flip);
 
diff --git a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
index 83b06681be2f48bf1340ce33d184be14ecb77552..6d9b4fef6dab946c1be23cf39866cdf8a550f4d8 100644
--- a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
@@ -19,15 +19,9 @@ package de.thm.arsnova.service;
 
 import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.event.AfterDeletionEvent;
-import de.thm.arsnova.event.BeforeDeletionEvent;
-import de.thm.arsnova.event.FeatureChangeEvent;
 import de.thm.arsnova.event.FlipFlashcardsEvent;
-import de.thm.arsnova.event.LockFeedbackEvent;
-import de.thm.arsnova.event.StatusRoomEvent;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.model.UserProfile;
-import de.thm.arsnova.model.migration.v2.ClientAuthentication;
 import de.thm.arsnova.model.transport.ImportExportContainer;
 import de.thm.arsnova.model.transport.ScoreStatistics;
 import de.thm.arsnova.persistence.AnswerRepository;
@@ -53,8 +47,10 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
@@ -97,25 +93,37 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	public RoomServiceImpl(
 			RoomRepository repository,
-			ContentRepository contentRepository,
-			AnswerRepository answerRepository,
-			CommentRepository commentRepository,
 			LogEntryRepository dbLogger,
 			UserService userService,
-			FeedbackService feedbackService,
 			ScoreCalculatorFactory scoreCalculatorFactory,
 			@Qualifier("defaultJsonMessageConverter") MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
 		super(Room.class, repository, jackson2HttpMessageConverter.getObjectMapper());
 		this.roomRepository = repository;
-		this.contentRepository = contentRepository;
-		this.answerRepository = answerRepository;
-		this.commentRepository = commentRepository;
 		this.dbLogger = dbLogger;
 		this.userService = userService;
-		this.feedbackService = feedbackService;
 		this.scoreCalculatorFactory = scoreCalculatorFactory;
 	}
 
+	@Autowired
+	public void setCommentRepository(final CommentRepository commentRepository) {
+		this.commentRepository = commentRepository;
+	}
+
+	@Autowired
+	public void setContentRepository(final ContentRepository contentRepository) {
+		this.contentRepository = contentRepository;
+	}
+
+	@Autowired
+	public void setAnswerRepository(final AnswerRepository answerRepository) {
+		this.answerRepository = answerRepository;
+	}
+
+	@Autowired
+	public void setFeedbackService(final FeedbackService feedbackService) {
+		this.feedbackService = feedbackService;
+	}
+
 	public static class RoomNameComparator implements Comparator<Room>, Serializable {
 		private static final long serialVersionUID = 1L;
 
@@ -197,7 +205,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	@Override
 	public Room join(final String id, final UUID socketId) {
-		Room room = null != id ? roomRepository.findOne(id) : null;
+		Room room = null != id ? get(id) : null;
 		if (null == room) {
 			userService.removeUserFromRoomBySocketId(socketId);
 			return null;
@@ -241,7 +249,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
 	public Room getForAdmin(final String id) {
-		return roomRepository.findOne(id);
+		return get(id);
 	}
 
 	/*
@@ -250,7 +258,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	 */
 	@Override
 	public Room getInternal(final String id, final String userId) {
-		final Room room = roomRepository.findOne(id);
+		final Room room = get(id, true);
 		if (room == null) {
 			throw new NotFoundException();
 		}
@@ -401,11 +409,9 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
-	public Room setActive(final String id, final Boolean lock) {
-		final Room room = roomRepository.findOne(id);
-		room.setClosed(!lock);
-		this.eventPublisher.publishEvent(new StatusRoomEvent(this, room.getId()));
-		roomRepository.save(room);
+	public Room setActive(final String id, final Boolean lock) throws IOException {
+		final Room room = get(id);
+		patch(room, Collections.singletonMap("closed", lock));
 
 		return room;
 	}
@@ -414,7 +420,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	/* TODO: move caching to DefaultEntityServiceImpl */
 	//@CachePut(value = "rooms", key = "#room")
 	protected void prepareUpdate(final Room room) {
-		final Room existingRoom = roomRepository.findOne(room.getId());
+		final Room existingRoom = get(room.getId());
 		room.setOwnerId(existingRoom.getOwnerId());
 		handleLogo(room);
 
@@ -430,19 +436,6 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		throw new UnsupportedOperationException("No longer implemented.");
 	}
 
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public Room updateInternal(final Room room, final ClientAuthentication user) {
-		if (room.getOwnerId().equals(user.getId())) {
-			roomRepository.save(room);
-			return room;
-		}
-		return null;
-	}
-
 	@Override
 	@PreAuthorize("hasPermission(#room, 'owner')")
 	@Caching(evict = {
@@ -455,9 +448,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		count[2] = commentRepository.deleteByRoomId(room.getId());
 		count[1] = answerRepository.deleteByContentIds(contentIds);
 		count[0] = contentRepository.deleteByRoomId(room.getId());
-		this.eventPublisher.publishEvent(new BeforeDeletionEvent<>(this, room));
-		roomRepository.delete(room);
-		this.eventPublisher.publishEvent(new AfterDeletionEvent<>(this, room));
+		delete(room);
 		logger.debug("Deleted room document {} and related data.", room.getId());
 		dbLogger.log("delete", "type", "session", "id", room.getId());
 
@@ -467,7 +458,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'read')")
 	public ScoreStatistics getLearningProgress(final String id, final String type, final String questionVariant) {
-		final Room room = roomRepository.findOne(id);
+		final Room room = get(id);
 		ScoreCalculator scoreCalculator = scoreCalculatorFactory.create(type, questionVariant);
 		return scoreCalculator.getCourseProgress(room);
 	}
@@ -475,7 +466,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'read')")
 	public ScoreStatistics getMyLearningProgress(final String id, final String type, final String questionVariant) {
-		final Room room = roomRepository.findOne(id);
+		final Room room = get(id);
 		final User user = userService.getCurrentUser();
 		ScoreCalculator scoreCalculator = scoreCalculatorFactory.create(type, questionVariant);
 		return scoreCalculator.getMyProgress(room, user.getId());
@@ -511,31 +502,28 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'read')")
 	public Room.Settings getFeatures(String id) {
-		return roomRepository.findOne(id).getSettings();
+		return get(id).getSettings();
 	}
 
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
 	public Room.Settings updateFeatures(String id, Room.Settings settings) {
-		final Room room = roomRepository.findOne(id);
+		final Room room = get(id);
 		room.setSettings(settings);
-		this.eventPublisher.publishEvent(new FeatureChangeEvent(this, room.getId()));
-		roomRepository.save(room);
+
+		update(room);
 
 		return room.getSettings();
 	}
 
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
-	public boolean lockFeedbackInput(String id, Boolean lock) {
-		final Room room = roomRepository.findOne(id);
+	public boolean lockFeedbackInput(String id, Boolean lock) throws IOException {
+		final Room room = get(id);
 		if (!lock) {
 			feedbackService.cleanFeedbackVotesByRoomId(id, 0);
 		}
-
-		room.getSettings().setFeedbackLocked(lock);
-		this.eventPublisher.publishEvent(new LockFeedbackEvent(this, room.getId()));
-		roomRepository.save(room);
+		patch(room, Collections.singletonMap("feedbackLocked", lock), Room::getSettings);
 
 		return room.getSettings().isFeedbackLocked();
 	}
@@ -543,7 +531,7 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
 	public boolean flipFlashcards(String id, Boolean flip) {
-		final Room room = roomRepository.findOne(id);
+		final Room room = get(id);
 		this.eventPublisher.publishEvent(new FlipFlashcardsEvent(this, room.getId()));
 
 		return flip;
diff --git a/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java b/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
index 40b07f175fedad40329e6f930d3c00ec4ab6ecf3..5a4fad25a5b18feae35a0012d4f943011b8205d3 100644
--- a/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
@@ -1,18 +1,10 @@
 package de.thm.arsnova.service;
 
-import de.thm.arsnova.event.PiRoundCancelEvent;
-import de.thm.arsnova.event.PiRoundDelayedStartEvent;
-import de.thm.arsnova.event.PiRoundEndEvent;
-import de.thm.arsnova.event.PiRoundResetEvent;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.persistence.AnswerRepository;
-import de.thm.arsnova.persistence.ContentRepository;
-import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.security.User;
 import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
@@ -22,52 +14,48 @@ import java.util.Timer;
 import java.util.TimerTask;
 
 @Service
-public class TimerServiceImpl implements TimerService, ApplicationEventPublisherAware {
+public class TimerServiceImpl implements TimerService {
 	private HashMap<String, Timer> timerList = new HashMap<>();
 	private UserService userService;
-	private RoomRepository roomRepository;
-	private ContentRepository contentRepository;
+	private RoomService roomService;
+	private ContentService contentService;
 	private AnswerRepository answerRepository;
-	private ApplicationEventPublisher publisher;
 
-	public TimerServiceImpl(final UserService userService, final RoomRepository roomRepository,
-			final ContentRepository contentRepository, final AnswerRepository answerRepository) {
+	public TimerServiceImpl(final UserService userService, final RoomService roomService,
+			final ContentService contentService, final AnswerRepository answerRepository) {
 		this.userService = userService;
-		this.roomRepository = roomRepository;
-		this.contentRepository = contentRepository;
+		this.roomService = roomService;
+		this.contentService = contentService;
 		this.answerRepository = answerRepository;
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#contentId, 'content', 'owner')")
 	public void startNewRound(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Content content = contentService.get(contentId);
+		final Room room = roomService.get(content.getRoomId());
 
 		cancelDelayedRoundChange(contentId);
 
 		content.getState().setRoundEndTimestamp(null);
 		content.getState().setResponsesEnabled(false);
 		updateRoundManagementState(content);
-		contentRepository.save(content);
-
-		this.publisher.publishEvent(new PiRoundEndEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void startNewRoundDelayed(final String contentId, final int time) {
 		final User user = userService.getCurrentUser();
-		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Content content = contentService.get(contentId);
+		final Room room = roomService.get(content.getRoomId());
 
 		final Date date = new Date();
 		final Timer timer = new Timer();
 		final Date endDate = new Date(date.getTime() + (time * 1000));
 		updateRoundStartVariables(content, date, endDate);
-		contentRepository.save(content);
+		contentService.update(content);
 
-		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, room.getId(), content));
 		timerList.put(contentId, timer);
 
 		timer.schedule(new TimerTask() {
@@ -81,8 +69,8 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void cancelRoundChange(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Content content = contentService.get(contentId);
+		final Room room = roomService.get(content.getRoomId());
 
 		cancelDelayedRoundChange(contentId);
 		resetRoundManagementState(content);
@@ -92,8 +80,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 		}
 		content.getState().setRoundEndTimestamp(null);
 
-		contentRepository.save(content);
-		this.publisher.publishEvent(new PiRoundCancelEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	@Override
@@ -111,8 +98,8 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	@CacheEvict("answerlists")
 	public void resetRoundState(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
+		final Content content = contentService.get(contentId);
+		final Room room = roomService.get(content.getRoomId());
 		cancelDelayedRoundChange(contentId);
 
 		if (Content.Format.TEXT == content.getFormat()) {
@@ -123,8 +110,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 
 		resetRoundManagementState(content);
 		answerRepository.deleteByContentId(content.getId());
-		contentRepository.save(content);
-		this.publisher.publishEvent(new PiRoundResetEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	private void updateRoundStartVariables(final Content content, final Date start, final Date end) {
@@ -157,9 +143,4 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 		content.getState().setRound(1);
 		content.getState().setRoundEndTimestamp(null);
 	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
-		this.publisher = applicationEventPublisher;
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java b/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
index 908c17bccfa9103764fefd3c999e3fe26b7d7411..2eea760750f48d6cc15682d443d8c00bcd7fd2ea 100644
--- a/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
+++ b/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
@@ -17,7 +17,10 @@
  */
 package de.thm.arsnova.service.score;
 
-import de.thm.arsnova.event.*;
+import de.thm.arsnova.event.AfterCreationEvent;
+import de.thm.arsnova.event.AfterDeletionEvent;
+import de.thm.arsnova.event.ChangeScoreEvent;
+import de.thm.arsnova.event.StateChangeEvent;
 import de.thm.arsnova.model.Answer;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.persistence.SessionStatisticsRepository;
@@ -59,28 +62,10 @@ public class ScoreCalculatorFactoryImpl implements ScoreCalculatorFactory, Appli
 		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleUnlockQuestion(UnlockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleUnlockQuestions(UnlockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleLockQuestion(LockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleLockQuestions(LockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
+	@CacheEvict(value = "score", key = "#event.entity.roomId")
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentStateChange(StateChangeEvent<Content, Content.State> event) {
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
 	@CacheEvict(value = "score", key = "#event.entity.roomId")
@@ -101,36 +86,6 @@ public class ScoreCalculatorFactoryImpl implements ScoreCalculatorFactory, Appli
 		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllQuestions(DeleteAllQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllQuestionsAnswers(DeleteAllQuestionsAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllPreparationAnswers(DeleteAllPreparationAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllLectureAnswers(DeleteAllLectureAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handlePiRoundReset(PiRoundResetEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
 	@Override
 	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
 		this.publisher = publisher;
diff --git a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
index 8e99eca9e25362342d03d708d78117842626bf9c..874ea935bc869328fb07a3d4893dcb63ab963cc1 100644
--- a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
@@ -28,7 +28,13 @@ 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.event.*;
+import de.thm.arsnova.event.AfterCreationEvent;
+import de.thm.arsnova.event.AfterDeletionEvent;
+import de.thm.arsnova.event.ChangeScoreEvent;
+import de.thm.arsnova.event.DeleteFeedbackForRoomsEvent;
+import de.thm.arsnova.event.FlipFlashcardsEvent;
+import de.thm.arsnova.event.NewFeedbackEvent;
+import de.thm.arsnova.event.StateChangeEvent;
 import de.thm.arsnova.model.Answer;
 import de.thm.arsnova.model.Comment;
 import de.thm.arsnova.model.ScoreOptions;
@@ -60,7 +66,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
@@ -496,24 +504,13 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 		this.reportContentAvailable(event.getEntity().getId(), Collections.singletonList(event.getEntity()));
 	}
 
-	@EventListener
-	public void handleUnlockQuestion(UnlockQuestionEvent event) {
-		this.reportContentAvailable(event.getRoomId(), Collections.singletonList(event.getQuestion()));
-	}
-
-	@EventListener
-	public void handleLockQuestion(LockQuestionEvent event) {
-		this.reportContentsLocked(event.getRoomId(), Collections.singletonList(event.getQuestion()));
-	}
-
-	@EventListener
-	public void handleUnlockQuestions(UnlockQuestionsEvent event) {
-		this.reportContentAvailable(event.getRoomId(), event.getQuestions());
-	}
-
-	@EventListener
-	public void handleLockQuestions(LockQuestionsEvent event) {
-		this.reportContentsLocked(event.getRoomId(), event.getQuestions());
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentIsVisibleStateChange(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		if (event.getEntity().getState().isVisible()) {
+			this.reportContentAvailable(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+		} else {
+			this.reportContentsLocked(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+		}
 	}
 
 	@EventListener
@@ -555,80 +552,80 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundDelayedStart(PiRoundDelayedStartEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "startDelayedPiRound", event.getPiRoundInformations());
+	public void handlePiRoundDelayedStart(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "startDelayedPiRound", generateRoundInfo(event.getEntity()));
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundEnd(PiRoundEndEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "endPiRound", event.getPiRoundEndInformations());
+	public void handlePiRoundEnd(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "endPiRound", generateRoundInfo(event.getEntity()));
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundCancel(PiRoundCancelEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "cancelPiRound", event.getContentId());
+	public void handlePiRoundCancel(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "cancelPiRound", event.getEntity().getId());
 	}
 
-	@EventListener
-	public void handlePiRoundReset(PiRoundResetEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "resetPiRound", event.getPiRoundResetInformations());
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handlePiRoundReset(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "resetPiRound", generateRoundInfo(event.getEntity()));
 	}
 
-	@EventListener
-	public void handleLockVote(LockVoteEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "lockVote", event.getVotingAdmission());
-	}
-
-	@EventListener
-	public void handleUnlockVote(UnlockVoteEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "unlockVote", event.getVotingAdmission());
-	}
-
-	@EventListener
-	public void handleLockVotes(LockVotesEvent event) {
-		List<Content> contents = new ArrayList<>();
-		for (de.thm.arsnova.model.Content q : event.getQuestions()) {
-			contents.add(new Content(q));
+	private Map<String, Object> generateRoundInfo(de.thm.arsnova.model.Content content) {
+		Map<String, Object> map = new HashMap<>();
+		map.put("_id", content.getId());
+		if (content.getState().getRoundEndTimestamp() != null) {
+			map.put("endTime", content.getState().getRoundEndTimestamp().getTime());
 		}
-		broadcastInRoom(event.getRoomId(), "lockVotes", contents);
-	}
-
-	@EventListener
-	public void handleUnlockVotes(UnlockVotesEvent event) {
-		List<Content> contents = new ArrayList<>();
-		for (de.thm.arsnova.model.Content q : event.getQuestions()) {
-			contents.add(new Content(q));
+		/* FIXME: getRoundStartTimestamp is not implemented for Content.State. Is a delayed start still useful? */
+		/*
+		if (content.getState().getRoundStartTimestamp() != null) {
+			map.put("startTime", content.getState().getRoundStartTimestamp().getTime());
+		}
+		*/
+		map.put("variant", content.getGroups());
+		map.put("round", content.getState().getRound());
+
+		return map;
+	}
+
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentResponsesEnabledStateChange(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		/* Multiple groups for a single Content are not handled. */
+		final String groupName = event.getEntity().getGroups().iterator().hasNext() ?
+				event.getEntity().getGroups().iterator().next() : "";
+		Map<String, Object> map = new HashMap<>();
+		map.put("_id", event.getEntity().getId());
+		map.put("variant", groupName);
+		if (event.getEntity().getState().isResponsesEnabled()) {
+			this.reportContentAvailable(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+			broadcastInRoom(event.getEntity().getRoomId(), "unlockVote", map);
+		} else {
+			broadcastInRoom(event.getEntity().getRoomId(), "lockVote", map);
 		}
-		broadcastInRoom(event.getRoomId(), "unlockVotes", contents);
 	}
 
-	@EventListener
-	public void handleFeatureChange(FeatureChangeEvent event) {
-//		final String roomId = event.getRoomId();
-//		final de.thm.arsnova.model.Room.Settings settings = event.getRoom().getSettings();
-//		broadcastInRoom(roomId, "featureChange", toV2Migrator.migrate(settings));
-//
-//		if (settings.isFlashcardsEnabled()) {
-//			broadcastInRoom(roomId, "countFlashcards", contentService.countFlashcardsForUserInternal(roomId));
-//			broadcastInRoom(roomId, "flipFlashcards", event.getRoom().getFlipFlashcards());
-//		}
+	@EventListener(condition = "#event.stateName == 'settings'")
+	public void handleFeatureChange(StateChangeEvent<de.thm.arsnova.model.Room, de.thm.arsnova.model.Room.Settings> event) {
+		final String roomId = event.getEntity().getId();
+		final de.thm.arsnova.model.Room.Settings settings = event.getEntity().getSettings();
+		broadcastInRoom(roomId, "featureChange", toV2Migrator.migrate(settings));
+
+		if (settings.isFlashcardsEnabled()) {
+			broadcastInRoom(roomId, "countFlashcards", contentService.countFlashcardsForUserInternal(roomId));
+//			broadcastInRoom(roomId, "flipFlashcards", event.getEntity().getSettings().isFlipFlashcards());
+		}
 	}
 
-	@EventListener
-	public void handleLockFeedback(LockFeedbackEvent event) {
-//		broadcastInRoom(event.getRoomId(), "lockFeedback", event.getRoom().getSettings().isFeedbackLocked());
+	@EventListener(condition = "#event.stateName == 'settings'")
+	public void handleLockFeedback(StateChangeEvent<de.thm.arsnova.model.Room, de.thm.arsnova.model.Room.Settings> event) {
+		broadcastInRoom(event.getEntity().getId(), "lockFeedback", event.getEntity().getSettings().isFeedbackLocked());
 	}
 
 	@EventListener
@@ -647,9 +644,9 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 
 	}
 
-	@EventListener
-	public void handleStatusRoom(StatusRoomEvent event) {
-//		this.reportRoomStatus(event.getRoomId(), !event.getRoom().isClosed());
+	@EventListener(condition = "#event.stateName == 'closed'")
+	public void handleRoomClosedStateChange(StateChangeEvent<de.thm.arsnova.model.Room, Boolean> event) {
+		this.reportRoomStatus(event.getEntity().getId(), !event.getNewValue());
 	}
 
 	@EventListener
diff --git a/src/test/java/de/thm/arsnova/event/StateEventDispatcherTest.java b/src/test/java/de/thm/arsnova/event/StateEventDispatcherTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1b082e6f78b2f495174db3d5820385700ad72b1
--- /dev/null
+++ b/src/test/java/de/thm/arsnova/event/StateEventDispatcherTest.java
@@ -0,0 +1,141 @@
+package de.thm.arsnova.event;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+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.model.Content;
+import de.thm.arsnova.model.Room;
+import de.thm.arsnova.persistence.ContentRepository;
+import de.thm.arsnova.persistence.RoomRepository;
+import de.thm.arsnova.service.DefaultEntityServiceImpl;
+import de.thm.arsnova.test.context.support.WithMockUser;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.event.EventListener;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+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.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration(classes = {
+		AppConfig.class,
+		StateEventDispatcherTest.EventListenerConfig.class,
+		TestAppConfig.class,
+		TestPersistanceConfig.class,
+		TestSecurityConfig.class})
+@ActiveProfiles("test")
+public class StateEventDispatcherTest {
+	public static final String SETTINGS_PROPERTY_NAME = "settings";
+	public static final String STATE_PROPERTY_NAME = "state";
+	private static final String QUESTIONS_ENABLED_PROPERTY_NAME = "questionsEnabled";
+	private static final String VISIBLE_PROPERTY_NAME = "visible";
+	private static final String TEST_USER_ID = "TestUser";
+	private static final String TEST_ROOM_ID = "TestRoom";
+
+	@Autowired
+	private EventListenerConfig eventListenerConfig;
+
+	@Autowired
+	@Qualifier("defaultJsonMessageConverter")
+	private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;
+
+	@Autowired
+	private ApplicationEventPublisher eventPublisher;
+
+	@Autowired
+	private RoomRepository roomRepository;
+
+	@Autowired
+	private ContentRepository contentRepository;
+
+	@Before
+	public void prepare() {
+		eventListenerConfig.resetEvents();
+	}
+
+	@Test
+	@WithMockUser(TEST_USER_ID)
+	public void testDispatchRoomSettingsStateEvent() throws IOException {
+		final ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
+		final DefaultEntityServiceImpl<Room> entityService = new DefaultEntityServiceImpl<>(
+				Room.class, roomRepository, objectMapper);
+		entityService.setApplicationEventPublisher(eventPublisher);
+
+		when(roomRepository.save(any(Room.class))).then(returnsFirstArg());
+
+		Room room = new Room();
+		room.setOwnerId(TEST_USER_ID);
+		entityService.patch(room, Collections.singletonMap(QUESTIONS_ENABLED_PROPERTY_NAME, false), Room::getSettings);
+		assertEquals(1, eventListenerConfig.getRoomSettingsStateChangeEvents().size());
+		assertEquals(SETTINGS_PROPERTY_NAME, eventListenerConfig.getRoomSettingsStateChangeEvents().get(0).getStateName());
+	}
+
+	@Test
+	@WithMockUser(TEST_USER_ID)
+	public void testDispatchContentStateEvent() throws IOException {
+		final ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
+		final DefaultEntityServiceImpl<Content> entityService = new DefaultEntityServiceImpl<>(
+				Content.class, contentRepository, objectMapper);
+		entityService.setApplicationEventPublisher(eventPublisher);
+
+		Room room = new Room();
+		room.setId(TEST_ROOM_ID);
+		room.setOwnerId(TEST_USER_ID);
+		when(contentRepository.save(any(Content.class))).then(returnsFirstArg());
+		when(roomRepository.findOne(eq(room.getId()))).thenReturn(room);
+
+		Content content = new Content();
+		content.setRoomId(room.getId());
+		entityService.patch(content, Collections.singletonMap(VISIBLE_PROPERTY_NAME, false), Content::getState);
+		assertEquals(1, eventListenerConfig.getContentStateChangeEvents().size());
+		assertEquals(STATE_PROPERTY_NAME, eventListenerConfig.getContentStateChangeEvents().get(0).getStateName());
+	}
+
+	public static class EventListenerConfig {
+		private List<StateChangeEvent<Room, Room.Settings>> roomSettingsStateChangeEvents = new ArrayList<>();
+		private List<StateChangeEvent<Content, Content.State>> contentStateChangeEvents = new ArrayList<>();
+
+		@EventListener(condition = "#event.stateName == '" + SETTINGS_PROPERTY_NAME + "'")
+		public void handleRoomSettingsStateChangeEvent(StateChangeEvent<Room, Room.Settings> event) {
+			roomSettingsStateChangeEvents.add(event);
+		}
+
+		@EventListener(condition = "#event.stateName == '" + STATE_PROPERTY_NAME + "'")
+		public void handleContentStateChangeEvent(StateChangeEvent<Content, Content.State> event) {
+			contentStateChangeEvents.add(event);
+		}
+
+		public List<StateChangeEvent<Room, Room.Settings>> getRoomSettingsStateChangeEvents() {
+			return roomSettingsStateChangeEvents;
+		}
+
+		public List<StateChangeEvent<Content, Content.State>> getContentStateChangeEvents() {
+			return contentStateChangeEvents;
+		}
+
+		public void resetEvents() {
+			roomSettingsStateChangeEvents.clear();
+			contentStateChangeEvents.clear();
+		}
+	}
+}