diff --git a/src/main/java/de/thm/arsnova/aop/RangeAspect.java b/src/main/java/de/thm/arsnova/aop/RangeAspect.java
index 705b7529d44fd94f9082500236d10078210a671a..19734913d270bfad14d5bb6bdc85bae958d2eba5 100644
--- a/src/main/java/de/thm/arsnova/aop/RangeAspect.java
+++ b/src/main/java/de/thm/arsnova/aop/RangeAspect.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.aop;
 
-import de.thm.arsnova.PaginationListDecorator;
+import de.thm.arsnova.util.PaginationListDecorator;
 import de.thm.arsnova.controller.PaginationController;
 import de.thm.arsnova.services.ResponseProviderService;
 import org.aspectj.lang.ProceedingJoinPoint;
diff --git a/src/main/java/de/thm/arsnova/cache/CacheBuster.java b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
index c998bd4f30ddd4c69b9f6a0eaef4149e93e8fcb0..510f9e7404e435c2d8636d9456ef7f5991edcdad 100644
--- a/src/main/java/de/thm/arsnova/cache/CacheBuster.java
+++ b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
@@ -17,114 +17,7 @@
  */
 package de.thm.arsnova.cache;
 
-import de.thm.arsnova.events.*;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.stereotype.Component;
-
 /**
- * This class is used to evict caches based on events. The events carry all necessary information to clear the
- * caches, e.g, for a specific session.
+ * This interface is used as a tag to make Spring dependency injection happy...
  */
-@Component
-public class CacheBuster implements ICacheBuster, NovaEventVisitor {
-
-	@CacheEvict(value = "statistics", allEntries = true)
-	@Override
-	public void visit(NewCommentEvent event) { }
-
-	@CacheEvict(value = "statistics", allEntries = true)
-	@Override
-	public void visit(DeleteCommentEvent event) { }
-
-	@Override
-	public void visit(NewQuestionEvent event) { }
-
-	@Override
-	public void visit(UnlockQuestionEvent event) { }
-
-	@Override
-	public void visit(UnlockQuestionsEvent newQuestionsEvent) { }
-
-	@Override
-	public void visit(LockQuestionEvent lockQuestionEvent) { }
-
-	@Override
-	public void visit(LockQuestionsEvent lockQuestionsEvent) { }
-
-	@CacheEvict(value = "answers", key = "#event.content")
-	@Override
-	public void visit(NewAnswerEvent event) { }
-
-	@Override
-	public void visit(DeleteAnswerEvent event) { }
-
-	@Override
-	public void visit(DeleteQuestionEvent event) { }
-
-	@Override
-	public void visit(DeleteAllQuestionsEvent event) { }
-
-	@Override
-	public void visit(DeleteAllQuestionsAnswersEvent event) { }
-
-	@Override
-	public void visit(DeleteAllPreparationAnswersEvent event) { }
-
-	@Override
-	public void visit(DeleteAllLectureAnswersEvent event) { }
-
-	@Override
-	public void visit(NewFeedbackEvent event) { }
-
-	@Override
-	public void visit(DeleteFeedbackForSessionsEvent event) { }
-
-	@Override
-	public void visit(StatusSessionEvent event) { }
-
-	@CacheEvict(value = "statistics", allEntries = true)
-	@Override
-	public void visit(ChangeLearningProgressEvent changeLearningProgress) { }
-
-	@Override
-	public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
-
-	@Override
-	public void visit(PiRoundEndEvent piRoundEndEvent) { }
-
-	@Override
-	public void visit(PiRoundCancelEvent piRoundCancelEvent) { }
-
-	@Override
-	public void visit(PiRoundResetEvent piRoundResetEvent) { }
-
-	@CacheEvict(value = "statistics", allEntries = true)
-	@Override
-	public void visit(NewSessionEvent newSessionEvent) { }
-
-	@CacheEvict(value = "statistics", allEntries = true)
-	@Override
-	public void visit(DeleteSessionEvent deleteSessionEvent) { }
-
-	@Override
-	public void visit(LockVoteEvent lockVoteEvent) { }
-
-	@Override
-	public void visit(LockVotesEvent lockVotesEvent) { }
-
-	@Override
-	public void visit(UnlockVoteEvent unlockVoteEvent) { }
-
-	@Override
-	public void visit(UnlockVotesEvent unlockVotesEvent) { }
-
-	@Override
-	public void visit(FeatureChangeEvent featureChangeEvent) { }
-
-	@Override
-	public void visit(LockFeedbackEvent lockFeedbackEvent) { }
-
-	@Override
-	public void visit(FlipFlashcardsEvent flipFlashcardsEvent) { }
-
-}
+public interface CacheBuster { }
diff --git a/src/main/java/de/thm/arsnova/cache/CacheBusterImpl.java b/src/main/java/de/thm/arsnova/cache/CacheBusterImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..dad103c848cd90aecdf923af00c1c14ae47cc7e6
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/cache/CacheBusterImpl.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.cache;
+
+import de.thm.arsnova.events.*;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.stereotype.Component;
+
+/**
+ * This class is used to evict caches based on events. The events carry all necessary information to clear the
+ * caches, e.g, for a specific session.
+ */
+@Component
+public class CacheBusterImpl implements CacheBuster, ArsnovaEventVisitor {
+
+	@CacheEvict(value = "statistics", allEntries = true)
+	@Override
+	public void visit(NewCommentEvent event) { }
+
+	@CacheEvict(value = "statistics", allEntries = true)
+	@Override
+	public void visit(DeleteCommentEvent event) { }
+
+	@Override
+	public void visit(NewQuestionEvent event) { }
+
+	@Override
+	public void visit(UnlockQuestionEvent event) { }
+
+	@Override
+	public void visit(UnlockQuestionsEvent newQuestionsEvent) { }
+
+	@Override
+	public void visit(LockQuestionEvent lockQuestionEvent) { }
+
+	@Override
+	public void visit(LockQuestionsEvent lockQuestionsEvent) { }
+
+	@CacheEvict(value = "answers", key = "#event.content")
+	@Override
+	public void visit(NewAnswerEvent event) { }
+
+	@Override
+	public void visit(DeleteAnswerEvent event) { }
+
+	@Override
+	public void visit(DeleteQuestionEvent event) { }
+
+	@Override
+	public void visit(DeleteAllQuestionsEvent event) { }
+
+	@Override
+	public void visit(DeleteAllQuestionsAnswersEvent event) { }
+
+	@Override
+	public void visit(DeleteAllPreparationAnswersEvent event) { }
+
+	@Override
+	public void visit(DeleteAllLectureAnswersEvent event) { }
+
+	@Override
+	public void visit(NewFeedbackEvent event) { }
+
+	@Override
+	public void visit(DeleteFeedbackForSessionsEvent event) { }
+
+	@Override
+	public void visit(StatusSessionEvent event) { }
+
+	@CacheEvict(value = "statistics", allEntries = true)
+	@Override
+	public void visit(ChangeScoreEvent changeLearningProgress) { }
+
+	@Override
+	public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
+
+	@Override
+	public void visit(PiRoundEndEvent piRoundEndEvent) { }
+
+	@Override
+	public void visit(PiRoundCancelEvent piRoundCancelEvent) { }
+
+	@Override
+	public void visit(PiRoundResetEvent piRoundResetEvent) { }
+
+	@CacheEvict(value = "statistics", allEntries = true)
+	@Override
+	public void visit(NewSessionEvent newSessionEvent) { }
+
+	@CacheEvict(value = "statistics", allEntries = true)
+	@Override
+	public void visit(DeleteSessionEvent deleteSessionEvent) { }
+
+	@Override
+	public void visit(LockVoteEvent lockVoteEvent) { }
+
+	@Override
+	public void visit(LockVotesEvent lockVotesEvent) { }
+
+	@Override
+	public void visit(UnlockVoteEvent unlockVoteEvent) { }
+
+	@Override
+	public void visit(UnlockVotesEvent unlockVotesEvent) { }
+
+	@Override
+	public void visit(FeatureChangeEvent featureChangeEvent) { }
+
+	@Override
+	public void visit(LockFeedbackEvent lockFeedbackEvent) { }
+
+	@Override
+	public void visit(FlipFlashcardsEvent flipFlashcardsEvent) { }
+
+}
diff --git a/src/main/java/de/thm/arsnova/cache/CacheBustListener.java b/src/main/java/de/thm/arsnova/cache/CacheBusterListener.java
similarity index 78%
rename from src/main/java/de/thm/arsnova/cache/CacheBustListener.java
rename to src/main/java/de/thm/arsnova/cache/CacheBusterListener.java
index db365d064524b497694501240ab02a40e9d825d7..5bab8c63ce3364add9e12d43a375eef9602ff021 100644
--- a/src/main/java/de/thm/arsnova/cache/CacheBustListener.java
+++ b/src/main/java/de/thm/arsnova/cache/CacheBusterListener.java
@@ -17,8 +17,8 @@
  */
 package de.thm.arsnova.cache;
 
-import de.thm.arsnova.events.NovaEvent;
-import de.thm.arsnova.events.NovaEventVisitor;
+import de.thm.arsnova.events.ArsnovaEvent;
+import de.thm.arsnova.events.ArsnovaEventVisitor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
@@ -29,14 +29,14 @@ import org.springframework.stereotype.Component;
  * Note that this class is necessary in order for the annotations to work.
  */
 @Component
-public class CacheBustListener implements ApplicationListener<NovaEvent> {
+public class CacheBusterListener implements ApplicationListener<ArsnovaEvent> {
 
 	@Autowired
-	private ICacheBuster cacheBuster;
+	private CacheBuster cacheBuster;
 
 	@Override
-	public void onApplicationEvent(NovaEvent event) {
-		event.accept((NovaEventVisitor) cacheBuster);
+	public void onApplicationEvent(ArsnovaEvent event) {
+		event.accept((ArsnovaEventVisitor) cacheBuster);
 	}
 
 }
diff --git a/src/main/java/de/thm/arsnova/cache/ICacheBuster.java b/src/main/java/de/thm/arsnova/cache/ICacheBuster.java
deleted file mode 100644
index ab937cdd3354799182bf69959f94e03caeb8e376..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/cache/ICacheBuster.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.cache;
-
-/**
- * This interface is used as a tag to make Spring dependency injection happy...
- */
-public interface ICacheBuster { }
diff --git a/src/main/java/de/thm/arsnova/config/AppConfig.java b/src/main/java/de/thm/arsnova/config/AppConfig.java
index ab92d140c2511cc0263d5e0ded6b945748949aea..73719ab35bd516a4d9b731928230e9283c2c64b8 100644
--- a/src/main/java/de/thm/arsnova/config/AppConfig.java
+++ b/src/main/java/de/thm/arsnova/config/AppConfig.java
@@ -20,26 +20,18 @@ package de.thm.arsnova.config;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
-import de.thm.arsnova.ImageUtils;
+import de.thm.arsnova.util.ImageUtils;
 import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.client.ConnectorClientImpl;
-import de.thm.arsnova.entities.*;
 import de.thm.arsnova.entities.serialization.CouchDbDocumentModule;
-import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
 import de.thm.arsnova.entities.serialization.View;
-import de.thm.arsnova.persistance.*;
-import de.thm.arsnova.persistance.couchdb.*;
-import de.thm.arsnova.persistance.couchdb.InitializingCouchDbConnector;
-import de.thm.arsnova.socket.ARSnovaSocket;
-import de.thm.arsnova.socket.ARSnovaSocketIOServer;
-import de.thm.arsnova.socket.ARSnovaSocketListener;
+import de.thm.arsnova.websocket.ArsnovaSocketioServer;
+import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
+import de.thm.arsnova.websocket.ArsnovaSocketioServerListener;
 import de.thm.arsnova.web.CacheControlInterceptorHandler;
 import de.thm.arsnova.web.CorsFilter;
 import de.thm.arsnova.web.DeprecatedApiInterceptorHandler;
 import de.thm.arsnova.web.ResponseInterceptorHandler;
-import org.ektorp.CouchDbConnector;
-import org.ektorp.impl.StdCouchDbInstance;
-import org.ektorp.spring.HttpClientFactoryBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.config.PropertiesFactoryBean;
@@ -88,7 +80,6 @@ import java.util.List;
 		"de.thm.arsnova.aop",
 		"de.thm.arsnova.cache",
 		"de.thm.arsnova.controller",
-		"de.thm.arsnova.domain",
 		"de.thm.arsnova.dao",
 		"de.thm.arsnova.events",
 		"de.thm.arsnova.security",
@@ -261,19 +252,19 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 
 	@Profile("!test")
 	@Bean(name = "socketServer", initMethod = "startServer", destroyMethod = "stopServer")
-	public ARSnovaSocket socketServer() {
-		final ARSnovaSocketIOServer socketServer = new ARSnovaSocketIOServer();
-		socketServer.setHostIp(socketAddress);
-		socketServer.setPortNumber(socketPort);
-		socketServer.setUseSSL(!socketKeystore.isEmpty());
-		socketServer.setKeystore(socketKeystore);
-		socketServer.setStorepass(socketKeystorePassword);
-		return socketServer;
+	public ArsnovaSocketioServer socketServer() {
+		final ArsnovaSocketioServerImpl socketioServer = new ArsnovaSocketioServerImpl();
+		socketioServer.setHostIp(socketAddress);
+		socketioServer.setPortNumber(socketPort);
+		socketioServer.setUseSSL(!socketKeystore.isEmpty());
+		socketioServer.setKeystore(socketKeystore);
+		socketioServer.setStorepass(socketKeystorePassword);
+		return socketioServer;
 	}
 
 	@Bean
-	public ARSnovaSocketListener arsnovaSocketListener() {
-		return new ARSnovaSocketListener();
+	public ArsnovaSocketioServerListener arsnovaSocketListener() {
+		return new ArsnovaSocketioServerListener();
 	}
 
 	@Bean
diff --git a/src/main/java/de/thm/arsnova/config/SecurityConfig.java b/src/main/java/de/thm/arsnova/config/SecurityConfig.java
index fbf39de4f9e3131d076871a8169ff304b7f0f141..fe8fb597421ba6bf7dea5abccce3245122e879a8 100644
--- a/src/main/java/de/thm/arsnova/config/SecurityConfig.java
+++ b/src/main/java/de/thm/arsnova/config/SecurityConfig.java
@@ -17,10 +17,10 @@
  */
 package de.thm.arsnova.config;
 
-import de.thm.arsnova.CASLogoutSuccessHandler;
-import de.thm.arsnova.CasUserDetailsService;
-import de.thm.arsnova.LoginAuthenticationFailureHandler;
-import de.thm.arsnova.LoginAuthenticationSucessHandler;
+import de.thm.arsnova.security.CasLogoutSuccessHandler;
+import de.thm.arsnova.security.CasUserDetailsService;
+import de.thm.arsnova.security.LoginAuthenticationFailureHandler;
+import de.thm.arsnova.security.LoginAuthenticationSucessHandler;
 import de.thm.arsnova.security.ApplicationPermissionEvaluator;
 import de.thm.arsnova.security.CustomLdapUserDetailsMapper;
 import de.thm.arsnova.security.DbUserDetailsService;
@@ -364,7 +364,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
 	@Bean
 	public LogoutSuccessHandler casLogoutSuccessHandler() {
-		CASLogoutSuccessHandler handler = new CASLogoutSuccessHandler();
+		CasLogoutSuccessHandler handler = new CasLogoutSuccessHandler();
 		handler.setCasUrl(casUrl);
 		handler.setDefaultTarget(rootUrl);
 
diff --git a/src/main/java/de/thm/arsnova/controller/CommentController.java b/src/main/java/de/thm/arsnova/controller/CommentController.java
index e38c98630f2406217bcdc5f0c532f86db677b36c..c2d792f3600307853671f9d9d6eb37afce6a17f3 100644
--- a/src/main/java/de/thm/arsnova/controller/CommentController.java
+++ b/src/main/java/de/thm/arsnova/controller/CommentController.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.controller;
 import de.thm.arsnova.entities.CommentReadingCount;
 import de.thm.arsnova.entities.transport.Comment;
 import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.services.IContentService;
+import de.thm.arsnova.services.ContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -49,7 +49,7 @@ import java.util.List;
 public class CommentController extends PaginationController {
 
 	@Autowired
-	private IContentService contentService;
+	private ContentService contentService;
 
 	@ApiOperation(value = "Count all the comments in current session",
 			nickname = "getAudienceQuestionCount")
diff --git a/src/main/java/de/thm/arsnova/controller/ContentController.java b/src/main/java/de/thm/arsnova/controller/ContentController.java
index 9695d365164c78e6bff74e820cd1d12da6c4dd3d..f2c77d5e3c6b876dbe8b01114125fbfd9df1ab1a 100644
--- a/src/main/java/de/thm/arsnova/controller/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/ContentController.java
@@ -17,14 +17,14 @@
  */
 package de.thm.arsnova.controller;
 
-import de.thm.arsnova.PaginationListDecorator;
+import de.thm.arsnova.util.PaginationListDecorator;
 import de.thm.arsnova.entities.Answer;
 import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.services.IContentService;
+import de.thm.arsnova.services.ContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -54,7 +54,7 @@ import java.util.List;
 @Api(value = "/lecturerquestion", description = "Operations for Lecture Questions")
 public class ContentController extends PaginationController {
 	@Autowired
-	private IContentService contentService;
+	private ContentService contentService;
 
 	@ApiOperation(value = "Get question with provided question Id",
 			nickname = "getQuestion")
diff --git a/src/main/java/de/thm/arsnova/controller/CourseController.java b/src/main/java/de/thm/arsnova/controller/CourseController.java
index e8919bc4328b9b8f96daba68863b5770b5eac614..74e6c51485c1599e32cefe6cab31ee4ff8044bce 100644
--- a/src/main/java/de/thm/arsnova/controller/CourseController.java
+++ b/src/main/java/de/thm/arsnova/controller/CourseController.java
@@ -23,7 +23,7 @@ import de.thm.arsnova.connector.model.UserRole;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.NotImplementedException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.UserService;
 import io.swagger.annotations.ApiParam;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -46,7 +46,7 @@ public class CourseController extends AbstractController {
 	private ConnectorClient connectorClient;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@RequestMapping(value = "/mycourses", method = RequestMethod.GET)
 	public List<Course> myCourses(
diff --git a/src/main/java/de/thm/arsnova/controller/FeedbackController.java b/src/main/java/de/thm/arsnova/controller/FeedbackController.java
index 2f3e3b16c7b509ee4336f6dda7805ef240245436..000176a2114d39fe4f0ae16d709acd9e82735d37 100644
--- a/src/main/java/de/thm/arsnova/controller/FeedbackController.java
+++ b/src/main/java/de/thm/arsnova/controller/FeedbackController.java
@@ -20,8 +20,9 @@ package de.thm.arsnova.controller;
 import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.services.IFeedbackService;
-import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.FeedbackService;
+import de.thm.arsnova.services.UserService;
+import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
 import de.thm.arsnova.web.DeprecatedApi;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -36,15 +37,15 @@ import org.springframework.web.bind.annotation.RestController;
  * Handles requests concerning the user's feedback, i.e., "too fast" or "faster, please". This HTTP API is
  * deprecated in favor of the socket implementation.
  *
- * @see de.thm.arsnova.socket.ARSnovaSocketIOServer
+ * @see ArsnovaSocketioServerImpl
  */
 @RestController
 public class FeedbackController extends AbstractController {
 	@Autowired
-	private IFeedbackService feedbackService;
+	private FeedbackService feedbackService;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@DeprecatedApi
 	@Deprecated
diff --git a/src/main/java/de/thm/arsnova/controller/LegacyController.java b/src/main/java/de/thm/arsnova/controller/LegacyController.java
index a1d3a9de2a3ff25df4bba88b735f4e46853a8a94..f1966160e00150c952fda3ec9a275310ab5c911d 100644
--- a/src/main/java/de/thm/arsnova/controller/LegacyController.java
+++ b/src/main/java/de/thm/arsnova/controller/LegacyController.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.controller;
 
-import de.thm.arsnova.services.IContentService;
+import de.thm.arsnova.services.ContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -33,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
 public class LegacyController extends AbstractController {
 
 	@Autowired
-	private IContentService contentService;
+	private ContentService contentService;
 
 	/* specific routes */
 
diff --git a/src/main/java/de/thm/arsnova/controller/LoginController.java b/src/main/java/de/thm/arsnova/controller/LoginController.java
index 119e7594e516d997bf2f1f250717d6605337fac3..820bba4357a8166ff599d1c0ee574314aaba4679 100644
--- a/src/main/java/de/thm/arsnova/controller/LoginController.java
+++ b/src/main/java/de/thm/arsnova/controller/LoginController.java
@@ -21,7 +21,7 @@ import de.thm.arsnova.entities.ServiceDescription;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.UserService;
 import de.thm.arsnova.services.UserSessionService;
 import org.pac4j.core.context.J2EContext;
 import org.pac4j.core.exception.HttpAction;
@@ -142,7 +142,7 @@ public class LoginController extends AbstractController {
 	private CasAuthenticationEntryPoint casEntryPoint;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@Autowired
 	private UserSessionService userSessionService;
diff --git a/src/main/java/de/thm/arsnova/controller/MotdController.java b/src/main/java/de/thm/arsnova/controller/MotdController.java
index 9f1e5155476635107bb6a1cd114feeb03e8bac3a..c566a602ec5e82844a41d927b4ec496ab777aebf 100644
--- a/src/main/java/de/thm/arsnova/controller/MotdController.java
+++ b/src/main/java/de/thm/arsnova/controller/MotdController.java
@@ -19,7 +19,7 @@ package de.thm.arsnova.controller;
 
 import de.thm.arsnova.entities.Motd;
 import de.thm.arsnova.entities.MotdList;
-import de.thm.arsnova.services.IMotdService;
+import de.thm.arsnova.services.MotdService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -48,7 +48,7 @@ import java.util.List;
 public class MotdController extends AbstractController {
 
 	@Autowired
-	private IMotdService motdService;
+	private MotdService motdService;
 
 	@ApiOperation(value = "get messages. if adminview=false, only messages with startdate<clientdate<enddate are returned")
 	@RequestMapping(value = "/", method = RequestMethod.GET)
diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java
index 9b0dcd48a023fa8903103653c755eae633174dfc..77ee2bfac097733b74243f68ddc7ac841be92ab8 100644
--- a/src/main/java/de/thm/arsnova/controller/SessionController.java
+++ b/src/main/java/de/thm/arsnova/controller/SessionController.java
@@ -22,14 +22,14 @@ import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.SessionFeature;
 import de.thm.arsnova.entities.SessionInfo;
 import de.thm.arsnova.entities.transport.ImportExportSession;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.services.ISessionService;
-import de.thm.arsnova.services.IUserService;
-import de.thm.arsnova.services.SessionService.SessionInfoNameComparator;
-import de.thm.arsnova.services.SessionService.SessionInfoShortNameComparator;
-import de.thm.arsnova.services.SessionService.SessionNameComparator;
-import de.thm.arsnova.services.SessionService.SessionShortNameComparator;
+import de.thm.arsnova.services.SessionService;
+import de.thm.arsnova.services.UserService;
+import de.thm.arsnova.services.SessionServiceImpl.SessionInfoNameComparator;
+import de.thm.arsnova.services.SessionServiceImpl.SessionInfoShortNameComparator;
+import de.thm.arsnova.services.SessionServiceImpl.SessionNameComparator;
+import de.thm.arsnova.services.SessionServiceImpl.SessionShortNameComparator;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -61,10 +61,10 @@ import java.util.List;
 @Api(value = "/session", description = "the Session Controller API")
 public class SessionController extends PaginationController {
 	@Autowired
-	private ISessionService sessionService;
+	private SessionService sessionService;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@ApiOperation(value = "join a session",
 			nickname = "joinSession")
@@ -345,28 +345,28 @@ public class SessionController extends PaginationController {
 		return null;
 	}
 
-	@ApiOperation(value = "retrieves a value for the learning progress",
+	@ApiOperation(value = "retrieves a value for the score",
 			nickname = "getLearningProgress")
 	@RequestMapping(value = "/{sessionkey}/learningprogress", method = RequestMethod.GET)
-	public LearningProgressValues getLearningProgress(
+	public ScoreStatistics getLearningProgress(
 			@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
-			@ApiParam(value = "progress type", required = false) @RequestParam(value = "type", defaultValue = "questions") final String progressType,
+			@ApiParam(value = "type", required = false) @RequestParam(value = "type", defaultValue = "questions") final String type,
 			@ApiParam(value = "question variant", required = false) @RequestParam(value = "questionVariant", required = false) final String questionVariant,
 			final HttpServletResponse response
 			) {
-		return sessionService.getLearningProgress(sessionkey, progressType, questionVariant);
+		return sessionService.getLearningProgress(sessionkey, type, questionVariant);
 	}
 
 	@ApiOperation(value = "retrieves a value for the learning progress for the current user",
 			nickname = "getMyLearningProgress")
 	@RequestMapping(value = "/{sessionkey}/mylearningprogress", method = RequestMethod.GET)
-	public LearningProgressValues getMyLearningProgress(
+	public ScoreStatistics getMyLearningProgress(
 			@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
-			@RequestParam(value = "type", defaultValue = "questions") final String progressType,
+			@RequestParam(value = "type", defaultValue = "questions") final String type,
 			@RequestParam(value = "questionVariant", required = false) final String questionVariant,
 			final HttpServletResponse response
 			) {
-		return sessionService.getMyLearningProgress(sessionkey, progressType, questionVariant);
+		return sessionService.getMyLearningProgress(sessionkey, type, questionVariant);
 	}
 
 	@ApiOperation(value = "retrieves all session features",
diff --git a/src/main/java/de/thm/arsnova/controller/SocketController.java b/src/main/java/de/thm/arsnova/controller/SocketController.java
index 685d1ff7dccd6432aff1348e63c924ee76ef5576..0fa66e39a45f072029716738f7a39ecd64ff461f 100644
--- a/src/main/java/de/thm/arsnova/controller/SocketController.java
+++ b/src/main/java/de/thm/arsnova/controller/SocketController.java
@@ -18,9 +18,9 @@
 package de.thm.arsnova.controller;
 
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.UserService;
 import de.thm.arsnova.services.UserSessionService;
-import de.thm.arsnova.socket.ARSnovaSocket;
+import de.thm.arsnova.websocket.ArsnovaSocketioServer;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -49,13 +49,13 @@ import java.util.UUID;
 public class SocketController extends AbstractController {
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@Autowired
 	private UserSessionService userSessionService;
 
 	@Autowired
-	private ARSnovaSocket server;
+	private ArsnovaSocketioServer server;
 
 	private static final Logger logger = LoggerFactory.getLogger(SocketController.class);
 
diff --git a/src/main/java/de/thm/arsnova/controller/StatisticsController.java b/src/main/java/de/thm/arsnova/controller/StatisticsController.java
index a82e575357d426d07aa41c7509d207a4db02d212..baedcf0aed2f0e6e7fc36d3902f94156ff1d6d77 100644
--- a/src/main/java/de/thm/arsnova/controller/StatisticsController.java
+++ b/src/main/java/de/thm/arsnova/controller/StatisticsController.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.controller;
 
 import de.thm.arsnova.entities.Statistics;
-import de.thm.arsnova.services.IStatisticsService;
+import de.thm.arsnova.services.StatisticsService;
 import de.thm.arsnova.web.CacheControl;
 import de.thm.arsnova.web.DeprecatedApi;
 import io.swagger.annotations.Api;
@@ -36,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController;
 public class StatisticsController extends AbstractController {
 
 	@Autowired
-	private IStatisticsService statisticsService;
+	private StatisticsService statisticsService;
 
 	@ApiOperation(value = "Retrieves global statistics",
 			nickname = "getStatistics")
diff --git a/src/main/java/de/thm/arsnova/controller/UserController.java b/src/main/java/de/thm/arsnova/controller/UserController.java
index df9237599b782733210c06f11fb39edb4c9ec10c..e9b2d5c251f8e95f4a256b83e1344e5d4fb3f087 100644
--- a/src/main/java/de/thm/arsnova/controller/UserController.java
+++ b/src/main/java/de/thm/arsnova/controller/UserController.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.controller;
 
 import de.thm.arsnova.entities.DbUser;
-import de.thm.arsnova.services.IUserService;
+import de.thm.arsnova.services.UserService;
 import de.thm.arsnova.services.UserSessionService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -41,7 +41,7 @@ public class UserController extends AbstractController {
 	private DaoAuthenticationProvider daoProvider;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@Autowired
 	private UserSessionService userSessionService;
diff --git a/src/main/java/de/thm/arsnova/domain/package-info.java b/src/main/java/de/thm/arsnova/domain/package-info.java
deleted file mode 100644
index 4995c742eaa8e7cb5f67adfe4ece28bf18ce9fcc..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/domain/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * The 'M' in MVC
- */
-package de.thm.arsnova.domain;
diff --git a/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java b/src/main/java/de/thm/arsnova/entities/ScoreOptions.java
similarity index 74%
rename from src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java
rename to src/main/java/de/thm/arsnova/entities/ScoreOptions.java
index 342d2c29ae9fda8142356792dadd863211d1b9ee..1715f484e7dd2d2e868dd01199c46071d994e8b0 100644
--- a/src/main/java/de/thm/arsnova/entities/LearningProgressOptions.java
+++ b/src/main/java/de/thm/arsnova/entities/ScoreOptions.java
@@ -25,22 +25,22 @@ import io.swagger.annotations.ApiModelProperty;
 import java.io.Serializable;
 
 /**
- * A session's settings regarding the calculation of the learning progress.
+ * A session's settings regarding the calculation of the score.
  */
-@ApiModel(value = "learning progress options", description = "the learning progress entity")
-public class LearningProgressOptions implements Serializable {
+@ApiModel(value = "score options", description = "the score entity")
+public class ScoreOptions implements Serializable {
 
 	private String type = "questions";
 
 	private String questionVariant = "";
 
-	public LearningProgressOptions(LearningProgressOptions learningProgressOptions) {
+	public ScoreOptions(ScoreOptions scoreOptions) {
 		this();
-		this.type = learningProgressOptions.getType();
-		this.questionVariant = learningProgressOptions.getQuestionVariant();
+		this.type = scoreOptions.getType();
+		this.questionVariant = scoreOptions.getQuestionVariant();
 	}
 
-	public LearningProgressOptions() { }
+	public ScoreOptions() { }
 
 	@ApiModelProperty(required = true, value = "the type")
 	@JsonView({View.Persistence.class, View.Public.class})
@@ -49,8 +49,8 @@ public class LearningProgressOptions implements Serializable {
 	}
 
 	@JsonView({View.Persistence.class, View.Public.class})
-	public void setType(String learningProgressType) {
-		this.type = learningProgressType;
+	public void setType(String type) {
+		this.type = type;
 	}
 
 	@ApiModelProperty(required = true, value = "either lecture or preparation")
diff --git a/src/main/java/de/thm/arsnova/entities/Session.java b/src/main/java/de/thm/arsnova/entities/Session.java
index 65bf6c6263075a06a9f40260e202ea676a88dea1..eeb54b5f48d38c1a28924df8cd495919997933ad 100644
--- a/src/main/java/de/thm/arsnova/entities/Session.java
+++ b/src/main/java/de/thm/arsnova/entities/Session.java
@@ -39,7 +39,7 @@ public class Session implements Entity {
 	private String courseType;
 	private String courseId;
 	private long creationTime;
-	private LearningProgressOptions learningProgressOptions = new LearningProgressOptions();
+	private ScoreOptions learningProgressOptions = new ScoreOptions();
 	private SessionFeature features = new SessionFeature();
 
 	private String ppAuthorName;
@@ -71,7 +71,7 @@ public class Session implements Entity {
 		copy.courseType = original.courseType;
 		copy.courseId = original.courseId;
 		copy.creationTime = original.creationTime;
-		copy.learningProgressOptions = new LearningProgressOptions(original.learningProgressOptions);
+		copy.learningProgressOptions = new ScoreOptions(original.learningProgressOptions);
 		copy.features = new SessionFeature(original.features);
 		// public pool
 		copy.ppAuthorName = original.ppAuthorName;
@@ -220,14 +220,14 @@ public class Session implements Entity {
 		this.creationTime = creationTime;
 	}
 
-	@ApiModelProperty(required = true, value = "the learning progress options")
+	@ApiModelProperty(required = true, value = "the score options")
 	@JsonView({View.Persistence.class, View.Public.class})
-	public LearningProgressOptions getLearningProgressOptions() {
+	public ScoreOptions getLearningProgressOptions() {
 		return learningProgressOptions;
 	}
 
 	@JsonView({View.Persistence.class, View.Public.class})
-	public void setLearningProgressOptions(LearningProgressOptions learningProgressOptions) {
+	public void setLearningProgressOptions(ScoreOptions learningProgressOptions) {
 		this.learningProgressOptions = learningProgressOptions;
 	}
 
diff --git a/src/main/java/de/thm/arsnova/entities/SessionFeature.java b/src/main/java/de/thm/arsnova/entities/SessionFeature.java
index e04390add673beb3c89c54e8a85bde3cd7d1dc2e..618727cd6098d5130e7810aecc227c2071739d9f 100644
--- a/src/main/java/de/thm/arsnova/entities/SessionFeature.java
+++ b/src/main/java/de/thm/arsnova/entities/SessionFeature.java
@@ -128,7 +128,7 @@ public class SessionFeature implements Serializable {
 		this.pi = pi;
 	}
 
-	@ApiModelProperty(required = true, value = "learning progress")
+	@ApiModelProperty(required = true, value = "score")
 	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isLearningProgress() {
 		return learningProgress;
diff --git a/src/main/java/de/thm/arsnova/entities/transport/Answer.java b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
index de074d3e1cc30029d1ee9a60c315aa69319931fc..02e1f2fe4b40524b9f171960635c4ca038ff1d34 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/Answer.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
@@ -139,7 +139,7 @@ public class Answer implements Serializable {
 		theAnswer.setTimestamp(new Date().getTime());
 		theAnswer.setQuestionVariant(content.getQuestionVariant());
 		theAnswer.setAbstention(this.isAbstention());
-		// calculate learning progress value after all properties are set
+		// calculate score value after all properties are set
 		theAnswer.setQuestionValue(content.calculateValue(theAnswer));
 		theAnswer.setAnswerImage(this.getAnswerImage());
 		theAnswer.setSuccessfulFreeTextAnswer(this.isSuccessfulFreeTextAnswer());
diff --git a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressOptions.java b/src/main/java/de/thm/arsnova/entities/transport/ScoreOptions.java
similarity index 78%
rename from src/main/java/de/thm/arsnova/entities/transport/LearningProgressOptions.java
rename to src/main/java/de/thm/arsnova/entities/transport/ScoreOptions.java
index 2bdc44886d518d97bed200f54246369148e1e257..5bf0b49a82d0cd04c358a40a96a129e1ad602f6e 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressOptions.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/ScoreOptions.java
@@ -18,9 +18,9 @@
 package de.thm.arsnova.entities.transport;
 
 /**
- * A session's settings regarding the calculation of the learning progress.
+ * A session's settings regarding the calculation of the score.
  */
-public class LearningProgressOptions {
+public class ScoreOptions {
 
 	private String sessionKeyword;
 
@@ -40,8 +40,8 @@ public class LearningProgressOptions {
 		return type;
 	}
 
-	public void setType(String learningProgressType) {
-		this.type = learningProgressType;
+	public void setType(String type) {
+		this.type = type;
 	}
 
 	public String getQuestionVariant() {
@@ -52,8 +52,8 @@ public class LearningProgressOptions {
 		this.questionVariant = questionVariant;
 	}
 
-	public de.thm.arsnova.entities.LearningProgressOptions toEntity() {
-		de.thm.arsnova.entities.LearningProgressOptions entity = new de.thm.arsnova.entities.LearningProgressOptions();
+	public de.thm.arsnova.entities.ScoreOptions toEntity() {
+		de.thm.arsnova.entities.ScoreOptions entity = new de.thm.arsnova.entities.ScoreOptions();
 		entity.setType(this.getType());
 		entity.setQuestionVariant(this.getQuestionVariant());
 		return entity;
diff --git a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java b/src/main/java/de/thm/arsnova/entities/transport/ScoreStatistics.java
similarity index 93%
rename from src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java
rename to src/main/java/de/thm/arsnova/entities/transport/ScoreStatistics.java
index 686a733d09775ba257e97c8a11970bb105942ba4..1bb9af1a69c41ddc2bd6551611aa6b9d16808933 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/LearningProgressValues.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/ScoreStatistics.java
@@ -23,10 +23,10 @@ import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
 /**
- * The calculated learning progress along with meta-data.
+ * The calculated score along with meta-data.
  */
-@ApiModel(value = "session/{sessionkey}/learningprogress", description = "the learning progress API")
-public class LearningProgressValues {
+@ApiModel(value = "session/{sessionkey}/learningprogress", description = "the score API")
+public class ScoreStatistics {
 
 	private int courseProgress;
 
@@ -40,7 +40,7 @@ public class LearningProgressValues {
 
 	private int numUsers;
 
-	@ApiModelProperty(required = true, value = "used to display course progress")
+	@ApiModelProperty(required = true, value = "used to display course score")
 	@JsonView(View.Public.class)
 	public int getCourseProgress() {
 		return courseProgress;
@@ -50,7 +50,7 @@ public class LearningProgressValues {
 		this.courseProgress = courseProgress;
 	}
 
-	@ApiModelProperty(required = true, value = "used to display my progress")
+	@ApiModelProperty(required = true, value = "used to display my score")
 	@JsonView(View.Public.class)
 	public int getMyProgress() {
 		return myProgress;
@@ -121,7 +121,7 @@ public class LearningProgressValues {
 		if (getClass() != obj.getClass()) {
 			return false;
 		}
-		LearningProgressValues other = (LearningProgressValues) obj;
+		ScoreStatistics other = (ScoreStatistics) obj;
 		if (courseProgress != other.courseProgress) {
 			return false;
 		}
diff --git a/src/main/java/de/thm/arsnova/events/NovaEvent.java b/src/main/java/de/thm/arsnova/events/ArsnovaEvent.java
similarity index 85%
rename from src/main/java/de/thm/arsnova/events/NovaEvent.java
rename to src/main/java/de/thm/arsnova/events/ArsnovaEvent.java
index 818113b284d112345af39eecefb27bb0ec241562..a647eeb35426da6341e84366958e98a7139bc13c 100644
--- a/src/main/java/de/thm/arsnova/events/NovaEvent.java
+++ b/src/main/java/de/thm/arsnova/events/ArsnovaEvent.java
@@ -22,14 +22,14 @@ import org.springframework.context.ApplicationEvent;
 /**
  * Base class of an ARSnova event.
  */
-public abstract class NovaEvent extends ApplicationEvent {
+public abstract class ArsnovaEvent extends ApplicationEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	public NovaEvent(Object source) {
+	public ArsnovaEvent(Object source) {
 		super(source);
 	}
 
-	public abstract void accept(NovaEventVisitor visitor);
+	public abstract void accept(ArsnovaEventVisitor visitor);
 
 }
diff --git a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java b/src/main/java/de/thm/arsnova/events/ArsnovaEventVisitor.java
similarity index 96%
rename from src/main/java/de/thm/arsnova/events/NovaEventVisitor.java
rename to src/main/java/de/thm/arsnova/events/ArsnovaEventVisitor.java
index f02c32154ca8897d5f95251bb59a895387b72c76..6e09b1a9c9f4cf4bd10b2c90492c65b9a48ff076 100644
--- a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java
+++ b/src/main/java/de/thm/arsnova/events/ArsnovaEventVisitor.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.events;
 /**
  * Listeners wanting to receive ARSnova's internal events should implement this interface.
  */
-public interface NovaEventVisitor {
+public interface ArsnovaEventVisitor {
 
 	void visit(NewCommentEvent newCommentEvent);
 
@@ -56,7 +56,7 @@ public interface NovaEventVisitor {
 
 	void visit(StatusSessionEvent statusSessionEvent);
 
-	void visit(ChangeLearningProgressEvent changeLearningProgress);
+	void visit(ChangeScoreEvent changeLearningProgress);
 
 	void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent);
 
diff --git a/src/main/java/de/thm/arsnova/events/ChangeLearningProgressEvent.java b/src/main/java/de/thm/arsnova/events/ChangeScoreEvent.java
similarity index 79%
rename from src/main/java/de/thm/arsnova/events/ChangeLearningProgressEvent.java
rename to src/main/java/de/thm/arsnova/events/ChangeScoreEvent.java
index 60c104ab2cf1cab6f63e57e61aaec791ea3445cd..ef8e5f94446fd0d9d4c1f88b8e69d71b6fbee6df 100644
--- a/src/main/java/de/thm/arsnova/events/ChangeLearningProgressEvent.java
+++ b/src/main/java/de/thm/arsnova/events/ChangeScoreEvent.java
@@ -20,18 +20,18 @@ package de.thm.arsnova.events;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a learning progress related value changes.
+ * Fires whenever a score related value changes.
  */
-public class ChangeLearningProgressEvent extends SessionEvent {
+public class ChangeScoreEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	public ChangeLearningProgressEvent(Object source, Session session) {
+	public ChangeScoreEvent(Object source, Session session) {
 		super(source, session);
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAllLectureAnswersEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAllLectureAnswersEvent.java
index 14848eec737fb72b827e00a2845c8205877469bf..48825596351c5abc5ed24c3b0812dc6434c61280 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAllLectureAnswersEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAllLectureAnswersEvent.java
@@ -31,7 +31,7 @@ public class DeleteAllLectureAnswersEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAllPreparationAnswersEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAllPreparationAnswersEvent.java
index 1321bc33f9eca71b93e7506b43582e26a7afdcd2..df3b433d12dacc17cce90b1407982f0c41098529 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAllPreparationAnswersEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAllPreparationAnswersEvent.java
@@ -31,7 +31,7 @@ public class DeleteAllPreparationAnswersEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsAnswersEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsAnswersEvent.java
index 7c772bc4676d8d8f990ce5839ef946bf1bebced2..0fbfe9639120099e229830965e8a65a48004b446 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsAnswersEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsAnswersEvent.java
@@ -31,7 +31,7 @@ public class DeleteAllQuestionsAnswersEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsEvent.java
index 0ee77d87ca63cb691c06b723c0e83440de3d6ce5..8eb0c43cac4f6fc737ece93467bb1ce0208d7715 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAllQuestionsEvent.java
@@ -32,7 +32,7 @@ public class DeleteAllQuestionsEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
index 55970e207457824eff5d3ba23772ed3e24ad4e68..b47ae62d191ac285a0c8837cfdff9e29103e559b 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
@@ -35,7 +35,7 @@ public class DeleteAnswerEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java b/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
index a22b545aa4a81d7f0e80d9059531537943f3a3fd..54541263244339fa0482d997bb2769035ee452f9 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteCommentEvent.java
@@ -35,7 +35,7 @@ public class DeleteCommentEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteFeedbackForSessionsEvent.java b/src/main/java/de/thm/arsnova/events/DeleteFeedbackForSessionsEvent.java
index a416b205d9eb714f2d55d183a4930a9a7c1e1332..f8f3f49944be61bfeb7695d5d354242d40991de2 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteFeedbackForSessionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteFeedbackForSessionsEvent.java
@@ -25,7 +25,7 @@ import java.util.Set;
 /**
  * Fires whenever the feedback of a specific user has been reset.
  */
-public class DeleteFeedbackForSessionsEvent extends NovaEvent {
+public class DeleteFeedbackForSessionsEvent extends ArsnovaEvent {
 
 	private static final long serialVersionUID = 1L;
 
@@ -48,7 +48,7 @@ public class DeleteFeedbackForSessionsEvent extends NovaEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
index 0204ee5b7d5ca8bdd101a0cf41ddb7c005da0800..e2c1d7eb56d6d4d6ae42cc4ec5573ffee8fb7ed4 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
@@ -39,7 +39,7 @@ public class DeleteQuestionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
index 3bffe73bb4639cc8c8509a3fad8fabf74dada234..94f621e1aa063c2a5d0fac2c6180f6ae852303b4 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteSessionEvent.java
@@ -32,7 +32,7 @@ public class DeleteSessionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/FeatureChangeEvent.java b/src/main/java/de/thm/arsnova/events/FeatureChangeEvent.java
index 40e08462519cc183a18b7ad108327312f3b387a8..f8b062fa4bd281ec62f54f2597e2a018ec736df0 100644
--- a/src/main/java/de/thm/arsnova/events/FeatureChangeEvent.java
+++ b/src/main/java/de/thm/arsnova/events/FeatureChangeEvent.java
@@ -31,7 +31,7 @@ public class FeatureChangeEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/FlipFlashcardsEvent.java b/src/main/java/de/thm/arsnova/events/FlipFlashcardsEvent.java
index 78d7688fbf0fe22648fc3bb83fe00b89ee141be6..023378e3500b5bba827461fac69da2a8debe445f 100644
--- a/src/main/java/de/thm/arsnova/events/FlipFlashcardsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/FlipFlashcardsEvent.java
@@ -31,7 +31,7 @@ public class FlipFlashcardsEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/LockFeedbackEvent.java b/src/main/java/de/thm/arsnova/events/LockFeedbackEvent.java
index c6330eb6e78b18e4d17f34c0a9df9a2b8dc6c4d4..fa19651f6999f68fede3f104a08b476d10731e33 100644
--- a/src/main/java/de/thm/arsnova/events/LockFeedbackEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockFeedbackEvent.java
@@ -31,7 +31,7 @@ public class LockFeedbackEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
index 8371c8b01c59573a2f7b6320421623a1dba4ff26..d9b3a11b3dd5d49d57b8431b436bf12fa2280512 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
@@ -39,7 +39,7 @@ public class LockQuestionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
index 2981f4e18f5610a5799167146c1036e0cb286be2..b5e792edeb26ae737a86648033e4764cdafdba3f 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
@@ -41,7 +41,7 @@ public class LockQuestionsEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
index e3e9f000fd8fafa074572445ce493510a1a06d68..e18457bd036c8cfb418f138eef5f9e6a1053f78c 100644
--- a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
@@ -58,7 +58,7 @@ public class LockVoteEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
index 6f8043c18ae279962546d180e4191bde2d5e193e..5a08c87a3e8f1dc67cd49ac460ec1cf98acd7cf3 100644
--- a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
@@ -41,7 +41,7 @@ public class LockVotesEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
index 9172a5b5a5b307be3ed77a881ee0a868872674f7..afb0e6a703c47477a46968f0386c10a47f439a28 100644
--- a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
@@ -43,7 +43,7 @@ public class NewAnswerEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/NewCommentEvent.java b/src/main/java/de/thm/arsnova/events/NewCommentEvent.java
index 8330cbcd5b40c4d6ecf4b7cd2a1ad3c2e0dd0865..8b205b4017d84083c1bb63319c3aa64018163ba5 100644
--- a/src/main/java/de/thm/arsnova/events/NewCommentEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewCommentEvent.java
@@ -39,7 +39,7 @@ public class NewCommentEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/NewFeedbackEvent.java b/src/main/java/de/thm/arsnova/events/NewFeedbackEvent.java
index f4a88dbf8c8474a56386850738ed112ca3f6a494..10cd6fe9be5fc78e9364b97e1486e4b6f8cfa80a 100644
--- a/src/main/java/de/thm/arsnova/events/NewFeedbackEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewFeedbackEvent.java
@@ -31,7 +31,7 @@ public class NewFeedbackEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
index 1a9e1616ec35819e5825b14b6ad1fb5609e82f92..07e93e03e98764488becc8a91924a4cc968c9a88 100644
--- a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
@@ -39,7 +39,7 @@ public class NewQuestionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/NewSessionEvent.java b/src/main/java/de/thm/arsnova/events/NewSessionEvent.java
index ca8115e501c6df0f639f5fb11e628a2443a49311..5bb2893848e1717c5f3471528b9eea4e0d7618bc 100644
--- a/src/main/java/de/thm/arsnova/events/NewSessionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewSessionEvent.java
@@ -31,7 +31,7 @@ public class NewSessionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
index 64b51aea8780c1a4d5b68efd667b21f5eecc3e4d..6833d7a03f980e27db8f592d4b5ce7a8e5bf419f 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
@@ -32,7 +32,7 @@ public class PiRoundCancelEvent extends PiRoundEndEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
index dfaed2bb4fbf87b73fc27b9dae220de29b3bf29f..c9979950295be97c7d1887cdb896fcd77ea3a50d 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
@@ -46,7 +46,7 @@ public class PiRoundDelayedStartEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
index 335345185364f88e718c0f19d4b50c55520390f4..c6d6c240cc65af9924aee42ce3e4f3e633118a4b 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
@@ -40,7 +40,7 @@ public class PiRoundEndEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
index 96bd50df0d30637ea3c4fc3b87c65cba5b0bee70..0182fec221dbb085282169e0e1b533785898062d 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
@@ -40,7 +40,7 @@ public class PiRoundResetEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/SessionEvent.java b/src/main/java/de/thm/arsnova/events/SessionEvent.java
index 00ea28c423c6ae48e12f19a58c486a4df970d6e9..66200d7de2b3cbfb9c6c824e84581792583ef61c 100644
--- a/src/main/java/de/thm/arsnova/events/SessionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/SessionEvent.java
@@ -20,9 +20,9 @@ package de.thm.arsnova.events;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Base class for all {@link NovaEvent}s that are related to a session.
+ * Base class for all {@link ArsnovaEvent}s that are related to a session.
  */
-public abstract class SessionEvent extends NovaEvent {
+public abstract class SessionEvent extends ArsnovaEvent {
 
 	private static final long serialVersionUID = 1L;
 
diff --git a/src/main/java/de/thm/arsnova/events/StatusSessionEvent.java b/src/main/java/de/thm/arsnova/events/StatusSessionEvent.java
index 32c575dad0272c8c6da408f7b7e55737b6aa414f..751aebee8f037ebf959541c75626a19091911309 100644
--- a/src/main/java/de/thm/arsnova/events/StatusSessionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/StatusSessionEvent.java
@@ -31,7 +31,7 @@ public class StatusSessionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
index 1d8e24c61ae3661a45b6d42b3f92d464df9f71e2..8401e08453f7f963d533713a99af2207d2625d10 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
@@ -39,7 +39,7 @@ public class UnlockQuestionEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
index 153b702a6fbdd1177eb6fbb5fa920c8d99319215..72fbe8e98138bb4b224309de3856044fc4d821ea 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
@@ -41,7 +41,7 @@ public class UnlockQuestionsEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
index 00bf47a4d8781e8368cc33fcd14a0c1f122314b3..5ec26409244e2bf2c71d876506fe4edb17c99249 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
@@ -58,7 +58,7 @@ public class UnlockVoteEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
index 2dd3d4671849ac43cccf7a457ddc2e43a63feec7..1f834846fab6a58538b32061aa7d54cddb5e95d9 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
@@ -41,7 +41,7 @@ public class UnlockVotesEvent extends SessionEvent {
 	}
 
 	@Override
-	public void accept(NovaEventVisitor visitor) {
+	public void accept(ArsnovaEventVisitor visitor) {
 		visitor.visit(this);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java
index 193da0333f4aaf143065f69a12eaef674b1a75c3..5791280415b3c7379780d03c7799eb5c2892b6ad 100644
--- a/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java
+++ b/src/main/java/de/thm/arsnova/persistance/SessionStatisticsRepository.java
@@ -1,8 +1,8 @@
 package de.thm.arsnova.persistance;
 
-import de.thm.arsnova.domain.CourseScore;
+import de.thm.arsnova.services.score.Score;
 import de.thm.arsnova.entities.Session;
 
 public interface SessionStatisticsRepository {
-	CourseScore getLearningProgress(Session session);
+	Score getLearningProgress(Session session);
 }
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java
index b0ee9bf42706d0e06edc596c771f3d885d85ba76..e3df5f055a1c1cf6781acbb0bdb92efdaf398a5e 100644
--- a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbMotdRepository.java
@@ -19,7 +19,7 @@ package de.thm.arsnova.persistance.couchdb;
 
 import de.thm.arsnova.entities.Motd;
 import de.thm.arsnova.persistance.MotdRepository;
-import de.thm.arsnova.services.ISessionService;
+import de.thm.arsnova.services.SessionService;
 import org.ektorp.CouchDbConnector;
 import org.ektorp.support.CouchDbRepositorySupport;
 import org.slf4j.Logger;
@@ -35,7 +35,7 @@ public class CouchDbMotdRepository extends CouchDbRepositorySupport<Motd> implem
 	private static final Logger logger = LoggerFactory.getLogger(CouchDbMotdRepository.class);
 
 	@Autowired
-	private ISessionService sessionService;
+	private SessionService sessionService;
 
 	public CouchDbMotdRepository(CouchDbConnector db, boolean createIfNotExists) {
 		super(Motd.class, db, createIfNotExists);
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
index 2731d70a0f7b9fc2b88a02cac1cc0b9a691a82af..8a00fddfe46ee1454d625a375c8a6dde4e1590fb 100644
--- a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
@@ -29,7 +29,7 @@ import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.persistance.LogEntryRepository;
 import de.thm.arsnova.persistance.MotdRepository;
 import de.thm.arsnova.persistance.SessionRepository;
-import de.thm.arsnova.services.ISessionService;
+import de.thm.arsnova.services.SessionService;
 import org.ektorp.ComplexKey;
 import org.ektorp.CouchDbConnector;
 import org.ektorp.DocumentNotFoundException;
@@ -59,7 +59,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 	private static final Logger logger = LoggerFactory.getLogger(CouchDbSessionRepository.class);
 
 	@Autowired
-	private ISessionService sessionService;
+	private SessionService sessionService;
 
 	@Autowired
 	private LogEntryRepository dbLogger;
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java
index 0d7bbcb1dc115ee8a02f3ba484d074d9c0e1fefe..f98ea9e1a00b635553ad125081006da8830a49a4 100644
--- a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionStatisticsRepository.java
@@ -1,7 +1,7 @@
 package de.thm.arsnova.persistance.couchdb;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import de.thm.arsnova.domain.CourseScore;
+import de.thm.arsnova.services.score.Score;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
 import org.ektorp.ComplexKey;
@@ -17,14 +17,14 @@ public class CouchDbSessionStatisticsRepository extends CouchDbRepositorySupport
 
 	@Cacheable("learningprogress")
 	@Override
-	public CourseScore getLearningProgress(final Session session) {
+	public Score getLearningProgress(final Session session) {
 		final ViewResult maximumValueResult = db.queryView(createQuery("maximum_value_of_question")
 				.startKey(ComplexKey.of(session.getId()))
 				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
 		final ViewResult answerSumResult = db.queryView(createQuery("question_value_achieved_for_user")
 				.startKey(ComplexKey.of(session.getId()))
 				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
-		final CourseScore courseScore = new CourseScore();
+		final Score courseScore = new Score();
 
 		// no results found
 		if (maximumValueResult.isEmpty() && answerSumResult.isEmpty()) {
diff --git a/src/main/java/de/thm/arsnova/CASLogoutSuccessHandler.java b/src/main/java/de/thm/arsnova/security/CasLogoutSuccessHandler.java
similarity index 92%
rename from src/main/java/de/thm/arsnova/CASLogoutSuccessHandler.java
rename to src/main/java/de/thm/arsnova/security/CasLogoutSuccessHandler.java
index 13eb9343443ed2cd00203d11f3d3c2d0562761b8..dbb451a6f651b28ff36f1346ff230797a59a1ddc 100644
--- a/src/main/java/de/thm/arsnova/CASLogoutSuccessHandler.java
+++ b/src/main/java/de/thm/arsnova/security/CasLogoutSuccessHandler.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.security;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,9 +32,9 @@ import java.io.IOException;
 /**
  * This class gets called when a user has been successfully logged out from CAS.
  */
-public class CASLogoutSuccessHandler implements LogoutSuccessHandler {
+public class CasLogoutSuccessHandler implements LogoutSuccessHandler {
 
-	private static final Logger logger = LoggerFactory.getLogger(CASLogoutSuccessHandler.class);
+	private static final Logger logger = LoggerFactory.getLogger(CasLogoutSuccessHandler.class);
 
 	private String casUrl;
 	private String defaultTarget;
diff --git a/src/main/java/de/thm/arsnova/CasUserDetailsService.java b/src/main/java/de/thm/arsnova/security/CasUserDetailsService.java
similarity index 98%
rename from src/main/java/de/thm/arsnova/CasUserDetailsService.java
rename to src/main/java/de/thm/arsnova/security/CasUserDetailsService.java
index 9261648fb99860ff9a37c1509dbf4e427dd2e50c..05bc988df36b482fad37dea5616a52561e81a23b 100644
--- a/src/main/java/de/thm/arsnova/CasUserDetailsService.java
+++ b/src/main/java/de/thm/arsnova/security/CasUserDetailsService.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.security;
 
 import org.jasig.cas.client.validation.Assertion;
 import org.springframework.security.cas.userdetails.AbstractCasAssertionUserDetailsService;
diff --git a/src/main/java/de/thm/arsnova/LoginAuthenticationFailureHandler.java b/src/main/java/de/thm/arsnova/security/LoginAuthenticationFailureHandler.java
similarity index 98%
rename from src/main/java/de/thm/arsnova/LoginAuthenticationFailureHandler.java
rename to src/main/java/de/thm/arsnova/security/LoginAuthenticationFailureHandler.java
index f3f4298751c83d7fa410272ff28bb3a729216453..3a4e1c04d0879c2a2da2f5b6f3ffd4a5427c079e 100644
--- a/src/main/java/de/thm/arsnova/LoginAuthenticationFailureHandler.java
+++ b/src/main/java/de/thm/arsnova/security/LoginAuthenticationFailureHandler.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.security;
 
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.DefaultRedirectStrategy;
diff --git a/src/main/java/de/thm/arsnova/LoginAuthenticationSucessHandler.java b/src/main/java/de/thm/arsnova/security/LoginAuthenticationSucessHandler.java
similarity index 97%
rename from src/main/java/de/thm/arsnova/LoginAuthenticationSucessHandler.java
rename to src/main/java/de/thm/arsnova/security/LoginAuthenticationSucessHandler.java
index 714578ee6ef33665a588ae9443b495da91a8c2d3..b78b7462748120230c25d7d252413d7faa2561f6 100644
--- a/src/main/java/de/thm/arsnova/LoginAuthenticationSucessHandler.java
+++ b/src/main/java/de/thm/arsnova/security/LoginAuthenticationSucessHandler.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.security;
 
 import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
 
diff --git a/src/main/java/de/thm/arsnova/services/ContentService.java b/src/main/java/de/thm/arsnova/services/ContentService.java
index 2191d8cf7d1216bd621fbe8bb1ad194f8a0bffb9..9e861af00ce2d131ecf5b9fb7e07ce0a237cd78f 100644
--- a/src/main/java/de/thm/arsnova/services/ContentService.java
+++ b/src/main/java/de/thm/arsnova/services/ContentService.java
@@ -17,1025 +17,155 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.ImageUtils;
 import de.thm.arsnova.entities.Answer;
 import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.CommentReadingCount;
 import de.thm.arsnova.entities.Content;
-import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.events.*;
-import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.exceptions.ForbiddenException;
-import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.persistance.AnswerRepository;
-import de.thm.arsnova.persistance.CommentRepository;
-import de.thm.arsnova.persistance.ContentRepository;
-import de.thm.arsnova.persistance.SessionRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
+
 import java.util.List;
 import java.util.Map;
-import java.util.Timer;
-import java.util.TimerTask;
 
 /**
- * Performs all question, comment, and answer related operations.
+ * The functionality the question service should provide.
  */
-@Service
-public class ContentService implements IContentService, ApplicationEventPublisherAware {
-	@Autowired
-	private IUserService userService;
-
-	@Autowired
-	private SessionRepository sessionRepository;
-
-	@Autowired
-	private CommentRepository commentRepository;
-
-	@Autowired
-	private ContentRepository contentRepository;
-
-	@Autowired
-	private AnswerRepository answerRepository;
-
-	@Autowired
-	private ImageUtils imageUtils;
-
-	@Value("${upload.filesize_b}")
-	private int uploadFileSizeByte;
-
-	private ApplicationEventPublisher publisher;
-
-	private static final Logger logger = LoggerFactory.getLogger(ContentService.class);
-
-	private HashMap<String, Timer> timerList = new HashMap<>();
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Content> getSkillQuestions(final String sessionkey) {
-		final Session session = getSession(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (session.isCreator(user)) {
-			return contentRepository.getSkillQuestionsForTeachers(session);
-		} else {
-			return contentRepository.getSkillQuestionsForUsers(session);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getSkillQuestionCount(final String sessionkey) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		return contentRepository.getSkillQuestionCount(session);
-	}
-
-	/* FIXME: #content.getSessionKeyword() cannot be checked since keyword is no longer set for content. */
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#content.getSessionKeyword(), 'session', 'owner')")
-	public Content saveQuestion(final Content content) {
-		final Session session = sessionRepository.getSessionFromKeyword(content.getSessionKeyword());
-		content.setSessionId(session.getId());
-		content.setTimestamp(System.currentTimeMillis() / 1000L);
-
-		if ("freetext".equals(content.getQuestionType())) {
-			content.setPiRound(0);
-		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
-			content.setPiRound(1);
-		}
-
-		// convert imageurl to base64 if neccessary
-		if ("grid".equals(content.getQuestionType()) && !content.getImage().startsWith("http")) {
-			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
-			final int fileSize = (int) ((content.getImage().length() - 814) / 1.37);
-			if (fileSize > uploadFileSizeByte) {
-				logger.error("Could not save file. File is too large with {} Byte.", fileSize);
-				throw new BadRequestException();
-			}
-		}
-
-		final Content result = contentRepository.saveQuestion(session, content);
-
-		final NewQuestionEvent event = new NewQuestionEvent(this, session, result);
-		this.publisher.publishEvent(event);
-
-		return result;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public boolean saveQuestion(final Comment comment) {
-		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
-		final Comment result = commentRepository.saveQuestion(session, comment, userService.getCurrentUser());
-
-		if (null != result) {
-			final NewCommentEvent event = new NewCommentEvent(this, session, result);
-			this.publisher.publishEvent(event);
-			return true;
-		}
-		return false;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Content getQuestion(final String id) {
-		final Content result = contentRepository.getQuestion(id);
-		if (result == null) {
-			return null;
-		}
-		if (!"freetext".equals(result.getQuestionType()) && 0 == result.getPiRound()) {
-			/* needed for legacy questions whose piRound property has not been set */
-			result.setPiRound(1);
-		}
-
-		return result;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void deleteQuestion(final String questionId) {
-		final Content content = contentRepository.getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		if (session == null) {
-			throw new UnauthorizedException();
-		}
-		contentRepository.deleteQuestionWithAnswers(content);
-
-		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, content);
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionKeyword, 'session', 'owner')")
-	public void deleteAllQuestions(final String sessionKeyword) {
-		final Session session = getSessionWithAuthCheck(sessionKeyword);
-		contentRepository.deleteAllQuestionsWithAnswers(session);
-
-		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, session);
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void startNewPiRound(final String questionId, User user) {
-		final Content content = contentRepository.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-
-		if (null == user) {
-			user = userService.getCurrentUser();
-		}
-
-		cancelDelayedPiRoundChange(questionId);
-
-		content.setPiRoundEndTime(0);
-		content.setVotingDisabled(true);
-		content.updateRoundManagementState();
-		update(content, user);
-
-		this.publisher.publishEvent(new PiRoundEndEvent(this, session, content));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void startNewPiRoundDelayed(final String questionId, final int time) {
-		final IContentService contentService = this;
-		final User user = userService.getCurrentUser();
-		final Content content = contentRepository.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-
-		final Date date = new Date();
-		final Timer timer = new Timer();
-		final Date endDate = new Date(date.getTime() + (time * 1000));
-		content.updateRoundStartVariables(date, endDate);
-		update(content);
-
-		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, content));
-		timerList.put(questionId, timer);
-
-		timer.schedule(new TimerTask() {
-			@Override
-			public void run() {
-				contentService.startNewPiRound(questionId, user);
-			}
-		}, endDate);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void cancelPiRoundChange(final String questionId) {
-		final Content content = contentRepository.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-
-		cancelDelayedPiRoundChange(questionId);
-		content.resetRoundManagementState();
-
-		if (0 == content.getPiRound() || 1 == content.getPiRound()) {
-			content.setPiRoundFinished(false);
-		} else {
-			content.setPiRound(1);
-			content.setPiRoundFinished(true);
-		}
-
-		update(content);
-		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, content));
-	}
-
-	@Override
-	public void cancelDelayedPiRoundChange(final String questionId) {
-		Timer timer = timerList.get(questionId);
-
-		if (null != timer) {
-			timer.cancel();
-			timerList.remove(questionId);
-			timer.purge();
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void resetPiRoundState(final String questionId) {
-		final Content content = contentRepository.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		cancelDelayedPiRoundChange(questionId);
-
-		if ("freetext".equals(content.getQuestionType())) {
-			content.setPiRound(0);
-		} else {
-			content.setPiRound(1);
-		}
-
-		content.resetRoundManagementState();
-		answerRepository.deleteAnswers(content);
-		update(content);
-		this.publisher.publishEvent(new PiRoundResetEvent(this, session, content));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void setVotingAdmission(final String questionId, final boolean disableVoting) {
-		final Content content = contentRepository.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		content.setVotingDisabled(disableVoting);
-
-		if (!disableVoting && !content.isActive()) {
-			content.setActive(true);
-			update(content);
-		} else {
-			contentRepository.updateQuestion(content);
-		}
-		NovaEvent event;
-		if (disableVoting) {
-			event = new LockVoteEvent(this, session, content);
-		} else {
-			event = new UnlockVoteEvent(this, session, content);
-		}
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Content> contents) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionkey);
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		contentRepository.setVotingAdmissions(session, disableVoting, contents);
-		NovaEvent event;
-		if (disableVoting) {
-			event = new LockVotesEvent(this, session, contents);
-		} else {
-			event = new UnlockVotesEvent(this, session, contents);
-		}
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void setVotingAdmissionForAllQuestions(final String sessionkey, final boolean disableVoting) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionkey);
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		final List<Content> contents = contentRepository.setVotingAdmissionForAllQuestions(session, disableVoting);
-		NovaEvent event;
-		if (disableVoting) {
-			event = new LockVotesEvent(this, session, contents);
-		} else {
-			event = new UnlockVotesEvent(this, session, contents);
-		}
-		this.publisher.publishEvent(event);
-	}
-
-	private Session getSessionWithAuthCheck(final String sessionKeyword) {
-		final User user = userService.getCurrentUser();
-		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
-		if (user == null || session == null || !session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		return session;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#commentId, 'comment', 'owner')")
-	public void deleteInterposedQuestion(final String commentId) {
-		final Comment comment = commentRepository.getInterposedQuestion(commentId);
-		if (comment == null) {
-			throw new NotFoundException();
-		}
-		commentRepository.deleteInterposedQuestion(comment);
-
-		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
-		final DeleteCommentEvent event = new DeleteCommentEvent(this, session, comment);
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deleteAllInterposedQuestions(final String sessionKeyword) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
-		if (session == null) {
-			throw new UnauthorizedException();
-		}
-		final User user = getCurrentUser();
-		if (session.isCreator(user)) {
-			commentRepository.deleteAllInterposedQuestions(session);
-		} else {
-			commentRepository.deleteAllInterposedQuestions(session, user);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
-	public void deleteAnswers(final String questionId) {
-		final Content content = contentRepository.getQuestion(questionId);
-		content.resetQuestionState();
-		contentRepository.updateQuestion(content);
-		answerRepository.deleteAnswers(content);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<String> getUnAnsweredQuestionIds(final String sessionKey) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionKey);
-		return contentRepository.getUnAnsweredQuestionIds(session, user);
-	}
-
-	private User getCurrentUser() {
-		final User user = userService.getCurrentUser();
-		if (user == null) {
-			throw new UnauthorizedException();
-		}
-		return user;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Answer getMyAnswer(final String questionId) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		return answerRepository.getMyAnswer(userService.getCurrentUser(), questionId, content.getPiRound());
-	}
-
-	@Override
-	public void readFreetextAnswer(final String answerId, final User user) {
-		final Answer answer = answerRepository.get(answerId);
-		if (answer == null) {
-			throw new NotFoundException();
-		}
-		if (answer.isRead()) {
-			return;
-		}
-		final Session session = sessionRepository.getSessionFromId(answer.getSessionId());
-		if (session.isCreator(user)) {
-			answer.setRead(true);
-			answerRepository.updateAnswer(answer);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getAnswers(final String questionId, final int piRound, final int offset, final int limit) {
-		final Content content = contentRepository.getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		return "freetext".equals(content.getQuestionType())
-				? getFreetextAnswers(questionId, offset, limit)
-						: answerRepository.getAnswers(content, piRound);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getAnswers(final String questionId, final int offset, final int limit) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		if ("freetext".equals(content.getQuestionType())) {
-			return getFreetextAnswers(questionId, offset, limit);
-		} else {
-			return answerRepository.getAnswers(content);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getAllAnswers(final String questionId, final int offset, final int limit) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		if ("freetext".equals(content.getQuestionType())) {
-			return getFreetextAnswers(questionId, offset, limit);
-		} else {
-			return answerRepository.getAllAnswers(content);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getAnswerCount(final String questionId) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			return 0;
-		}
-
-		if ("freetext".equals(content.getQuestionType())) {
-			return answerRepository.getTotalAnswerCountByQuestion(content);
-		} else {
-			return answerRepository.getAnswerCount(content, content.getPiRound());
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getAnswerCount(final String questionId, final int piRound) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.getAnswerCount(content, piRound);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getAbstentionAnswerCount(final String questionId) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.getAbstentionAnswerCount(questionId);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getTotalAnswerCountByQuestion(final String questionId) {
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			return 0;
-		}
-
-		return answerRepository.getTotalAnswerCountByQuestion(content);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getFreetextAnswers(final String questionId, final int offset, final int limit) {
-		final List<Answer> answers = answerRepository.getFreetextAnswers(questionId, offset, limit);
-		if (answers == null) {
-			throw new NotFoundException();
-		}
-		/* Remove user for privacy concerns */
-		for (Answer answer : answers) {
-			answer.setUser(null);
-		}
-
-		return answers;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Answer> getMyAnswers(final String sessionKey) {
-		final Session session = getSession(sessionKey);
-		// Load contents first because we are only interested in answers of the latest piRound.
-		final List<Content> contents = contentRepository.getSkillQuestionsForUsers(session);
-		final Map<String, Content> questionIdToQuestion = new HashMap<>();
-		for (final Content content : contents) {
-			questionIdToQuestion.put(content.getId(), content);
-		}
-
-		/* filter answers by active piRound per question */
-		final List<Answer> answers = answerRepository.getMyAnswers(userService.getCurrentUser(), session);
-		final List<Answer> filteredAnswers = new ArrayList<>();
-		for (final Answer answer : answers) {
-			final Content content = questionIdToQuestion.get(answer.getQuestionId());
-			if (content == null) {
-				// Content is not present. Most likely it has been locked by the
-				// Session's creator. Locked Questions do not appear in this list.
-				continue;
-			}
-			if (0 == answer.getPiRound() && !"freetext".equals(content.getQuestionType())) {
-				answer.setPiRound(1);
-			}
-
-			// discard all answers that aren't in the same piRound as the content
-			if (answer.getPiRound() == content.getPiRound()) {
-				filteredAnswers.add(answer);
-			}
-		}
-
-		return filteredAnswers;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getTotalAnswerCount(final String sessionKey) {
-		return answerRepository.getTotalAnswerCount(sessionKey);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getInterposedCount(final String sessionKey) {
-		return commentRepository.getInterposedCount(sessionKey);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public CommentReadingCount getInterposedReadingCount(final String sessionKey, String username) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionKey);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		if (username == null) {
-			return commentRepository.getInterposedReadingCount(session);
-		} else {
-			User currentUser = userService.getCurrentUser();
-			if (!currentUser.getUsername().equals(username)) {
-				throw new ForbiddenException();
-			}
-
-			return commentRepository.getInterposedReadingCount(session, currentUser);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Comment> getInterposedQuestions(final String sessionKey, final int offset, final int limit) {
-		final Session session = this.getSession(sessionKey);
-		final User user = getCurrentUser();
-		if (session.isCreator(user)) {
-			return commentRepository.getInterposedQuestions(session, offset, limit);
-		} else {
-			return commentRepository.getInterposedQuestions(session, user, offset, limit);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Comment readInterposedQuestion(final String commentId) {
-		final User user = userService.getCurrentUser();
-		return this.readInterposedQuestionInternal(commentId, user);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public Comment readInterposedQuestionInternal(final String commentId, User user) {
-		final Comment comment = commentRepository.getInterposedQuestion(commentId);
-		if (comment == null) {
-			throw new NotFoundException();
-		}
-		final Session session = sessionRepository.getSessionFromId(comment.getSessionId());
-		if (!comment.isCreator(user) && !session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		if (session.isCreator(user)) {
-			commentRepository.markInterposedQuestionAsRead(comment);
-		}
-		return comment;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Content update(final Content content) {
-		final User user = userService.getCurrentUser();
-		return update(content, user);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Content update(final Content content, User user) {
-		final Content oldContent = contentRepository.getQuestion(content.getId());
-		if (null == oldContent) {
-			throw new NotFoundException();
-		}
-
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		if (user == null || session == null || !session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-
-		if ("freetext".equals(content.getQuestionType())) {
-			content.setPiRound(0);
-		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
-			content.setPiRound(oldContent.getPiRound() > 0 ? oldContent.getPiRound() : 1);
-		}
-
-		final Content result = contentRepository.updateQuestion(content);
-
-		if (!oldContent.isActive() && content.isActive()) {
-			final UnlockQuestionEvent event = new UnlockQuestionEvent(this, session, result);
-			this.publisher.publishEvent(event);
-		} else if (oldContent.isActive() && !content.isActive()) {
-			final LockQuestionEvent event = new LockQuestionEvent(this, session, result);
-			this.publisher.publishEvent(event);
-		}
-		return result;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Answer saveAnswer(final String questionId, final de.thm.arsnova.entities.transport.Answer answer) {
-		final User user = getCurrentUser();
-		final Content content = getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-
-		Answer theAnswer = answer.generateAnswerEntity(user, content);
-		theAnswer.setUser(user.getUsername());
-		theAnswer.setQuestionId(content.getId());
-		theAnswer.setSessionId(session.getId());
-		if ("freetext".equals(content.getQuestionType())) {
-			imageUtils.generateThumbnailImage(theAnswer);
-			if (content.isFixedAnswer() && content.getText() != null) {
-				theAnswer.setAnswerTextRaw(theAnswer.getAnswerText());
-
-				if (content.isStrictMode()) {
-					content.checkTextStrictOptions(theAnswer);
-				}
-				theAnswer.setQuestionValue(content.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
-				theAnswer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
-			}
-		}
-
-		return answerRepository.saveAnswer(theAnswer, user, content, session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Answer updateAnswer(final Answer answer) {
-		final User user = userService.getCurrentUser();
-		final Answer realAnswer = this.getMyAnswer(answer.getQuestionId());
-		if (user == null || realAnswer == null || !user.getUsername().equals(realAnswer.getUser())) {
-			throw new UnauthorizedException();
-		}
-
-		final Content content = getQuestion(answer.getQuestionId());
-		if ("freetext".equals(content.getQuestionType())) {
-			imageUtils.generateThumbnailImage(realAnswer);
-			content.checkTextStrictOptions(realAnswer);
-		}
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		answer.setUser(user.getUsername());
-		answer.setQuestionId(content.getId());
-		answer.setSessionId(session.getId());
-		final Answer result = answerRepository.updateAnswer(realAnswer);
-		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, content));
-
-		return result;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deleteAnswer(final String questionId, final String answerId) {
-		final Content content = contentRepository.getQuestion(questionId);
-		if (content == null) {
-			throw new NotFoundException();
-		}
-		final User user = userService.getCurrentUser();
-		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
-		if (user == null || session == null || !session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		answerRepository.deleteAnswer(answerId);
-
-		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, content));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Content> getLectureQuestions(final String sessionkey) {
-		final Session session = getSession(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (session.isCreator(user)) {
-			return contentRepository.getLectureQuestionsForTeachers(session);
-		} else {
-			return contentRepository.getLectureQuestionsForUsers(session);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Content> getFlashcards(final String sessionkey) {
-		final Session session = getSession(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (session.isCreator(user)) {
-			return contentRepository.getFlashcardsForTeachers(session);
-		} else {
-			return contentRepository.getFlashcardsForUsers(session);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Content> getPreparationQuestions(final String sessionkey) {
-		final Session session = getSession(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (session.isCreator(user)) {
-			return contentRepository.getPreparationQuestionsForTeachers(session);
-		} else {
-			return contentRepository.getPreparationQuestionsForUsers(session);
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Content> replaceImageData(final List<Content> contents) {
-		for (Content q : contents) {
-			if (q.getImage() != null && q.getImage().startsWith("data:image/")) {
-				q.setImage("true");
-			}
-		}
-
-		return contents;
-	}
-
-	private Session getSession(final String sessionkey) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		return session;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getLectureQuestionCount(final String sessionkey) {
-		return contentRepository.getLectureQuestionCount(getSession(sessionkey));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getFlashcardCount(final String sessionkey) {
-		return contentRepository.getFlashcardCount(getSession(sessionkey));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int getPreparationQuestionCount(final String sessionkey) {
-		return contentRepository.getPreparationQuestionCount(getSession(sessionkey));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countLectureQuestionAnswers(final String sessionkey) {
-		return this.countLectureQuestionAnswersInternal(sessionkey);
-	}
-
-	/*
-	 * 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 sessionkey) {
-		return answerRepository.countLectureQuestionAnswers(getSession(sessionkey));
-	}
-
-	@Override
-	public Map<String, Object> getAnswerAndAbstentionCountInternal(final String questionId) {
-		final Content content = getQuestion(questionId);
-		HashMap<String, Object> map = new HashMap<>();
-
-		if (content == null) {
-			return null;
-		}
-
-		map.put("_id", questionId);
-		map.put("answers", answerRepository.getAnswerCount(content, content.getPiRound()));
-		map.put("abstentions", answerRepository.getAbstentionAnswerCount(questionId));
-
-		return map;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public int countPreparationQuestionAnswers(final String sessionkey) {
-		return this.countPreparationQuestionAnswersInternal(sessionkey);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public int countPreparationQuestionAnswersInternal(final String sessionkey) {
-		return answerRepository.countPreparationQuestionAnswers(getSession(sessionkey));
-	}
-
-	/*
-	 * 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 countFlashcardsForUserInternal(final String sessionkey) {
-		return contentRepository.getFlashcardsForUsers(getSession(sessionkey)).size();
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deleteLectureQuestions(final String sessionkey) {
-		final Session session = getSessionWithAuthCheck(sessionkey);
-		contentRepository.deleteAllLectureQuestionsWithAnswers(session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deleteFlashcards(final String sessionkey) {
-		final Session session = getSessionWithAuthCheck(sessionkey);
-		contentRepository.deleteAllFlashcardsWithAnswers(session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deletePreparationQuestions(final String sessionkey) {
-		final Session session = getSessionWithAuthCheck(sessionkey);
-		contentRepository.deleteAllPreparationQuestionsWithAnswers(session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey) {
-		final User user = getCurrentUser();
-		return this.getUnAnsweredLectureQuestionIds(sessionkey, user);
-	}
-
-	@Override
-	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey, final User user) {
-		final Session session = getSession(sessionkey);
-		return contentRepository.getUnAnsweredLectureQuestionIds(session, user);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey) {
-		final User user = getCurrentUser();
-		return this.getUnAnsweredPreparationQuestionIds(sessionkey, user);
-	}
-
-	@Override
-	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey, final User user) {
-		final Session session = getSession(sessionkey);
-		return contentRepository.getUnAnsweredPreparationQuestionIds(session, user);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void publishAll(final String sessionkey, final boolean publish) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionkey);
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		final List<Content> contents = contentRepository.publishAllQuestions(session, publish);
-		NovaEvent event;
-		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, contents);
-		} else {
-			event = new LockQuestionsEvent(this, session, contents);
-		}
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void publishQuestions(final String sessionkey, final boolean publish, List<Content> contents) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionkey);
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		contentRepository.publishQuestions(session, publish, contents);
-		NovaEvent event;
-		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, contents);
-		} else {
-			event = new LockQuestionsEvent(this, session, contents);
-		}
-		this.publisher.publishEvent(event);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public void deleteAllQuestionsAnswers(final String sessionkey) {
-		final User user = getCurrentUser();
-		final Session session = getSession(sessionkey);
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException();
-		}
-		answerRepository.deleteAllQuestionsAnswers(session);
-
-		this.publisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, session));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public void deleteAllPreparationAnswers(String sessionkey) {
-		final Session session = getSession(sessionkey);
-		answerRepository.deleteAllPreparationAnswers(session);
-
-		this.publisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, session));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public void deleteAllLectureAnswers(String sessionkey) {
-		final Session session = getSession(sessionkey);
-		answerRepository.deleteAllLectureAnswers(session);
-
-		this.publisher.publishEvent(new DeleteAllLectureAnswersEvent(this, session));
-	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
-
-	@Override
-	public String getImage(String questionId, String answerId) {
-		final List<Answer> answers = getAnswers(questionId, -1, -1);
-		Answer answer = null;
-
-		for (Answer a : answers) {
-			if (answerId.equals(a.getId())) {
-				answer = a;
-				break;
-			}
-		}
-
-		if (answer == null) {
-			throw new NotFoundException();
-		}
-
-		return answer.getAnswerImage();
-	}
-
-	@Override
-	public String getQuestionImage(String questionId) {
-		Content content = contentRepository.getQuestion(questionId);
-		String imageData = content.getImage();
-
-		if (imageData == null) {
-			imageData = "";
-		}
-
-		return imageData;
-	}
-
-	@Override
-	public String getQuestionFcImage(String questionId) {
-		Content content = contentRepository.getQuestion(questionId);
-		String imageData = content.getFcImage();
-
-		if (imageData == null) {
-			imageData = "";
-		}
-
-		return imageData;
-	}
+public interface ContentService {
+	Content saveQuestion(Content content);
+
+	boolean saveQuestion(Comment comment);
+
+	Content getQuestion(String id);
+
+	List<Content> getSkillQuestions(String sessionkey);
+
+	int getSkillQuestionCount(String sessionkey);
+
+	void deleteQuestion(String questionId);
+
+	void deleteAllQuestions(String sessionKeyword);
+
+	void startNewPiRound(String questionId, User user);
+
+	void startNewPiRoundDelayed(String questionId, int time);
+
+	void cancelPiRoundChange(String questionId);
+
+	void cancelDelayedPiRoundChange(String questionId);
+
+	void resetPiRoundState(String questionId);
+
+	List<String> getUnAnsweredQuestionIds(String sessionKey);
+
+	Answer getMyAnswer(String questionId);
+
+	void readFreetextAnswer(String answerId, User user);
+
+	List<Answer> getAnswers(String questionId, int piRound, int offset, int limit);
+
+	List<Answer> getAnswers(String questionId, int offset, int limit);
+
+	List<Answer> getAllAnswers(String questionId, int offset, int limit);
+
+	int getAnswerCount(String questionId);
+
+	int getAnswerCount(String questionId, int piRound);
+
+	List<Answer> getFreetextAnswers(String questionId, int offset, int limit);
+
+	List<Answer> getMyAnswers(String sessionKey);
+
+	int getTotalAnswerCount(String sessionKey);
+
+	int getTotalAnswerCountByQuestion(String questionId);
+
+	int getInterposedCount(String sessionKey);
+
+	CommentReadingCount getInterposedReadingCount(String sessionKey, String username);
+
+	List<Comment> getInterposedQuestions(String sessionKey, int offset, int limit);
+
+	Comment readInterposedQuestion(String commentId);
+
+	Comment readInterposedQuestionInternal(String commentId, User user);
+
+	Content update(Content content);
+
+	Content update(Content content, User user);
+
+	void deleteAnswers(String questionId);
+
+	Answer saveAnswer(String questionId, de.thm.arsnova.entities.transport.Answer answer);
+
+	Answer updateAnswer(Answer answer);
+
+	void deleteAnswer(String questionId, String answerId);
+
+	void deleteInterposedQuestion(String commentId);
+
+	List<Content> getLectureQuestions(String sessionkey);
+
+	List<Content> getFlashcards(String sessionkey);
+
+	List<Content> getPreparationQuestions(String sessionkey);
+
+	int getLectureQuestionCount(String sessionkey);
+
+	int getFlashcardCount(String sessionkey);
+
+	int getPreparationQuestionCount(String sessionkey);
+
+	Map<String, Object> getAnswerAndAbstentionCountInternal(String questionid);
+
+	int countLectureQuestionAnswers(String sessionkey);
+
+	int countLectureQuestionAnswersInternal(String sessionkey);
+
+	int countPreparationQuestionAnswers(String sessionkey);
+
+	int countPreparationQuestionAnswersInternal(String sessionkey);
+
+	int countFlashcardsForUserInternal(String sessionkey);
+
+	void deleteLectureQuestions(String sessionkey);
+
+	void deleteFlashcards(String sessionkey);
+
+	void deletePreparationQuestions(String sessionkey);
+
+	List<String> getUnAnsweredLectureQuestionIds(String sessionkey);
+
+	List<String> getUnAnsweredLectureQuestionIds(String sessionKey, User user);
+
+	List<String> getUnAnsweredPreparationQuestionIds(String sessionkey);
+
+	List<String> getUnAnsweredPreparationQuestionIds(String sessionKey, User user);
+
+	void deleteAllInterposedQuestions(String sessionKeyword);
+
+	void publishAll(String sessionkey, boolean publish);
+
+	void publishQuestions(String sessionkey, boolean publish, List<Content> contents);
+
+	void deleteAllQuestionsAnswers(String sessionkey);
+
+	void deleteAllPreparationAnswers(String sessionkey);
+
+	void deleteAllLectureAnswers(String sessionkey);
+
+	int getAbstentionAnswerCount(String questionId);
+
+	String getImage(String questionId, String answerId);
+
+	void setVotingAdmission(String questionId, boolean disableVoting);
+
+	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Content> contents);
+
+	void setVotingAdmissionForAllQuestions(String sessionkey, boolean disableVoting);
+
+	String getQuestionImage(String questionId);
+
+	String getQuestionFcImage(String questionId);
+
+	List<Content> replaceImageData(List<Content> contents);
+
 }
diff --git a/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..62580548f676b6b9c70c3ba20553c17d196cbce4
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/ContentServiceImpl.java
@@ -0,0 +1,1041 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.util.ImageUtils;
+import de.thm.arsnova.entities.Answer;
+import de.thm.arsnova.entities.Comment;
+import de.thm.arsnova.entities.CommentReadingCount;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.events.*;
+import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.exceptions.ForbiddenException;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.AnswerRepository;
+import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.SessionRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Performs all question, comment, and answer related operations.
+ */
+@Service
+public class ContentServiceImpl implements ContentService, ApplicationEventPublisherAware {
+	@Autowired
+	private UserService userService;
+
+	@Autowired
+	private SessionRepository sessionRepository;
+
+	@Autowired
+	private CommentRepository commentRepository;
+
+	@Autowired
+	private ContentRepository contentRepository;
+
+	@Autowired
+	private AnswerRepository answerRepository;
+
+	@Autowired
+	private ImageUtils imageUtils;
+
+	@Value("${upload.filesize_b}")
+	private int uploadFileSizeByte;
+
+	private ApplicationEventPublisher publisher;
+
+	private static final Logger logger = LoggerFactory.getLogger(ContentServiceImpl.class);
+
+	private HashMap<String, Timer> timerList = new HashMap<>();
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Content> getSkillQuestions(final String sessionkey) {
+		final Session session = getSession(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (session.isCreator(user)) {
+			return contentRepository.getSkillQuestionsForTeachers(session);
+		} else {
+			return contentRepository.getSkillQuestionsForUsers(session);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getSkillQuestionCount(final String sessionkey) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		return contentRepository.getSkillQuestionCount(session);
+	}
+
+	/* FIXME: #content.getSessionKeyword() cannot be checked since keyword is no longer set for content. */
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#content.getSessionKeyword(), 'session', 'owner')")
+	public Content saveQuestion(final Content content) {
+		final Session session = sessionRepository.getSessionFromKeyword(content.getSessionKeyword());
+		content.setSessionId(session.getId());
+		content.setTimestamp(System.currentTimeMillis() / 1000L);
+
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(1);
+		}
+
+		// convert imageurl to base64 if neccessary
+		if ("grid".equals(content.getQuestionType()) && !content.getImage().startsWith("http")) {
+			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
+			final int fileSize = (int) ((content.getImage().length() - 814) / 1.37);
+			if (fileSize > uploadFileSizeByte) {
+				logger.error("Could not save file. File is too large with {} Byte.", fileSize);
+				throw new BadRequestException();
+			}
+		}
+
+		final Content result = contentRepository.saveQuestion(session, content);
+
+		final NewQuestionEvent event = new NewQuestionEvent(this, session, result);
+		this.publisher.publishEvent(event);
+
+		return result;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public boolean saveQuestion(final Comment comment) {
+		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
+		final Comment result = commentRepository.saveQuestion(session, comment, userService.getCurrentUser());
+
+		if (null != result) {
+			final NewCommentEvent event = new NewCommentEvent(this, session, result);
+			this.publisher.publishEvent(event);
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Content getQuestion(final String id) {
+		final Content result = contentRepository.getQuestion(id);
+		if (result == null) {
+			return null;
+		}
+		if (!"freetext".equals(result.getQuestionType()) && 0 == result.getPiRound()) {
+			/* needed for legacy questions whose piRound property has not been set */
+			result.setPiRound(1);
+		}
+
+		return result;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void deleteQuestion(final String questionId) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		if (session == null) {
+			throw new UnauthorizedException();
+		}
+		contentRepository.deleteQuestionWithAnswers(content);
+
+		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, content);
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionKeyword, 'session', 'owner')")
+	public void deleteAllQuestions(final String sessionKeyword) {
+		final Session session = getSessionWithAuthCheck(sessionKeyword);
+		contentRepository.deleteAllQuestionsWithAnswers(session);
+
+		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, session);
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void startNewPiRound(final String questionId, User user) {
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+
+		if (null == user) {
+			user = userService.getCurrentUser();
+		}
+
+		cancelDelayedPiRoundChange(questionId);
+
+		content.setPiRoundEndTime(0);
+		content.setVotingDisabled(true);
+		content.updateRoundManagementState();
+		update(content, user);
+
+		this.publisher.publishEvent(new PiRoundEndEvent(this, session, content));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void startNewPiRoundDelayed(final String questionId, final int time) {
+		final ContentService contentService = this;
+		final User user = userService.getCurrentUser();
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+
+		final Date date = new Date();
+		final Timer timer = new Timer();
+		final Date endDate = new Date(date.getTime() + (time * 1000));
+		content.updateRoundStartVariables(date, endDate);
+		update(content);
+
+		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, content));
+		timerList.put(questionId, timer);
+
+		timer.schedule(new TimerTask() {
+			@Override
+			public void run() {
+				contentService.startNewPiRound(questionId, user);
+			}
+		}, endDate);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void cancelPiRoundChange(final String questionId) {
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+
+		cancelDelayedPiRoundChange(questionId);
+		content.resetRoundManagementState();
+
+		if (0 == content.getPiRound() || 1 == content.getPiRound()) {
+			content.setPiRoundFinished(false);
+		} else {
+			content.setPiRound(1);
+			content.setPiRoundFinished(true);
+		}
+
+		update(content);
+		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, content));
+	}
+
+	@Override
+	public void cancelDelayedPiRoundChange(final String questionId) {
+		Timer timer = timerList.get(questionId);
+
+		if (null != timer) {
+			timer.cancel();
+			timerList.remove(questionId);
+			timer.purge();
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void resetPiRoundState(final String questionId) {
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		cancelDelayedPiRoundChange(questionId);
+
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else {
+			content.setPiRound(1);
+		}
+
+		content.resetRoundManagementState();
+		answerRepository.deleteAnswers(content);
+		update(content);
+		this.publisher.publishEvent(new PiRoundResetEvent(this, session, content));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void setVotingAdmission(final String questionId, final boolean disableVoting) {
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		content.setVotingDisabled(disableVoting);
+
+		if (!disableVoting && !content.isActive()) {
+			content.setActive(true);
+			update(content);
+		} else {
+			contentRepository.updateQuestion(content);
+		}
+		ArsnovaEvent event;
+		if (disableVoting) {
+			event = new LockVoteEvent(this, session, content);
+		} else {
+			event = new UnlockVoteEvent(this, session, content);
+		}
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Content> contents) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionkey);
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		contentRepository.setVotingAdmissions(session, disableVoting, contents);
+		ArsnovaEvent event;
+		if (disableVoting) {
+			event = new LockVotesEvent(this, session, contents);
+		} else {
+			event = new UnlockVotesEvent(this, session, contents);
+		}
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void setVotingAdmissionForAllQuestions(final String sessionkey, final boolean disableVoting) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionkey);
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		final List<Content> contents = contentRepository.setVotingAdmissionForAllQuestions(session, disableVoting);
+		ArsnovaEvent event;
+		if (disableVoting) {
+			event = new LockVotesEvent(this, session, contents);
+		} else {
+			event = new UnlockVotesEvent(this, session, contents);
+		}
+		this.publisher.publishEvent(event);
+	}
+
+	private Session getSessionWithAuthCheck(final String sessionKeyword) {
+		final User user = userService.getCurrentUser();
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
+		if (user == null || session == null || !session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		return session;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#commentId, 'comment', 'owner')")
+	public void deleteInterposedQuestion(final String commentId) {
+		final Comment comment = commentRepository.getInterposedQuestion(commentId);
+		if (comment == null) {
+			throw new NotFoundException();
+		}
+		commentRepository.deleteInterposedQuestion(comment);
+
+		final Session session = sessionRepository.getSessionFromKeyword(comment.getSessionId());
+		final DeleteCommentEvent event = new DeleteCommentEvent(this, session, comment);
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deleteAllInterposedQuestions(final String sessionKeyword) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKeyword);
+		if (session == null) {
+			throw new UnauthorizedException();
+		}
+		final User user = getCurrentUser();
+		if (session.isCreator(user)) {
+			commentRepository.deleteAllInterposedQuestions(session);
+		} else {
+			commentRepository.deleteAllInterposedQuestions(session, user);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
+	public void deleteAnswers(final String questionId) {
+		final Content content = contentRepository.getQuestion(questionId);
+		content.resetQuestionState();
+		contentRepository.updateQuestion(content);
+		answerRepository.deleteAnswers(content);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<String> getUnAnsweredQuestionIds(final String sessionKey) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionKey);
+		return contentRepository.getUnAnsweredQuestionIds(session, user);
+	}
+
+	private User getCurrentUser() {
+		final User user = userService.getCurrentUser();
+		if (user == null) {
+			throw new UnauthorizedException();
+		}
+		return user;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Answer getMyAnswer(final String questionId) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		return answerRepository.getMyAnswer(userService.getCurrentUser(), questionId, content.getPiRound());
+	}
+
+	@Override
+	public void readFreetextAnswer(final String answerId, final User user) {
+		final Answer answer = answerRepository.get(answerId);
+		if (answer == null) {
+			throw new NotFoundException();
+		}
+		if (answer.isRead()) {
+			return;
+		}
+		final Session session = sessionRepository.getSessionFromId(answer.getSessionId());
+		if (session.isCreator(user)) {
+			answer.setRead(true);
+			answerRepository.updateAnswer(answer);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getAnswers(final String questionId, final int piRound, final int offset, final int limit) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		return "freetext".equals(content.getQuestionType())
+				? getFreetextAnswers(questionId, offset, limit)
+						: answerRepository.getAnswers(content, piRound);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getAnswers(final String questionId, final int offset, final int limit) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		if ("freetext".equals(content.getQuestionType())) {
+			return getFreetextAnswers(questionId, offset, limit);
+		} else {
+			return answerRepository.getAnswers(content);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getAllAnswers(final String questionId, final int offset, final int limit) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		if ("freetext".equals(content.getQuestionType())) {
+			return getFreetextAnswers(questionId, offset, limit);
+		} else {
+			return answerRepository.getAllAnswers(content);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getAnswerCount(final String questionId) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			return 0;
+		}
+
+		if ("freetext".equals(content.getQuestionType())) {
+			return answerRepository.getTotalAnswerCountByQuestion(content);
+		} else {
+			return answerRepository.getAnswerCount(content, content.getPiRound());
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getAnswerCount(final String questionId, final int piRound) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.getAnswerCount(content, piRound);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getAbstentionAnswerCount(final String questionId) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.getAbstentionAnswerCount(questionId);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getTotalAnswerCountByQuestion(final String questionId) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			return 0;
+		}
+
+		return answerRepository.getTotalAnswerCountByQuestion(content);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getFreetextAnswers(final String questionId, final int offset, final int limit) {
+		final List<Answer> answers = answerRepository.getFreetextAnswers(questionId, offset, limit);
+		if (answers == null) {
+			throw new NotFoundException();
+		}
+		/* Remove user for privacy concerns */
+		for (Answer answer : answers) {
+			answer.setUser(null);
+		}
+
+		return answers;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Answer> getMyAnswers(final String sessionKey) {
+		final Session session = getSession(sessionKey);
+		// Load contents first because we are only interested in answers of the latest piRound.
+		final List<Content> contents = contentRepository.getSkillQuestionsForUsers(session);
+		final Map<String, Content> questionIdToQuestion = new HashMap<>();
+		for (final Content content : contents) {
+			questionIdToQuestion.put(content.getId(), content);
+		}
+
+		/* filter answers by active piRound per question */
+		final List<Answer> answers = answerRepository.getMyAnswers(userService.getCurrentUser(), session);
+		final List<Answer> filteredAnswers = new ArrayList<>();
+		for (final Answer answer : answers) {
+			final Content content = questionIdToQuestion.get(answer.getQuestionId());
+			if (content == null) {
+				// Content is not present. Most likely it has been locked by the
+				// Session's creator. Locked Questions do not appear in this list.
+				continue;
+			}
+			if (0 == answer.getPiRound() && !"freetext".equals(content.getQuestionType())) {
+				answer.setPiRound(1);
+			}
+
+			// discard all answers that aren't in the same piRound as the content
+			if (answer.getPiRound() == content.getPiRound()) {
+				filteredAnswers.add(answer);
+			}
+		}
+
+		return filteredAnswers;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getTotalAnswerCount(final String sessionKey) {
+		return answerRepository.getTotalAnswerCount(sessionKey);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getInterposedCount(final String sessionKey) {
+		return commentRepository.getInterposedCount(sessionKey);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public CommentReadingCount getInterposedReadingCount(final String sessionKey, String username) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionKey);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		if (username == null) {
+			return commentRepository.getInterposedReadingCount(session);
+		} else {
+			User currentUser = userService.getCurrentUser();
+			if (!currentUser.getUsername().equals(username)) {
+				throw new ForbiddenException();
+			}
+
+			return commentRepository.getInterposedReadingCount(session, currentUser);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Comment> getInterposedQuestions(final String sessionKey, final int offset, final int limit) {
+		final Session session = this.getSession(sessionKey);
+		final User user = getCurrentUser();
+		if (session.isCreator(user)) {
+			return commentRepository.getInterposedQuestions(session, offset, limit);
+		} else {
+			return commentRepository.getInterposedQuestions(session, user, offset, limit);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Comment readInterposedQuestion(final String commentId) {
+		final User user = userService.getCurrentUser();
+		return this.readInterposedQuestionInternal(commentId, user);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public Comment readInterposedQuestionInternal(final String commentId, User user) {
+		final Comment comment = commentRepository.getInterposedQuestion(commentId);
+		if (comment == null) {
+			throw new NotFoundException();
+		}
+		final Session session = sessionRepository.getSessionFromId(comment.getSessionId());
+		if (!comment.isCreator(user) && !session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		if (session.isCreator(user)) {
+			commentRepository.markInterposedQuestionAsRead(comment);
+		}
+		return comment;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Content update(final Content content) {
+		final User user = userService.getCurrentUser();
+		return update(content, user);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Content update(final Content content, User user) {
+		final Content oldContent = contentRepository.getQuestion(content.getId());
+		if (null == oldContent) {
+			throw new NotFoundException();
+		}
+
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		if (user == null || session == null || !session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(oldContent.getPiRound() > 0 ? oldContent.getPiRound() : 1);
+		}
+
+		final Content result = contentRepository.updateQuestion(content);
+
+		if (!oldContent.isActive() && content.isActive()) {
+			final UnlockQuestionEvent event = new UnlockQuestionEvent(this, session, result);
+			this.publisher.publishEvent(event);
+		} else if (oldContent.isActive() && !content.isActive()) {
+			final LockQuestionEvent event = new LockQuestionEvent(this, session, result);
+			this.publisher.publishEvent(event);
+		}
+		return result;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Answer saveAnswer(final String questionId, final de.thm.arsnova.entities.transport.Answer answer) {
+		final User user = getCurrentUser();
+		final Content content = getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+
+		Answer theAnswer = answer.generateAnswerEntity(user, content);
+		theAnswer.setUser(user.getUsername());
+		theAnswer.setQuestionId(content.getId());
+		theAnswer.setSessionId(session.getId());
+		if ("freetext".equals(content.getQuestionType())) {
+			imageUtils.generateThumbnailImage(theAnswer);
+			if (content.isFixedAnswer() && content.getText() != null) {
+				theAnswer.setAnswerTextRaw(theAnswer.getAnswerText());
+
+				if (content.isStrictMode()) {
+					content.checkTextStrictOptions(theAnswer);
+				}
+				theAnswer.setQuestionValue(content.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
+				theAnswer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
+			}
+		}
+
+		return answerRepository.saveAnswer(theAnswer, user, content, session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Answer updateAnswer(final Answer answer) {
+		final User user = userService.getCurrentUser();
+		final Answer realAnswer = this.getMyAnswer(answer.getQuestionId());
+		if (user == null || realAnswer == null || !user.getUsername().equals(realAnswer.getUser())) {
+			throw new UnauthorizedException();
+		}
+
+		final Content content = getQuestion(answer.getQuestionId());
+		if ("freetext".equals(content.getQuestionType())) {
+			imageUtils.generateThumbnailImage(realAnswer);
+			content.checkTextStrictOptions(realAnswer);
+		}
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		answer.setUser(user.getUsername());
+		answer.setQuestionId(content.getId());
+		answer.setSessionId(session.getId());
+		final Answer result = answerRepository.updateAnswer(realAnswer);
+		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, content));
+
+		return result;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deleteAnswer(final String questionId, final String answerId) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
+			throw new NotFoundException();
+		}
+		final User user = userService.getCurrentUser();
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		if (user == null || session == null || !session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		answerRepository.deleteAnswer(answerId);
+
+		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, content));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Content> getLectureQuestions(final String sessionkey) {
+		final Session session = getSession(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (session.isCreator(user)) {
+			return contentRepository.getLectureQuestionsForTeachers(session);
+		} else {
+			return contentRepository.getLectureQuestionsForUsers(session);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Content> getFlashcards(final String sessionkey) {
+		final Session session = getSession(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (session.isCreator(user)) {
+			return contentRepository.getFlashcardsForTeachers(session);
+		} else {
+			return contentRepository.getFlashcardsForUsers(session);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Content> getPreparationQuestions(final String sessionkey) {
+		final Session session = getSession(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (session.isCreator(user)) {
+			return contentRepository.getPreparationQuestionsForTeachers(session);
+		} else {
+			return contentRepository.getPreparationQuestionsForUsers(session);
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Content> replaceImageData(final List<Content> contents) {
+		for (Content q : contents) {
+			if (q.getImage() != null && q.getImage().startsWith("data:image/")) {
+				q.setImage("true");
+			}
+		}
+
+		return contents;
+	}
+
+	private Session getSession(final String sessionkey) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		return session;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getLectureQuestionCount(final String sessionkey) {
+		return contentRepository.getLectureQuestionCount(getSession(sessionkey));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getFlashcardCount(final String sessionkey) {
+		return contentRepository.getFlashcardCount(getSession(sessionkey));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int getPreparationQuestionCount(final String sessionkey) {
+		return contentRepository.getPreparationQuestionCount(getSession(sessionkey));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countLectureQuestionAnswers(final String sessionkey) {
+		return this.countLectureQuestionAnswersInternal(sessionkey);
+	}
+
+	/*
+	 * 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 sessionkey) {
+		return answerRepository.countLectureQuestionAnswers(getSession(sessionkey));
+	}
+
+	@Override
+	public Map<String, Object> getAnswerAndAbstentionCountInternal(final String questionId) {
+		final Content content = getQuestion(questionId);
+		HashMap<String, Object> map = new HashMap<>();
+
+		if (content == null) {
+			return null;
+		}
+
+		map.put("_id", questionId);
+		map.put("answers", answerRepository.getAnswerCount(content, content.getPiRound()));
+		map.put("abstentions", answerRepository.getAbstentionAnswerCount(questionId));
+
+		return map;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public int countPreparationQuestionAnswers(final String sessionkey) {
+		return this.countPreparationQuestionAnswersInternal(sessionkey);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public int countPreparationQuestionAnswersInternal(final String sessionkey) {
+		return answerRepository.countPreparationQuestionAnswers(getSession(sessionkey));
+	}
+
+	/*
+	 * 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 countFlashcardsForUserInternal(final String sessionkey) {
+		return contentRepository.getFlashcardsForUsers(getSession(sessionkey)).size();
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deleteLectureQuestions(final String sessionkey) {
+		final Session session = getSessionWithAuthCheck(sessionkey);
+		contentRepository.deleteAllLectureQuestionsWithAnswers(session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deleteFlashcards(final String sessionkey) {
+		final Session session = getSessionWithAuthCheck(sessionkey);
+		contentRepository.deleteAllFlashcardsWithAnswers(session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deletePreparationQuestions(final String sessionkey) {
+		final Session session = getSessionWithAuthCheck(sessionkey);
+		contentRepository.deleteAllPreparationQuestionsWithAnswers(session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey) {
+		final User user = getCurrentUser();
+		return this.getUnAnsweredLectureQuestionIds(sessionkey, user);
+	}
+
+	@Override
+	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey, final User user) {
+		final Session session = getSession(sessionkey);
+		return contentRepository.getUnAnsweredLectureQuestionIds(session, user);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey) {
+		final User user = getCurrentUser();
+		return this.getUnAnsweredPreparationQuestionIds(sessionkey, user);
+	}
+
+	@Override
+	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey, final User user) {
+		final Session session = getSession(sessionkey);
+		return contentRepository.getUnAnsweredPreparationQuestionIds(session, user);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void publishAll(final String sessionkey, final boolean publish) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionkey);
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		final List<Content> contents = contentRepository.publishAllQuestions(session, publish);
+		ArsnovaEvent event;
+		if (publish) {
+			event = new UnlockQuestionsEvent(this, session, contents);
+		} else {
+			event = new LockQuestionsEvent(this, session, contents);
+		}
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void publishQuestions(final String sessionkey, final boolean publish, List<Content> contents) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionkey);
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		contentRepository.publishQuestions(session, publish, contents);
+		ArsnovaEvent event;
+		if (publish) {
+			event = new UnlockQuestionsEvent(this, session, contents);
+		} else {
+			event = new LockQuestionsEvent(this, session, contents);
+		}
+		this.publisher.publishEvent(event);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public void deleteAllQuestionsAnswers(final String sessionkey) {
+		final User user = getCurrentUser();
+		final Session session = getSession(sessionkey);
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		answerRepository.deleteAllQuestionsAnswers(session);
+
+		this.publisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, session));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public void deleteAllPreparationAnswers(String sessionkey) {
+		final Session session = getSession(sessionkey);
+		answerRepository.deleteAllPreparationAnswers(session);
+
+		this.publisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, session));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public void deleteAllLectureAnswers(String sessionkey) {
+		final Session session = getSession(sessionkey);
+		answerRepository.deleteAllLectureAnswers(session);
+
+		this.publisher.publishEvent(new DeleteAllLectureAnswersEvent(this, session));
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	@Override
+	public String getImage(String questionId, String answerId) {
+		final List<Answer> answers = getAnswers(questionId, -1, -1);
+		Answer answer = null;
+
+		for (Answer a : answers) {
+			if (answerId.equals(a.getId())) {
+				answer = a;
+				break;
+			}
+		}
+
+		if (answer == null) {
+			throw new NotFoundException();
+		}
+
+		return answer.getAnswerImage();
+	}
+
+	@Override
+	public String getQuestionImage(String questionId) {
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getImage();
+
+		if (imageData == null) {
+			imageData = "";
+		}
+
+		return imageData;
+	}
+
+	@Override
+	public String getQuestionFcImage(String questionId) {
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getFcImage();
+
+		if (imageData == null) {
+			imageData = "";
+		}
+
+		return imageData;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/FeedbackService.java b/src/main/java/de/thm/arsnova/services/FeedbackService.java
index 0a4a69599944a7f9d26df23104dcbda994fbf955..8e0d8842e2a9f988247001db22b26ad57de462a5 100644
--- a/src/main/java/de/thm/arsnova/services/FeedbackService.java
+++ b/src/main/java/de/thm/arsnova/services/FeedbackService.java
@@ -17,172 +17,26 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.FeedbackStorage;
 import de.thm.arsnova.entities.Feedback;
-import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.events.DeleteFeedbackForSessionsEvent;
-import de.thm.arsnova.events.NewFeedbackEvent;
-import de.thm.arsnova.exceptions.NoContentException;
-import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.persistance.SessionRepository;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.PostConstruct;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
- * Performs all feedback related operations.
+ * The functionality the feedback service should provide.
  */
-@Service
-public class FeedbackService implements IFeedbackService, ApplicationEventPublisherAware {
-
-	private static final int DEFAULT_SCHEDULER_DELAY = 5000;
-	private static final double Z_THRESHOLD = 0.1;
-
-	/**
-	 * minutes, after which the feedback is deleted
-	 */
-	@Value("${feedback.cleanup}")
-	private int cleanupFeedbackDelay;
-
-	@Autowired
-	private SessionRepository sessionRepository;
-
-	private FeedbackStorage feedbackStorage;
-
-	private ApplicationEventPublisher publisher;
-
-	@PostConstruct
-	public void init() {
-		feedbackStorage = new FeedbackStorage();
-	}
-
-	@Override
-	@Scheduled(fixedDelay = DEFAULT_SCHEDULER_DELAY)
-	public void cleanFeedbackVotes() {
-		Map<Session, List<User>> deletedFeedbackOfUsersInSession = feedbackStorage.cleanFeedbackVotes(cleanupFeedbackDelay);
-		/*
-		 * mapping (Session -> Users) is not suitable for web sockets, because we want to sent all affected
-		 * sessions to a single user in one go instead of sending multiple messages for each session. Hence,
-		 * we need the mapping (User -> Sessions)
-		 */
-		final Map<User, Set<Session>> affectedSessionsOfUsers = new HashMap<>();
-
-		for (Map.Entry<Session, List<User>> entry : deletedFeedbackOfUsersInSession.entrySet()) {
-			final Session session = entry.getKey();
-			final List<User> users = entry.getValue();
-			for (User user : users) {
-				Set<Session> affectedSessions;
-				if (affectedSessionsOfUsers.containsKey(user)) {
-					affectedSessions = affectedSessionsOfUsers.get(user);
-				} else {
-					affectedSessions = new HashSet<>();
-				}
-				affectedSessions.add(session);
-				affectedSessionsOfUsers.put(user, affectedSessions);
-			}
-		}
-		// Send feedback reset event to all affected users
-		for (Map.Entry<User, Set<Session>> entry : affectedSessionsOfUsers.entrySet()) {
-			final User user = entry.getKey();
-			final Set<Session> arsSessions = entry.getValue();
-			this.publisher.publishEvent(new DeleteFeedbackForSessionsEvent(this, arsSessions, user));
-		}
-		// For each session that has deleted feedback, send the new feedback to all clients
-		for (Session session : deletedFeedbackOfUsersInSession.keySet()) {
-			this.publisher.publishEvent(new NewFeedbackEvent(this, session));
-		}
-	}
-
-	@Override
-	public void cleanFeedbackVotesInSession(final String keyword, final int cleanupFeedbackDelayInMins) {
-		final Session session = sessionRepository.getSessionFromKeyword(keyword);
-		List<User> affectedUsers = feedbackStorage.cleanFeedbackVotesInSession(session, cleanupFeedbackDelayInMins);
-		Set<Session> sessionSet = new HashSet<>();
-		sessionSet.add(session);
-
-		// Send feedback reset event to all affected users
-		for (User user : affectedUsers) {
-			this.publisher.publishEvent(new DeleteFeedbackForSessionsEvent(this, sessionSet, user));
-		}
-		// send the new feedback to all clients in affected session
-		this.publisher.publishEvent(new NewFeedbackEvent(this, session));
-	}
-
-	@Override
-	public Feedback getFeedback(final String keyword) {
-		final Session session = sessionRepository.getSessionFromKeyword(keyword);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		return feedbackStorage.getFeedback(session);
-	}
-
-	@Override
-	public int getFeedbackCount(final String keyword) {
-		final Feedback feedback = this.getFeedback(keyword);
-		final List<Integer> values = feedback.getValues();
-		return values.get(Feedback.FEEDBACK_FASTER) + values.get(Feedback.FEEDBACK_OK)
-				+ values.get(Feedback.FEEDBACK_SLOWER) + values.get(Feedback.FEEDBACK_AWAY);
-	}
+public interface FeedbackService {
+	void cleanFeedbackVotes();
 
-	@Override
-	public double getAverageFeedback(final String sessionkey) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		final Feedback feedback = feedbackStorage.getFeedback(session);
-		final List<Integer> values = feedback.getValues();
-		final double count = values.get(Feedback.FEEDBACK_FASTER) + values.get(Feedback.FEEDBACK_OK)
-				+ values.get(Feedback.FEEDBACK_SLOWER) + values.get(Feedback.FEEDBACK_AWAY);
-		final double sum = values.get(Feedback.FEEDBACK_OK) + values.get(Feedback.FEEDBACK_SLOWER) * 2
-				+ values.get(Feedback.FEEDBACK_AWAY) * 3;
+	void cleanFeedbackVotesInSession(String keyword, int cleanupFeedbackDelayInMins);
 
-		if (Math.abs(count) < Z_THRESHOLD) {
-			throw new NoContentException();
-		}
-		return sum / count;
-	}
+	Feedback getFeedback(String keyword);
 
-	@Override
-	public long getAverageFeedbackRounded(final String sessionkey) {
-		return Math.round(getAverageFeedback(sessionkey));
-	}
+	int getFeedbackCount(String keyword);
 
-	@Override
-	public boolean saveFeedback(final String keyword, final int value, final User user) {
-		final Session session = sessionRepository.getSessionFromKeyword(keyword);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		feedbackStorage.saveFeedback(session, value, user);
+	double getAverageFeedback(String sessionkey);
 
-		this.publisher.publishEvent(new NewFeedbackEvent(this, session));
-		return true;
-	}
+	long getAverageFeedbackRounded(String sessionkey);
 
-	@Override
-	public Integer getMyFeedback(final String keyword, final User user) {
-		final Session session = sessionRepository.getSessionFromKeyword(keyword);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		return feedbackStorage.getMyFeedback(session, user);
-	}
+	boolean saveFeedback(String keyword, int value, User user);
 
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
+	Integer getMyFeedback(String keyword, User user);
 }
diff --git a/src/main/java/de/thm/arsnova/services/FeedbackServiceImpl.java b/src/main/java/de/thm/arsnova/services/FeedbackServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..8fe6eef74fad9489aa38666e30c4f3d8d6b9df67
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/FeedbackServiceImpl.java
@@ -0,0 +1,184 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.entities.Feedback;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.events.DeleteFeedbackForSessionsEvent;
+import de.thm.arsnova.events.NewFeedbackEvent;
+import de.thm.arsnova.exceptions.NoContentException;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.SessionRepository;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Performs all feedback related operations.
+ */
+@Service
+public class FeedbackServiceImpl implements FeedbackService, ApplicationEventPublisherAware {
+
+	private static final int DEFAULT_SCHEDULER_DELAY = 5000;
+	private static final double Z_THRESHOLD = 0.1;
+
+	/**
+	 * minutes, after which the feedback is deleted
+	 */
+	@Value("${feedback.cleanup}")
+	private int cleanupFeedbackDelay;
+
+	private SessionRepository sessionRepository;
+
+	private FeedbackStorageService feedbackStorage;
+
+	private ApplicationEventPublisher publisher;
+
+	public FeedbackServiceImpl(FeedbackStorageService feedbackStorage, SessionRepository sessionRepository) {
+		this.feedbackStorage = feedbackStorage;
+		this.sessionRepository = sessionRepository;
+	}
+
+	@Override
+	@Scheduled(fixedDelay = DEFAULT_SCHEDULER_DELAY)
+	public void cleanFeedbackVotes() {
+		Map<Session, List<User>> deletedFeedbackOfUsersInSession = feedbackStorage.cleanFeedbackVotes(cleanupFeedbackDelay);
+		/*
+		 * mapping (Session -> Users) is not suitable for web sockets, because we want to sent all affected
+		 * sessions to a single user in one go instead of sending multiple messages for each session. Hence,
+		 * we need the mapping (User -> Sessions)
+		 */
+		final Map<User, Set<Session>> affectedSessionsOfUsers = new HashMap<>();
+
+		for (Map.Entry<Session, List<User>> entry : deletedFeedbackOfUsersInSession.entrySet()) {
+			final Session session = entry.getKey();
+			final List<User> users = entry.getValue();
+			for (User user : users) {
+				Set<Session> affectedSessions;
+				if (affectedSessionsOfUsers.containsKey(user)) {
+					affectedSessions = affectedSessionsOfUsers.get(user);
+				} else {
+					affectedSessions = new HashSet<>();
+				}
+				affectedSessions.add(session);
+				affectedSessionsOfUsers.put(user, affectedSessions);
+			}
+		}
+		// Send feedback reset event to all affected users
+		for (Map.Entry<User, Set<Session>> entry : affectedSessionsOfUsers.entrySet()) {
+			final User user = entry.getKey();
+			final Set<Session> arsSessions = entry.getValue();
+			this.publisher.publishEvent(new DeleteFeedbackForSessionsEvent(this, arsSessions, user));
+		}
+		// For each session that has deleted feedback, send the new feedback to all clients
+		for (Session session : deletedFeedbackOfUsersInSession.keySet()) {
+			this.publisher.publishEvent(new NewFeedbackEvent(this, session));
+		}
+	}
+
+	@Override
+	public void cleanFeedbackVotesInSession(final String keyword, final int cleanupFeedbackDelayInMins) {
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
+		List<User> affectedUsers = feedbackStorage.cleanFeedbackVotesInSession(session, cleanupFeedbackDelayInMins);
+		Set<Session> sessionSet = new HashSet<>();
+		sessionSet.add(session);
+
+		// Send feedback reset event to all affected users
+		for (User user : affectedUsers) {
+			this.publisher.publishEvent(new DeleteFeedbackForSessionsEvent(this, sessionSet, user));
+		}
+		// send the new feedback to all clients in affected session
+		this.publisher.publishEvent(new NewFeedbackEvent(this, session));
+	}
+
+	@Override
+	public Feedback getFeedback(final String keyword) {
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		return feedbackStorage.getFeedback(session);
+	}
+
+	@Override
+	public int getFeedbackCount(final String keyword) {
+		final Feedback feedback = this.getFeedback(keyword);
+		final List<Integer> values = feedback.getValues();
+		return values.get(Feedback.FEEDBACK_FASTER) + values.get(Feedback.FEEDBACK_OK)
+				+ values.get(Feedback.FEEDBACK_SLOWER) + values.get(Feedback.FEEDBACK_AWAY);
+	}
+
+	@Override
+	public double getAverageFeedback(final String sessionkey) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		final Feedback feedback = feedbackStorage.getFeedback(session);
+		final List<Integer> values = feedback.getValues();
+		final double count = values.get(Feedback.FEEDBACK_FASTER) + values.get(Feedback.FEEDBACK_OK)
+				+ values.get(Feedback.FEEDBACK_SLOWER) + values.get(Feedback.FEEDBACK_AWAY);
+		final double sum = values.get(Feedback.FEEDBACK_OK) + values.get(Feedback.FEEDBACK_SLOWER) * 2
+				+ values.get(Feedback.FEEDBACK_AWAY) * 3;
+
+		if (Math.abs(count) < Z_THRESHOLD) {
+			throw new NoContentException();
+		}
+		return sum / count;
+	}
+
+	@Override
+	public long getAverageFeedbackRounded(final String sessionkey) {
+		return Math.round(getAverageFeedback(sessionkey));
+	}
+
+	@Override
+	public boolean saveFeedback(final String keyword, final int value, final User user) {
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		feedbackStorage.saveFeedback(session, value, user);
+
+		this.publisher.publishEvent(new NewFeedbackEvent(this, session));
+		return true;
+	}
+
+	@Override
+	public Integer getMyFeedback(final String keyword, final User user) {
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		return feedbackStorage.getMyFeedback(session, user);
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+		this.publisher = publisher;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/FeedbackStorageService.java b/src/main/java/de/thm/arsnova/services/FeedbackStorageService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5451ccfacefe8363be6aeb39a96262fbaed3087f
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/FeedbackStorageService.java
@@ -0,0 +1,16 @@
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.entities.Feedback;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+
+import java.util.List;
+import java.util.Map;
+
+public interface FeedbackStorageService {
+	Feedback getFeedback(Session session);
+	Integer getMyFeedback(Session session, User u);
+	void saveFeedback(Session session, int value, User user);
+	Map<Session, List<User>> cleanFeedbackVotes(int cleanupFeedbackDelay);
+	List<User> cleanFeedbackVotesInSession(Session session, int cleanupFeedbackDelayInMins);
+}
diff --git a/src/main/java/de/thm/arsnova/FeedbackStorage.java b/src/main/java/de/thm/arsnova/services/FeedbackStorageServiceImpl.java
similarity index 95%
rename from src/main/java/de/thm/arsnova/FeedbackStorage.java
rename to src/main/java/de/thm/arsnova/services/FeedbackStorageServiceImpl.java
index bb72be0d5e6bc89122922919464c18aff19ef98f..4dc11ef6318d9a6e975bc9fc9f1f18eb317505d3 100644
--- a/src/main/java/de/thm/arsnova/FeedbackStorage.java
+++ b/src/main/java/de/thm/arsnova/services/FeedbackStorageServiceImpl.java
@@ -15,11 +15,12 @@
  * 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;
+package de.thm.arsnova.services;
 
 import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
+import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Isolation;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -34,7 +35,8 @@ import java.util.concurrent.TimeUnit;
 /**
  * In-memory storage of feedback data.
  */
-public class FeedbackStorage {
+@Service
+public class FeedbackStorageServiceImpl implements FeedbackStorageService {
 	private static class FeedbackStorageObject {
 		private final int value;
 		private final Date timestamp;
@@ -60,6 +62,7 @@ public class FeedbackStorage {
 	private final Map<Session, Map<User, FeedbackStorageObject>> data =
 			new ConcurrentHashMap<>();
 
+	@Override
 	public Feedback getFeedback(final Session session) {
 		int a = 0;
 		int b = 0;
@@ -91,6 +94,7 @@ public class FeedbackStorage {
 		return new Feedback(a, b, c, d);
 	}
 
+	@Override
 	public Integer getMyFeedback(final Session session, final User u) {
 		if (data.get(session) == null) {
 			return null;
@@ -105,6 +109,7 @@ public class FeedbackStorage {
 		return null;
 	}
 
+	@Override
 	@Transactional(isolation = Isolation.READ_COMMITTED)
 	public void saveFeedback(final Session session, final int value, final User user) {
 		if (data.get(session) == null) {
@@ -114,6 +119,7 @@ public class FeedbackStorage {
 		data.get(session).put(user, new FeedbackStorageObject(value, user));
 	}
 
+	@Override
 	@Transactional(isolation = Isolation.READ_COMMITTED)
 	public Map<Session, List<User>> cleanFeedbackVotes(final int cleanupFeedbackDelay) {
 		final Map<Session, List<User>> removedFeedbackOfUsersInSession = new HashMap<>();
@@ -128,6 +134,7 @@ public class FeedbackStorage {
 		return removedFeedbackOfUsersInSession;
 	}
 
+	@Override
 	@Transactional(isolation = Isolation.READ_COMMITTED)
 	public List<User> cleanFeedbackVotesInSession(final Session session, final int cleanupFeedbackDelayInMins) {
 		final long timelimitInMillis = TimeUnit.MILLISECONDS.convert(cleanupFeedbackDelayInMins, TimeUnit.MINUTES);
diff --git a/src/main/java/de/thm/arsnova/services/IContentService.java b/src/main/java/de/thm/arsnova/services/IContentService.java
deleted file mode 100644
index 9b4e4f97bf40433a750034b28a3ee53d03ee19ef..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/IContentService.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Comment;
-import de.thm.arsnova.entities.CommentReadingCount;
-import de.thm.arsnova.entities.Content;
-import de.thm.arsnova.entities.User;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * The functionality the question service should provide.
- */
-public interface IContentService {
-	Content saveQuestion(Content content);
-
-	boolean saveQuestion(Comment comment);
-
-	Content getQuestion(String id);
-
-	List<Content> getSkillQuestions(String sessionkey);
-
-	int getSkillQuestionCount(String sessionkey);
-
-	void deleteQuestion(String questionId);
-
-	void deleteAllQuestions(String sessionKeyword);
-
-	void startNewPiRound(String questionId, User user);
-
-	void startNewPiRoundDelayed(String questionId, int time);
-
-	void cancelPiRoundChange(String questionId);
-
-	void cancelDelayedPiRoundChange(String questionId);
-
-	void resetPiRoundState(String questionId);
-
-	List<String> getUnAnsweredQuestionIds(String sessionKey);
-
-	Answer getMyAnswer(String questionId);
-
-	void readFreetextAnswer(String answerId, User user);
-
-	List<Answer> getAnswers(String questionId, int piRound, int offset, int limit);
-
-	List<Answer> getAnswers(String questionId, int offset, int limit);
-
-	List<Answer> getAllAnswers(String questionId, int offset, int limit);
-
-	int getAnswerCount(String questionId);
-
-	int getAnswerCount(String questionId, int piRound);
-
-	List<Answer> getFreetextAnswers(String questionId, int offset, int limit);
-
-	List<Answer> getMyAnswers(String sessionKey);
-
-	int getTotalAnswerCount(String sessionKey);
-
-	int getTotalAnswerCountByQuestion(String questionId);
-
-	int getInterposedCount(String sessionKey);
-
-	CommentReadingCount getInterposedReadingCount(String sessionKey, String username);
-
-	List<Comment> getInterposedQuestions(String sessionKey, int offset, int limit);
-
-	Comment readInterposedQuestion(String commentId);
-
-	Comment readInterposedQuestionInternal(String commentId, User user);
-
-	Content update(Content content);
-
-	Content update(Content content, User user);
-
-	void deleteAnswers(String questionId);
-
-	Answer saveAnswer(String questionId, de.thm.arsnova.entities.transport.Answer answer);
-
-	Answer updateAnswer(Answer answer);
-
-	void deleteAnswer(String questionId, String answerId);
-
-	void deleteInterposedQuestion(String commentId);
-
-	List<Content> getLectureQuestions(String sessionkey);
-
-	List<Content> getFlashcards(String sessionkey);
-
-	List<Content> getPreparationQuestions(String sessionkey);
-
-	int getLectureQuestionCount(String sessionkey);
-
-	int getFlashcardCount(String sessionkey);
-
-	int getPreparationQuestionCount(String sessionkey);
-
-	Map<String, Object> getAnswerAndAbstentionCountInternal(String questionid);
-
-	int countLectureQuestionAnswers(String sessionkey);
-
-	int countLectureQuestionAnswersInternal(String sessionkey);
-
-	int countPreparationQuestionAnswers(String sessionkey);
-
-	int countPreparationQuestionAnswersInternal(String sessionkey);
-
-	int countFlashcardsForUserInternal(String sessionkey);
-
-	void deleteLectureQuestions(String sessionkey);
-
-	void deleteFlashcards(String sessionkey);
-
-	void deletePreparationQuestions(String sessionkey);
-
-	List<String> getUnAnsweredLectureQuestionIds(String sessionkey);
-
-	List<String> getUnAnsweredLectureQuestionIds(String sessionKey, User user);
-
-	List<String> getUnAnsweredPreparationQuestionIds(String sessionkey);
-
-	List<String> getUnAnsweredPreparationQuestionIds(String sessionKey, User user);
-
-	void deleteAllInterposedQuestions(String sessionKeyword);
-
-	void publishAll(String sessionkey, boolean publish);
-
-	void publishQuestions(String sessionkey, boolean publish, List<Content> contents);
-
-	void deleteAllQuestionsAnswers(String sessionkey);
-
-	void deleteAllPreparationAnswers(String sessionkey);
-
-	void deleteAllLectureAnswers(String sessionkey);
-
-	int getAbstentionAnswerCount(String questionId);
-
-	String getImage(String questionId, String answerId);
-
-	void setVotingAdmission(String questionId, boolean disableVoting);
-
-	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Content> contents);
-
-	void setVotingAdmissionForAllQuestions(String sessionkey, boolean disableVoting);
-
-	String getQuestionImage(String questionId);
-
-	String getQuestionFcImage(String questionId);
-
-	List<Content> replaceImageData(List<Content> contents);
-
-}
diff --git a/src/main/java/de/thm/arsnova/services/IFeedbackService.java b/src/main/java/de/thm/arsnova/services/IFeedbackService.java
deleted file mode 100644
index dc0210164abc326739e2a300fcc636f4ff7e710c..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/IFeedbackService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.entities.Feedback;
-import de.thm.arsnova.entities.User;
-
-/**
- * The functionality the feedback service should provide.
- */
-public interface IFeedbackService {
-	void cleanFeedbackVotes();
-
-	void cleanFeedbackVotesInSession(String keyword, int cleanupFeedbackDelayInMins);
-
-	Feedback getFeedback(String keyword);
-
-	int getFeedbackCount(String keyword);
-
-	double getAverageFeedback(String sessionkey);
-
-	long getAverageFeedbackRounded(String sessionkey);
-
-	boolean saveFeedback(String keyword, int value, User user);
-
-	Integer getMyFeedback(String keyword, User user);
-}
diff --git a/src/main/java/de/thm/arsnova/services/IMotdService.java b/src/main/java/de/thm/arsnova/services/IMotdService.java
deleted file mode 100644
index a9488e4134b60f43be582c9a6f521bff83010ff4..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/IMotdService.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.entities.Motd;
-import de.thm.arsnova.entities.MotdList;
-
-import java.util.Date;
-import java.util.List;
-
-/**
- * The functionality the motd service should provide.
- */
-public interface IMotdService {
-	Motd getMotd(String keyword);
-
-	List<Motd> getAdminMotds();  //all w/o the sessionmotds
-
-	List<Motd> getAllSessionMotds(final String sessionkey);
-
-	List<Motd> getCurrentMotds(final Date clientdate, final String audience, final String sessionkey);
-
-	List<Motd> filterMotdsByDate(List<Motd> list, Date clientdate);
-
-	List<Motd> filterMotdsByList(List<Motd> list, MotdList motdList);
-
-	void deleteMotd(Motd motd);
-
-	void deleteSessionMotd(final String sessionkey, Motd motd);
-
-	Motd saveMotd(Motd motd);
-
-	Motd saveSessionMotd(final String sessionkey, final Motd motd);
-
-	Motd updateMotd(Motd motd);
-
-	Motd updateSessionMotd(final String sessionkey, Motd motd);
-
-	MotdList getMotdListForUser(final String username);
-
-	MotdList saveUserMotdList(MotdList motdList);
-
-	MotdList updateUserMotdList(MotdList userMotdList);
-}
diff --git a/src/main/java/de/thm/arsnova/services/ISessionService.java b/src/main/java/de/thm/arsnova/services/ISessionService.java
deleted file mode 100644
index c6796f7cebdf90210ac34e4c197242d1eb9ccd7c..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/ISessionService.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.entities.Session;
-import de.thm.arsnova.entities.SessionFeature;
-import de.thm.arsnova.entities.SessionInfo;
-import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.ImportExportSession;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
-
-import java.util.List;
-import java.util.UUID;
-
-/**
- * The functionality the session service should provide.
- */
-public interface ISessionService {
-	Session getSession(String keyword);
-
-	Session getSessionForAdmin(final String keyword);
-
-	Session getSessionInternal(String keyword, User user);
-
-	Session saveSession(Session session);
-
-	boolean sessionKeyAvailable(String keyword);
-
-	String generateKeyword();
-
-	List<Session> getUserSessions(String username);
-
-	List<Session> getUserVisitedSessions(String username);
-
-	List<Session> getMySessions(int offset, int limit);
-
-	List<Session> getMyVisitedSessions(int offset, int limit);
-
-	int countSessions(List<Course> courses);
-
-	int activeUsers(String sessionkey);
-
-	Session setActive(String sessionkey, Boolean lock);
-
-	Session joinSession(String keyword, UUID socketId);
-
-	Session updateSession(String sessionkey, Session session);
-
-	Session changeSessionCreator(String sessionkey, String newCreator);
-
-	Session updateSessionInternal(Session session, User user);
-
-	void deleteSession(String sessionkey);
-
-	LearningProgressValues getLearningProgress(String sessionkey, String progressType, String questionVariant);
-
-	LearningProgressValues getMyLearningProgress(String sessionkey, String progressType, String questionVariant);
-
-	List<SessionInfo> getMySessionsInfo(int offset, int limit);
-
-	List<SessionInfo> getPublicPoolSessionsInfo();
-
-	List<SessionInfo> getMyPublicPoolSessionsInfo();
-
-	List<SessionInfo> getMyVisitedSessionsInfo(int offset, int limit);
-
-	SessionInfo importSession(ImportExportSession session);
-
-	ImportExportSession exportSession(String sessionkey, Boolean withAnswerStatistics, Boolean withFeedbackQuestions);
-
-	SessionInfo copySessionToPublicPool(String sessionkey, de.thm.arsnova.entities.transport.ImportExportSession.PublicPool pp);
-
-	SessionFeature getSessionFeatures(String sessionkey);
-
-	SessionFeature changeSessionFeatures(String sessionkey, SessionFeature features);
-
-	boolean lockFeedbackInput(String sessionkey, Boolean lock);
-
-	boolean flipFlashcards(String sessionkey, Boolean flip);
-
-	void deleteInactiveSessions();
-
-	void deleteInactiveVisitedSessionLists();
-}
diff --git a/src/main/java/de/thm/arsnova/services/IStatisticsService.java b/src/main/java/de/thm/arsnova/services/IStatisticsService.java
deleted file mode 100644
index 86f72807b887166dd54e9f0a680591b9841d87e2..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/IStatisticsService.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.entities.Statistics;
-
-/**
- * The functionality the statistics service should provide.
- */
-public interface IStatisticsService {
-	Statistics getStatistics();
-}
diff --git a/src/main/java/de/thm/arsnova/services/IUserService.java b/src/main/java/de/thm/arsnova/services/IUserService.java
deleted file mode 100644
index 37b4d0b6076aa568cab3f86b420eb8b5aee729b0..0000000000000000000000000000000000000000
--- a/src/main/java/de/thm/arsnova/services/IUserService.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2017 The ARSnova Team
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.services;
-
-import de.thm.arsnova.entities.DbUser;
-import de.thm.arsnova.entities.User;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * The functionality the user service should provide.
- */
-public interface IUserService {
-	User getCurrentUser();
-
-	boolean isBannedFromLogin(String addr);
-
-	void increaseFailedLoginCount(String addr);
-
-	User getUser2SocketId(UUID socketId);
-
-	void putUser2SocketId(UUID socketId, User user);
-
-	void removeUser2SocketId(UUID socketId);
-
-	Set<Map.Entry<UUID, User>> socketId2User();
-
-	boolean isUserInSession(User user, String keyword);
-
-	Set<User> getUsersInSession(String keyword);
-
-	String getSessionForUser(String username);
-
-	void addUserToSessionBySocketId(UUID socketId, String keyword);
-
-	void removeUserFromSessionBySocketId(UUID socketId);
-
-	void removeUserFromMaps(User user);
-
-	int loggedInUsers();
-
-	DbUser getDbUser(String username);
-
-	DbUser createDbUser(String username, String password);
-
-	DbUser updateDbUser(DbUser dbUser);
-
-	DbUser deleteDbUser(String username);
-
-	void initiatePasswordReset(String username);
-
-	boolean resetPassword(DbUser dbUser, String key, String password);
-}
diff --git a/src/main/java/de/thm/arsnova/services/MotdService.java b/src/main/java/de/thm/arsnova/services/MotdService.java
index 674525ec2afe51c9fd917f0d1a992710dc2eb9cf..9df6a5afdc0a69bf632322d332ad3f16e41e0738 100644
--- a/src/main/java/de/thm/arsnova/services/MotdService.java
+++ b/src/main/java/de/thm/arsnova/services/MotdService.java
@@ -9,189 +9,51 @@
  *
  * 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
+ * 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/>.
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 package de.thm.arsnova.services;
 
 import de.thm.arsnova.entities.Motd;
 import de.thm.arsnova.entities.MotdList;
-import de.thm.arsnova.entities.Session;
-import de.thm.arsnova.entities.User;
-import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.persistance.MotdListRepository;
-import de.thm.arsnova.persistance.MotdRepository;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
+
 import java.util.Date;
-import java.util.HashSet;
 import java.util.List;
-import java.util.StringTokenizer;
+
 /**
- * Performs all question, interposed question, and answer related operations.
+ * The functionality the motd service should provide.
  */
-@Service
-public class MotdService implements IMotdService {
-	@Autowired
-	private IUserService userService;
-
-	@Autowired
-	private ISessionService sessionService;
-
-	@Autowired
-	private MotdRepository motdRepository;
-
-	@Autowired
-	private MotdListRepository motdListRepository;
-
-  @Override
-  @PreAuthorize("isAuthenticated()")
-  public Motd getMotd(final String key) {
-    return motdRepository.getMotdByKey(key);
-  }
-
-  @Override
-  @PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
-  public List<Motd> getAdminMotds() {
-    return motdRepository.getAdminMotds();
-  }
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public List<Motd> getAllSessionMotds(final String sessionkey) {
-		return motdRepository.getMotdsForSession(sessionkey);
-	}
-
-	@Override
-	public List<Motd> getCurrentMotds(final Date clientdate, final String audience, final String sessionkey) {
-		final List<Motd> motds;
-		switch (audience) {
-			case "all": motds = motdRepository.getMotdsForAll(); break;
-			case "loggedIn": motds = motdRepository.getMotdsForLoggedIn(); break;
-			case "students": motds = motdRepository.getMotdsForStudents(); break;
-			case "tutors": motds = motdRepository.getMotdsForTutors(); break;
-			case "session": motds = motdRepository.getMotdsForSession(sessionkey); break;
-			default: motds = motdRepository.getMotdsForAll(); break;
-		}
-		return filterMotdsByDate(motds, clientdate);
-	}
-
-  @Override
-  public List<Motd> filterMotdsByDate(List<Motd> list, Date clientdate) {
-		List<Motd> returns = new ArrayList<>();
-		for (Motd motd : list) {
-			if (motd.getStartdate().before(clientdate) && motd.getEnddate().after(clientdate)) {
-				returns.add(motd);
-			}
-		}
-		return returns;
-  }
-
-	@Override
-	public List<Motd> filterMotdsByList(List<Motd> list, MotdList motdlist) {
-		if (motdlist != null && motdlist.getMotdkeys() != null && !motdlist.getMotdkeys().isEmpty()) {
-			List<Motd> returns = new ArrayList<>();
-			HashSet<String> keys = new HashSet<>(500);  // Or a more realistic size
-			StringTokenizer st = new StringTokenizer(motdlist.getMotdkeys(), ",");
-			while (st.hasMoreTokens()) {
-				keys.add(st.nextToken());
-			}
-			for (Motd motd : list) {
-				if (!keys.contains(motd.getMotdkey())) {
-					returns.add(motd);
-				}
-			}
-			return returns;
-		} else {
-			return list;
-		}
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
-	public Motd saveMotd(final Motd motd) {
-		return createOrUpdateMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public Motd saveSessionMotd(final String sessionkey, final Motd motd) {
-		Session session = sessionService.getSession(sessionkey);
-		motd.setSessionId(session.getId());
-
-
-		return createOrUpdateMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
-	public Motd updateMotd(final Motd motd) {
-		return createOrUpdateMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public Motd updateSessionMotd(final String sessionkey, final Motd motd) {
-		return createOrUpdateMotd(motd);
-	}
-
-	private Motd createOrUpdateMotd(final Motd motd) {
-		if (motd.getMotdkey() != null) {
-			Motd oldMotd = motdRepository.getMotdByKey(motd.getMotdkey());
-			if (!(motd.getId().equals(oldMotd.getId()) && motd.getSessionkey().equals(oldMotd.getSessionkey())
-					&& motd.getAudience().equals(oldMotd.getAudience()))) {
-				throw new BadRequestException();
-			}
-		}
-
-		return motdRepository.createOrUpdateMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
-	public void deleteMotd(Motd motd) {
-		motdRepository.deleteMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public void deleteSessionMotd(final String sessionkey, Motd motd) {
-		motdRepository.deleteMotd(motd);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public MotdList getMotdListForUser(final String username) {
-		final User user = userService.getCurrentUser();
-		if (username.equals(user.getUsername()) && !"guest".equals(user.getType())) {
-			return motdListRepository.getMotdListForUser(username);
-		}
-		return null;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public MotdList saveUserMotdList(MotdList motdList) {
-		final User user = userService.getCurrentUser();
-		if (user.getUsername().equals(motdList.getUsername())) {
-			return motdListRepository.createOrUpdateMotdList(motdList);
-		}
-		return null;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public MotdList updateUserMotdList(MotdList motdList) {
-		final User user = userService.getCurrentUser();
-		if (user.getUsername().equals(motdList.getUsername())) {
-			return motdListRepository.createOrUpdateMotdList(motdList);
-		}
-		return null;
-	}
+public interface MotdService {
+	Motd getMotd(String keyword);
+
+	List<Motd> getAdminMotds();  //all w/o the sessionmotds
+
+	List<Motd> getAllSessionMotds(final String sessionkey);
+
+	List<Motd> getCurrentMotds(final Date clientdate, final String audience, final String sessionkey);
+
+	List<Motd> filterMotdsByDate(List<Motd> list, Date clientdate);
+
+	List<Motd> filterMotdsByList(List<Motd> list, MotdList motdList);
+
+	void deleteMotd(Motd motd);
+
+	void deleteSessionMotd(final String sessionkey, Motd motd);
+
+	Motd saveMotd(Motd motd);
+
+	Motd saveSessionMotd(final String sessionkey, final Motd motd);
+
+	Motd updateMotd(Motd motd);
+
+	Motd updateSessionMotd(final String sessionkey, Motd motd);
+
+	MotdList getMotdListForUser(final String username);
+
+	MotdList saveUserMotdList(MotdList motdList);
+
+	MotdList updateUserMotdList(MotdList userMotdList);
 }
diff --git a/src/main/java/de/thm/arsnova/services/MotdServiceImpl.java b/src/main/java/de/thm/arsnova/services/MotdServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..d373b001f8ecee18a2862f0c7be61e28f355dd23
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/MotdServiceImpl.java
@@ -0,0 +1,197 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.entities.MotdList;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.persistance.MotdListRepository;
+import de.thm.arsnova.persistance.MotdRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+/**
+ * Performs all question, interposed question, and answer related operations.
+ */
+@Service
+public class MotdServiceImpl implements MotdService {
+	@Autowired
+	private UserService userService;
+
+	@Autowired
+	private SessionService sessionService;
+
+	@Autowired
+	private MotdRepository motdRepository;
+
+	@Autowired
+	private MotdListRepository motdListRepository;
+
+  @Override
+  @PreAuthorize("isAuthenticated()")
+  public Motd getMotd(final String key) {
+    return motdRepository.getMotdByKey(key);
+  }
+
+  @Override
+  @PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
+  public List<Motd> getAdminMotds() {
+    return motdRepository.getAdminMotds();
+  }
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public List<Motd> getAllSessionMotds(final String sessionkey) {
+		return motdRepository.getMotdsForSession(sessionkey);
+	}
+
+	@Override
+	public List<Motd> getCurrentMotds(final Date clientdate, final String audience, final String sessionkey) {
+		final List<Motd> motds;
+		switch (audience) {
+			case "all": motds = motdRepository.getMotdsForAll(); break;
+			case "loggedIn": motds = motdRepository.getMotdsForLoggedIn(); break;
+			case "students": motds = motdRepository.getMotdsForStudents(); break;
+			case "tutors": motds = motdRepository.getMotdsForTutors(); break;
+			case "session": motds = motdRepository.getMotdsForSession(sessionkey); break;
+			default: motds = motdRepository.getMotdsForAll(); break;
+		}
+		return filterMotdsByDate(motds, clientdate);
+	}
+
+  @Override
+  public List<Motd> filterMotdsByDate(List<Motd> list, Date clientdate) {
+		List<Motd> returns = new ArrayList<>();
+		for (Motd motd : list) {
+			if (motd.getStartdate().before(clientdate) && motd.getEnddate().after(clientdate)) {
+				returns.add(motd);
+			}
+		}
+		return returns;
+  }
+
+	@Override
+	public List<Motd> filterMotdsByList(List<Motd> list, MotdList motdlist) {
+		if (motdlist != null && motdlist.getMotdkeys() != null && !motdlist.getMotdkeys().isEmpty()) {
+			List<Motd> returns = new ArrayList<>();
+			HashSet<String> keys = new HashSet<>(500);  // Or a more realistic size
+			StringTokenizer st = new StringTokenizer(motdlist.getMotdkeys(), ",");
+			while (st.hasMoreTokens()) {
+				keys.add(st.nextToken());
+			}
+			for (Motd motd : list) {
+				if (!keys.contains(motd.getMotdkey())) {
+					returns.add(motd);
+				}
+			}
+			return returns;
+		} else {
+			return list;
+		}
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
+	public Motd saveMotd(final Motd motd) {
+		return createOrUpdateMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public Motd saveSessionMotd(final String sessionkey, final Motd motd) {
+		Session session = sessionService.getSession(sessionkey);
+		motd.setSessionId(session.getId());
+
+
+		return createOrUpdateMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
+	public Motd updateMotd(final Motd motd) {
+		return createOrUpdateMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public Motd updateSessionMotd(final String sessionkey, final Motd motd) {
+		return createOrUpdateMotd(motd);
+	}
+
+	private Motd createOrUpdateMotd(final Motd motd) {
+		if (motd.getMotdkey() != null) {
+			Motd oldMotd = motdRepository.getMotdByKey(motd.getMotdkey());
+			if (!(motd.getId().equals(oldMotd.getId()) && motd.getSessionkey().equals(oldMotd.getSessionkey())
+					&& motd.getAudience().equals(oldMotd.getAudience()))) {
+				throw new BadRequestException();
+			}
+		}
+
+		return motdRepository.createOrUpdateMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
+	public void deleteMotd(Motd motd) {
+		motdRepository.deleteMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public void deleteSessionMotd(final String sessionkey, Motd motd) {
+		motdRepository.deleteMotd(motd);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public MotdList getMotdListForUser(final String username) {
+		final User user = userService.getCurrentUser();
+		if (username.equals(user.getUsername()) && !"guest".equals(user.getType())) {
+			return motdListRepository.getMotdListForUser(username);
+		}
+		return null;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public MotdList saveUserMotdList(MotdList motdList) {
+		final User user = userService.getCurrentUser();
+		if (user.getUsername().equals(motdList.getUsername())) {
+			return motdListRepository.createOrUpdateMotdList(motdList);
+		}
+		return null;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public MotdList updateUserMotdList(MotdList motdList) {
+		final User user = userService.getCurrentUser();
+		if (user.getUsername().equals(motdList.getUsername())) {
+			return motdListRepository.createOrUpdateMotdList(motdList);
+		}
+		return null;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java
index 0257be71d75230b9d3dcfc2a33ea491d571f3f06..25e7fe199c599edc5ca9b634e2007d5a6b5ec5ff 100644
--- a/src/main/java/de/thm/arsnova/services/SessionService.java
+++ b/src/main/java/de/thm/arsnova/services/SessionService.java
@@ -17,499 +17,84 @@
  */
 package de.thm.arsnova.services;
 
-import de.thm.arsnova.ImageUtils;
-import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.model.Course;
-import de.thm.arsnova.domain.ILearningProgressFactory;
-import de.thm.arsnova.domain.LearningProgress;
-import de.thm.arsnova.entities.LearningProgressOptions;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.SessionFeature;
 import de.thm.arsnova.entities.SessionInfo;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.transport.ImportExportSession;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
-import de.thm.arsnova.events.DeleteSessionEvent;
-import de.thm.arsnova.events.FeatureChangeEvent;
-import de.thm.arsnova.events.FlipFlashcardsEvent;
-import de.thm.arsnova.events.LockFeedbackEvent;
-import de.thm.arsnova.events.NewSessionEvent;
-import de.thm.arsnova.events.StatusSessionEvent;
-import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.exceptions.ForbiddenException;
-import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.exceptions.PayloadTooLargeException;
-import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.persistance.SessionRepository;
-import de.thm.arsnova.persistance.VisitedSessionRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Service;
-
-import java.io.Serializable;
-import java.util.Comparator;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
+
 import java.util.List;
 import java.util.UUID;
 
 /**
- * Performs all session related operations.
+ * The functionality the session service should provide.
  */
-@Service
-public class SessionService implements ISessionService, ApplicationEventPublisherAware {
+public interface SessionService {
+	Session getSession(String keyword);
+
+	Session getSessionForAdmin(final String keyword);
+
+	Session getSessionInternal(String keyword, User user);
+
+	Session saveSession(Session session);
+
+	boolean sessionKeyAvailable(String keyword);
+
+	String generateKeyword();
+
+	List<Session> getUserSessions(String username);
+
+	List<Session> getUserVisitedSessions(String username);
+
+	List<Session> getMySessions(int offset, int limit);
+
+	List<Session> getMyVisitedSessions(int offset, int limit);
+
+	int countSessions(List<Course> courses);
+
+	int activeUsers(String sessionkey);
+
+	Session setActive(String sessionkey, Boolean lock);
+
+	Session joinSession(String keyword, UUID socketId);
+
+	Session updateSession(String sessionkey, Session session);
+
+	Session changeSessionCreator(String sessionkey, String newCreator);
+
+	Session updateSessionInternal(Session session, User user);
+
+	void deleteSession(String sessionkey);
+
+	ScoreStatistics getLearningProgress(String sessionkey, String type, String questionVariant);
+
+	ScoreStatistics getMyLearningProgress(String sessionkey, String type, String questionVariant);
+
+	List<SessionInfo> getMySessionsInfo(int offset, int limit);
+
+	List<SessionInfo> getPublicPoolSessionsInfo();
+
+	List<SessionInfo> getMyPublicPoolSessionsInfo();
+
+	List<SessionInfo> getMyVisitedSessionsInfo(int offset, int limit);
+
+	SessionInfo importSession(ImportExportSession session);
+
+	ImportExportSession exportSession(String sessionkey, Boolean withAnswerStatistics, Boolean withFeedbackQuestions);
+
+	SessionInfo copySessionToPublicPool(String sessionkey, de.thm.arsnova.entities.transport.ImportExportSession.PublicPool pp);
+
+	SessionFeature getSessionFeatures(String sessionkey);
 
-	@Autowired
-	private SessionRepository sessionRepository;
+	SessionFeature changeSessionFeatures(String sessionkey, SessionFeature features);
 
-	public static class SessionNameComparator implements Comparator<Session>, Serializable {
-		private static final long serialVersionUID = 1L;
+	boolean lockFeedbackInput(String sessionkey, Boolean lock);
 
-		@Override
-		public int compare(final Session session1, final Session session2) {
-			return session1.getName().compareToIgnoreCase(session2.getName());
-		}
-	}
+	boolean flipFlashcards(String sessionkey, Boolean flip);
 
-	public static class SessionInfoNameComparator implements Comparator<SessionInfo>, Serializable {
-		private static final long serialVersionUID = 1L;
+	void deleteInactiveSessions();
 
-		@Override
-		public int compare(final SessionInfo session1, final SessionInfo session2) {
-			return session1.getName().compareToIgnoreCase(session2.getName());
-		}
-	}
-
-	public static class SessionShortNameComparator implements Comparator<Session>, Serializable {
-		private static final long serialVersionUID = 1L;
-
-		@Override
-		public int compare(final Session session1, final Session session2) {
-			return session1.getShortName().compareToIgnoreCase(session2.getShortName());
-		}
-	}
-
-	public static class SessionInfoShortNameComparator implements Comparator<SessionInfo>, Serializable {
-		private static final long serialVersionUID = 1L;
-
-		@Override
-		public int compare(final SessionInfo session1, final SessionInfo session2) {
-			return session1.getShortName().compareToIgnoreCase(session2.getShortName());
-		}
-	}
-
-	private static final long SESSION_INACTIVITY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
-
-	@Autowired
-	private VisitedSessionRepository visitedSessionRepository;
-
-	@Autowired
-	private IUserService userService;
-
-	@Autowired
-	private IFeedbackService feedbackService;
-
-	@Autowired
-	private ILearningProgressFactory learningProgressFactory;
-
-	@Autowired(required = false)
-	private ConnectorClient connectorClient;
-
-	@Autowired
-	private ImageUtils imageUtils;
-
-	@Value("${session.guest-session.cleanup-days:0}")
-	private int guestSessionInactivityThresholdDays;
-
-	@Value("${pp.logofilesize_b}")
-	private int uploadFileSizeByte;
-
-	private ApplicationEventPublisher publisher;
-
-	private static final Logger logger = LoggerFactory.getLogger(SessionService.class);
-
-	@Scheduled(fixedDelay = SESSION_INACTIVITY_CHECK_INTERVAL_MS)
-	public void deleteInactiveSessions() {
-		if (guestSessionInactivityThresholdDays > 0) {
-			logger.info("Delete inactive sessions.");
-			long unixTime = System.currentTimeMillis();
-			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
-			sessionRepository.deleteInactiveGuestSessions(lastActivityBefore);
-		}
-	}
-
-	@Scheduled(fixedDelay = SESSION_INACTIVITY_CHECK_INTERVAL_MS)
-	public void deleteInactiveVisitedSessionLists() {
-		if (guestSessionInactivityThresholdDays > 0) {
-			logger.info("Delete lists of visited session for inactive users.");
-			long unixTime = System.currentTimeMillis();
-			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
-			visitedSessionRepository.deleteInactiveGuestVisitedSessionLists(lastActivityBefore);
-		}
-	}
-
-	@Override
-	public Session joinSession(final String keyword, final UUID socketId) {
-		/* Socket.IO solution */
-
-		Session session = null != keyword ? sessionRepository.getSessionFromKeyword(keyword) : null;
-
-		if (null == session) {
-			userService.removeUserFromSessionBySocketId(socketId);
-			return null;
-		}
-		final User user = userService.getUser2SocketId(socketId);
-
-		userService.addUserToSessionBySocketId(socketId, keyword);
-
-		if (session.getCreator().equals(user.getUsername())) {
-			sessionRepository.updateSessionOwnerActivity(session);
-		}
-		sessionRepository.registerAsOnlineUser(user, session);
-
-		if (connectorClient != null && session.isCourseSession()) {
-			final String courseid = session.getCourseId();
-			if (!connectorClient.getMembership(user.getUsername(), courseid).isMember()) {
-				throw new ForbiddenException("User is no course member.");
-			}
-		}
-
-		return session;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Session getSession(final String keyword) {
-		final User user = userService.getCurrentUser();
-		return Session.anonymizedCopy(this.getSessionInternal(keyword, user));
-	}
-
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public Session getSessionForAdmin(final String keyword) {
-		return sessionRepository.getSessionFromKeyword(keyword);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public Session getSessionInternal(final String keyword, final User user) {
-		final Session session = sessionRepository.getSessionFromKeyword(keyword);
-		if (session == null) {
-			throw new NotFoundException();
-		}
-		if (!session.isActive()) {
-			if (user.hasRole(UserSessionService.Role.STUDENT)) {
-				throw new ForbiddenException("User is not session creator.");
-			} else if (user.hasRole(UserSessionService.Role.SPEAKER) && !session.isCreator(user)) {
-				throw new ForbiddenException("User is not session creator.");
-			}
-		}
-		if (connectorClient != null && session.isCourseSession()) {
-			final String courseid = session.getCourseId();
-			if (!connectorClient.getMembership(user.getUsername(), courseid).isMember()) {
-				throw new ForbiddenException("User is no course member.");
-			}
-		}
-		return session;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public List<Session> getUserSessions(String username) {
-		return sessionRepository.getSessionsForUsername(username, 0, 0);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Session> getMySessions(final int offset, final int limit) {
-		return sessionRepository.getMySessions(userService.getCurrentUser(), offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getPublicPoolSessionsInfo() {
-		return sessionRepository.getPublicPoolSessionsInfo();
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getMyPublicPoolSessionsInfo() {
-		return sessionRepository.getMyPublicPoolSessionsInfo(userService.getCurrentUser());
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getMySessionsInfo(final int offset, final int limit) {
-		final User user = userService.getCurrentUser();
-		return sessionRepository.getMySessionsInfo(user, offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<Session> getMyVisitedSessions(final int offset, final int limit) {
-		return sessionRepository.getVisitedSessionsForUsername(userService.getCurrentUser().getUsername(), offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(1, 'motd', 'admin')")
-	public List<Session> getUserVisitedSessions(String username) {
-		return sessionRepository.getVisitedSessionsForUsername(username, 0, 0);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public List<SessionInfo> getMyVisitedSessionsInfo(final int offset, final int limit) {
-		return sessionRepository.getMyVisitedSessionsInfo(userService.getCurrentUser(), offset, limit);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public Session saveSession(final Session session) {
-		if (connectorClient != null && session.getCourseId() != null) {
-			if (!connectorClient.getMembership(
-					userService.getCurrentUser().getUsername(), session.getCourseId()).isMember()
-					) {
-				throw new ForbiddenException();
-			}
-		}
-		handleLogo(session);
-
-		// set some default values
-		LearningProgressOptions lpo = new LearningProgressOptions();
-		lpo.setType("questions");
-		session.setLearningProgressOptions(lpo);
-
-		SessionFeature sf = new SessionFeature();
-		sf.setLecture(true);
-		sf.setFeedback(true);
-		sf.setInterposed(true);
-		sf.setJitt(true);
-		sf.setLearningProgress(true);
-		sf.setPi(true);
-		session.setFeatures(sf);
-
-		final Session result = sessionRepository.saveSession(userService.getCurrentUser(), session);
-		this.publisher.publishEvent(new NewSessionEvent(this, result));
-		return result;
-	}
-
-	@Override
-	public boolean sessionKeyAvailable(final String keyword) {
-		return sessionRepository.sessionKeyAvailable(keyword);
-	}
-
-	@Override
-	public String generateKeyword() {
-		final int low = 10000000;
-		final int high = 100000000;
-		final String keyword = String
-				.valueOf((int) (Math.random() * (high - low) + low));
-
-		if (sessionKeyAvailable(keyword)) {
-			return keyword;
-		}
-		return generateKeyword();
-	}
-
-	@Override
-	public int countSessions(final List<Course> courses) {
-		final List<Session> sessions = sessionRepository.getCourseSessions(courses);
-		if (sessions == null) {
-			return 0;
-		}
-		return sessions.size();
-	}
-
-	@Override
-	public int activeUsers(final String sessionkey) {
-		return userService.getUsersInSession(sessionkey).size();
-	}
-
-	@Override
-	public Session setActive(final String sessionkey, final Boolean lock) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (!session.isCreator(user)) {
-			throw new ForbiddenException("User is not session creator.");
-		}
-		session.setActive(lock);
-		this.publisher.publishEvent(new StatusSessionEvent(this, session));
-		return sessionRepository.updateSession(session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#session, 'owner')")
-	public Session updateSession(final String sessionkey, final Session session) {
-		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
-
-		existingSession.setActive(session.isActive());
-		existingSession.setShortName(session.getShortName());
-		existingSession.setPpAuthorName(session.getPpAuthorName());
-		existingSession.setPpAuthorMail(session.getPpAuthorMail());
-		existingSession.setShortName(session.getShortName());
-		existingSession.setPpAuthorName(session.getPpAuthorName());
-		existingSession.setPpFaculty(session.getPpFaculty());
-		existingSession.setName(session.getName());
-		existingSession.setPpUniversity(session.getPpUniversity());
-		existingSession.setPpDescription(session.getPpDescription());
-		existingSession.setPpLevel(session.getPpLevel());
-		existingSession.setPpLicense(session.getPpLicense());
-		existingSession.setPpSubject(session.getPpSubject());
-		existingSession.setFeedbackLock(session.getFeedbackLock());
-
-		handleLogo(session);
-		existingSession.setPpLogo(session.getPpLogo());
-
-		return sessionRepository.updateSession(existingSession);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
-	public Session changeSessionCreator(String sessionkey, String newCreator) {
-		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
-		if (existingSession == null) {
-			throw new NullPointerException("Could not load session " + sessionkey + ".");
-		}
-		return sessionRepository.changeSessionCreator(existingSession, newCreator);
-	}
-
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public Session updateSessionInternal(final Session session, final User user) {
-		if (session.isCreator(user)) {
-			return sessionRepository.updateSession(session);
-		}
-		return null;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public void deleteSession(final String sessionkey) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-
-		sessionRepository.deleteSession(session);
-
-		this.publisher.publishEvent(new DeleteSessionEvent(this, session));
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public LearningProgressValues getLearningProgress(final String sessionkey, final String progressType, final String questionVariant) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		LearningProgress learningProgress = learningProgressFactory.create(progressType, questionVariant);
-		return learningProgress.getCourseProgress(session);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public LearningProgressValues getMyLearningProgress(final String sessionkey, final String progressType, final String questionVariant) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		final User user = userService.getCurrentUser();
-		LearningProgress learningProgress = learningProgressFactory.create(progressType, questionVariant);
-		return learningProgress.getMyProgress(session, user);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated()")
-	public SessionInfo importSession(ImportExportSession importSession) {
-		final User user = userService.getCurrentUser();
-		final SessionInfo info = sessionRepository.importSession(user, importSession);
-		if (info == null) {
-			throw new NullPointerException("Could not import session.");
-		}
-		return info;
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public ImportExportSession exportSession(String sessionkey, Boolean withAnswerStatistics, Boolean withFeedbackQuestions) {
-		return sessionRepository.exportSession(sessionkey, withAnswerStatistics, withFeedbackQuestions);
-	}
-
-	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
-	public SessionInfo copySessionToPublicPool(String sessionkey, de.thm.arsnova.entities.transport.ImportExportSession.PublicPool pp) {
-		ImportExportSession temp = sessionRepository.exportSession(sessionkey, false, false);
-		temp.getSession().setPublicPool(pp);
-		temp.getSession().setSessionType("public_pool");
-		final User user = userService.getCurrentUser();
-		return sessionRepository.importSession(user, temp);
-	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
-		this.publisher = publisher;
-	}
-
-	@Override
-	public SessionFeature getSessionFeatures(String sessionkey) {
-		return sessionRepository.getSessionFromKeyword(sessionkey).getFeatures();
-	}
-
-	@Override
-	public SessionFeature changeSessionFeatures(String sessionkey, SessionFeature features) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException("User is not session creator.");
-		}
-		session.setFeatures(features);
-		this.publisher.publishEvent(new FeatureChangeEvent(this, session));
-		return sessionRepository.updateSession(session).getFeatures();
-	}
-
-	@Override
-	public boolean lockFeedbackInput(String sessionkey, Boolean lock) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException("User is not session creator.");
-		}
-		if (!lock) {
-			feedbackService.cleanFeedbackVotesInSession(sessionkey, 0);
-		}
-
-		session.setFeedbackLock(lock);
-		this.publisher.publishEvent(new LockFeedbackEvent(this, session));
-		return sessionRepository.updateSession(session).getFeedbackLock();
-	}
-
-	@Override
-	public boolean flipFlashcards(String sessionkey, Boolean flip) {
-		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		final User user = userService.getCurrentUser();
-		if (!session.isCreator(user)) {
-			throw new UnauthorizedException("User is not session creator.");
-		}
-		session.setFlipFlashcards(flip);
-		this.publisher.publishEvent(new FlipFlashcardsEvent(this, session));
-		return sessionRepository.updateSession(session).getFlipFlashcards();
-	}
-
-	private void handleLogo(Session session) {
-		if (session.getPpLogo() != null) {
-			if (session.getPpLogo().startsWith("http")) {
-				final String base64ImageString = imageUtils.encodeImageToString(session.getPpLogo());
-				if (base64ImageString == null) {
-					throw new BadRequestException("Could not encode image.");
-				}
-				session.setPpLogo(base64ImageString);
-			}
-
-			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
-			final int fileSize = (int) ((session.getPpLogo().length() - 814) / 1.37);
-			if (fileSize > uploadFileSizeByte) {
-				throw new PayloadTooLargeException("Could not save file. File is too large with " + fileSize + " Byte.");
-			}
-		}
-	}
+	void deleteInactiveVisitedSessionLists();
 }
diff --git a/src/main/java/de/thm/arsnova/services/SessionServiceImpl.java b/src/main/java/de/thm/arsnova/services/SessionServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d63abda419ea967f4e13b6ef91d7b41ae487c3e
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/SessionServiceImpl.java
@@ -0,0 +1,515 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.util.ImageUtils;
+import de.thm.arsnova.connector.client.ConnectorClient;
+import de.thm.arsnova.connector.model.Course;
+import de.thm.arsnova.services.score.ScoreCalculatorFactory;
+import de.thm.arsnova.services.score.ScoreCalculator;
+import de.thm.arsnova.entities.ScoreOptions;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.SessionFeature;
+import de.thm.arsnova.entities.SessionInfo;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.entities.transport.ImportExportSession;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
+import de.thm.arsnova.events.DeleteSessionEvent;
+import de.thm.arsnova.events.FeatureChangeEvent;
+import de.thm.arsnova.events.FlipFlashcardsEvent;
+import de.thm.arsnova.events.LockFeedbackEvent;
+import de.thm.arsnova.events.NewSessionEvent;
+import de.thm.arsnova.events.StatusSessionEvent;
+import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.exceptions.ForbiddenException;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.exceptions.PayloadTooLargeException;
+import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.SessionRepository;
+import de.thm.arsnova.persistance.VisitedSessionRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Performs all session related operations.
+ */
+@Service
+public class SessionServiceImpl implements SessionService, ApplicationEventPublisherAware {
+
+	@Autowired
+	private SessionRepository sessionRepository;
+
+	public static class SessionNameComparator implements Comparator<Session>, Serializable {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public int compare(final Session session1, final Session session2) {
+			return session1.getName().compareToIgnoreCase(session2.getName());
+		}
+	}
+
+	public static class SessionInfoNameComparator implements Comparator<SessionInfo>, Serializable {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public int compare(final SessionInfo session1, final SessionInfo session2) {
+			return session1.getName().compareToIgnoreCase(session2.getName());
+		}
+	}
+
+	public static class SessionShortNameComparator implements Comparator<Session>, Serializable {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public int compare(final Session session1, final Session session2) {
+			return session1.getShortName().compareToIgnoreCase(session2.getShortName());
+		}
+	}
+
+	public static class SessionInfoShortNameComparator implements Comparator<SessionInfo>, Serializable {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public int compare(final SessionInfo session1, final SessionInfo session2) {
+			return session1.getShortName().compareToIgnoreCase(session2.getShortName());
+		}
+	}
+
+	private static final long SESSION_INACTIVITY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
+
+	@Autowired
+	private VisitedSessionRepository visitedSessionRepository;
+
+	@Autowired
+	private UserService userService;
+
+	@Autowired
+	private FeedbackService feedbackService;
+
+	@Autowired
+	private ScoreCalculatorFactory scoreCalculatorFactory;
+
+	@Autowired(required = false)
+	private ConnectorClient connectorClient;
+
+	@Autowired
+	private ImageUtils imageUtils;
+
+	@Value("${session.guest-session.cleanup-days:0}")
+	private int guestSessionInactivityThresholdDays;
+
+	@Value("${pp.logofilesize_b}")
+	private int uploadFileSizeByte;
+
+	private ApplicationEventPublisher publisher;
+
+	private static final Logger logger = LoggerFactory.getLogger(SessionServiceImpl.class);
+
+	@Scheduled(fixedDelay = SESSION_INACTIVITY_CHECK_INTERVAL_MS)
+	public void deleteInactiveSessions() {
+		if (guestSessionInactivityThresholdDays > 0) {
+			logger.info("Delete inactive sessions.");
+			long unixTime = System.currentTimeMillis();
+			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
+			sessionRepository.deleteInactiveGuestSessions(lastActivityBefore);
+		}
+	}
+
+	@Scheduled(fixedDelay = SESSION_INACTIVITY_CHECK_INTERVAL_MS)
+	public void deleteInactiveVisitedSessionLists() {
+		if (guestSessionInactivityThresholdDays > 0) {
+			logger.info("Delete lists of visited session for inactive users.");
+			long unixTime = System.currentTimeMillis();
+			long lastActivityBefore = unixTime - guestSessionInactivityThresholdDays * 24 * 60 * 60 * 1000L;
+			visitedSessionRepository.deleteInactiveGuestVisitedSessionLists(lastActivityBefore);
+		}
+	}
+
+	@Override
+	public Session joinSession(final String keyword, final UUID socketId) {
+		/* Socket.IO solution */
+
+		Session session = null != keyword ? sessionRepository.getSessionFromKeyword(keyword) : null;
+
+		if (null == session) {
+			userService.removeUserFromSessionBySocketId(socketId);
+			return null;
+		}
+		final User user = userService.getUser2SocketId(socketId);
+
+		userService.addUserToSessionBySocketId(socketId, keyword);
+
+		if (session.getCreator().equals(user.getUsername())) {
+			sessionRepository.updateSessionOwnerActivity(session);
+		}
+		sessionRepository.registerAsOnlineUser(user, session);
+
+		if (connectorClient != null && session.isCourseSession()) {
+			final String courseid = session.getCourseId();
+			if (!connectorClient.getMembership(user.getUsername(), courseid).isMember()) {
+				throw new ForbiddenException("User is no course member.");
+			}
+		}
+
+		return session;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Session getSession(final String keyword) {
+		final User user = userService.getCurrentUser();
+		return Session.anonymizedCopy(this.getSessionInternal(keyword, user));
+	}
+
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public Session getSessionForAdmin(final String keyword) {
+		return sessionRepository.getSessionFromKeyword(keyword);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public Session getSessionInternal(final String keyword, final User user) {
+		final Session session = sessionRepository.getSessionFromKeyword(keyword);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		if (!session.isActive()) {
+			if (user.hasRole(UserSessionService.Role.STUDENT)) {
+				throw new ForbiddenException("User is not session creator.");
+			} else if (user.hasRole(UserSessionService.Role.SPEAKER) && !session.isCreator(user)) {
+				throw new ForbiddenException("User is not session creator.");
+			}
+		}
+		if (connectorClient != null && session.isCourseSession()) {
+			final String courseid = session.getCourseId();
+			if (!connectorClient.getMembership(user.getUsername(), courseid).isMember()) {
+				throw new ForbiddenException("User is no course member.");
+			}
+		}
+		return session;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public List<Session> getUserSessions(String username) {
+		return sessionRepository.getSessionsForUsername(username, 0, 0);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Session> getMySessions(final int offset, final int limit) {
+		return sessionRepository.getMySessions(userService.getCurrentUser(), offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<SessionInfo> getPublicPoolSessionsInfo() {
+		return sessionRepository.getPublicPoolSessionsInfo();
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<SessionInfo> getMyPublicPoolSessionsInfo() {
+		return sessionRepository.getMyPublicPoolSessionsInfo(userService.getCurrentUser());
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<SessionInfo> getMySessionsInfo(final int offset, final int limit) {
+		final User user = userService.getCurrentUser();
+		return sessionRepository.getMySessionsInfo(user, offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<Session> getMyVisitedSessions(final int offset, final int limit) {
+		return sessionRepository.getVisitedSessionsForUsername(userService.getCurrentUser().getUsername(), offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(1, 'motd', 'admin')")
+	public List<Session> getUserVisitedSessions(String username) {
+		return sessionRepository.getVisitedSessionsForUsername(username, 0, 0);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public List<SessionInfo> getMyVisitedSessionsInfo(final int offset, final int limit) {
+		return sessionRepository.getMyVisitedSessionsInfo(userService.getCurrentUser(), offset, limit);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public Session saveSession(final Session session) {
+		if (connectorClient != null && session.getCourseId() != null) {
+			if (!connectorClient.getMembership(
+					userService.getCurrentUser().getUsername(), session.getCourseId()).isMember()
+					) {
+				throw new ForbiddenException();
+			}
+		}
+		handleLogo(session);
+
+		// set some default values
+		ScoreOptions lpo = new ScoreOptions();
+		lpo.setType("questions");
+		session.setLearningProgressOptions(lpo);
+
+		SessionFeature sf = new SessionFeature();
+		sf.setLecture(true);
+		sf.setFeedback(true);
+		sf.setInterposed(true);
+		sf.setJitt(true);
+		sf.setLearningProgress(true);
+		sf.setPi(true);
+		session.setFeatures(sf);
+
+		final Session result = sessionRepository.saveSession(userService.getCurrentUser(), session);
+		this.publisher.publishEvent(new NewSessionEvent(this, result));
+		return result;
+	}
+
+	@Override
+	public boolean sessionKeyAvailable(final String keyword) {
+		return sessionRepository.sessionKeyAvailable(keyword);
+	}
+
+	@Override
+	public String generateKeyword() {
+		final int low = 10000000;
+		final int high = 100000000;
+		final String keyword = String
+				.valueOf((int) (Math.random() * (high - low) + low));
+
+		if (sessionKeyAvailable(keyword)) {
+			return keyword;
+		}
+		return generateKeyword();
+	}
+
+	@Override
+	public int countSessions(final List<Course> courses) {
+		final List<Session> sessions = sessionRepository.getCourseSessions(courses);
+		if (sessions == null) {
+			return 0;
+		}
+		return sessions.size();
+	}
+
+	@Override
+	public int activeUsers(final String sessionkey) {
+		return userService.getUsersInSession(sessionkey).size();
+	}
+
+	@Override
+	public Session setActive(final String sessionkey, final Boolean lock) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (!session.isCreator(user)) {
+			throw new ForbiddenException("User is not session creator.");
+		}
+		session.setActive(lock);
+		this.publisher.publishEvent(new StatusSessionEvent(this, session));
+		return sessionRepository.updateSession(session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#session, 'owner')")
+	public Session updateSession(final String sessionkey, final Session session) {
+		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
+
+		existingSession.setActive(session.isActive());
+		existingSession.setShortName(session.getShortName());
+		existingSession.setPpAuthorName(session.getPpAuthorName());
+		existingSession.setPpAuthorMail(session.getPpAuthorMail());
+		existingSession.setShortName(session.getShortName());
+		existingSession.setPpAuthorName(session.getPpAuthorName());
+		existingSession.setPpFaculty(session.getPpFaculty());
+		existingSession.setName(session.getName());
+		existingSession.setPpUniversity(session.getPpUniversity());
+		existingSession.setPpDescription(session.getPpDescription());
+		existingSession.setPpLevel(session.getPpLevel());
+		existingSession.setPpLicense(session.getPpLicense());
+		existingSession.setPpSubject(session.getPpSubject());
+		existingSession.setFeedbackLock(session.getFeedbackLock());
+
+		handleLogo(session);
+		existingSession.setPpLogo(session.getPpLogo());
+
+		return sessionRepository.updateSession(existingSession);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(1,'motd','admin')")
+	public Session changeSessionCreator(String sessionkey, String newCreator) {
+		final Session existingSession = sessionRepository.getSessionFromKeyword(sessionkey);
+		if (existingSession == null) {
+			throw new NullPointerException("Could not load session " + sessionkey + ".");
+		}
+		return sessionRepository.changeSessionCreator(existingSession, newCreator);
+	}
+
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public Session updateSessionInternal(final Session session, final User user) {
+		if (session.isCreator(user)) {
+			return sessionRepository.updateSession(session);
+		}
+		return null;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public void deleteSession(final String sessionkey) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+
+		sessionRepository.deleteSession(session);
+
+		this.publisher.publishEvent(new DeleteSessionEvent(this, session));
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public ScoreStatistics getLearningProgress(final String sessionkey, final String type, final String questionVariant) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		ScoreCalculator scoreCalculator = scoreCalculatorFactory.create(type, questionVariant);
+		return scoreCalculator.getCourseProgress(session);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public ScoreStatistics getMyLearningProgress(final String sessionkey, final String type, final String questionVariant) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		final User user = userService.getCurrentUser();
+		ScoreCalculator scoreCalculator = scoreCalculatorFactory.create(type, questionVariant);
+		return scoreCalculator.getMyProgress(session, user);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated()")
+	public SessionInfo importSession(ImportExportSession importSession) {
+		final User user = userService.getCurrentUser();
+		final SessionInfo info = sessionRepository.importSession(user, importSession);
+		if (info == null) {
+			throw new NullPointerException("Could not import session.");
+		}
+		return info;
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public ImportExportSession exportSession(String sessionkey, Boolean withAnswerStatistics, Boolean withFeedbackQuestions) {
+		return sessionRepository.exportSession(sessionkey, withAnswerStatistics, withFeedbackQuestions);
+	}
+
+	@Override
+	@PreAuthorize("isAuthenticated() and hasPermission(#sessionkey, 'session', 'owner')")
+	public SessionInfo copySessionToPublicPool(String sessionkey, de.thm.arsnova.entities.transport.ImportExportSession.PublicPool pp) {
+		ImportExportSession temp = sessionRepository.exportSession(sessionkey, false, false);
+		temp.getSession().setPublicPool(pp);
+		temp.getSession().setSessionType("public_pool");
+		final User user = userService.getCurrentUser();
+		return sessionRepository.importSession(user, temp);
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	@Override
+	public SessionFeature getSessionFeatures(String sessionkey) {
+		return sessionRepository.getSessionFromKeyword(sessionkey).getFeatures();
+	}
+
+	@Override
+	public SessionFeature changeSessionFeatures(String sessionkey, SessionFeature features) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException("User is not session creator.");
+		}
+		session.setFeatures(features);
+		this.publisher.publishEvent(new FeatureChangeEvent(this, session));
+		return sessionRepository.updateSession(session).getFeatures();
+	}
+
+	@Override
+	public boolean lockFeedbackInput(String sessionkey, Boolean lock) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException("User is not session creator.");
+		}
+		if (!lock) {
+			feedbackService.cleanFeedbackVotesInSession(sessionkey, 0);
+		}
+
+		session.setFeedbackLock(lock);
+		this.publisher.publishEvent(new LockFeedbackEvent(this, session));
+		return sessionRepository.updateSession(session).getFeedbackLock();
+	}
+
+	@Override
+	public boolean flipFlashcards(String sessionkey, Boolean flip) {
+		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
+		final User user = userService.getCurrentUser();
+		if (!session.isCreator(user)) {
+			throw new UnauthorizedException("User is not session creator.");
+		}
+		session.setFlipFlashcards(flip);
+		this.publisher.publishEvent(new FlipFlashcardsEvent(this, session));
+		return sessionRepository.updateSession(session).getFlipFlashcards();
+	}
+
+	private void handleLogo(Session session) {
+		if (session.getPpLogo() != null) {
+			if (session.getPpLogo().startsWith("http")) {
+				final String base64ImageString = imageUtils.encodeImageToString(session.getPpLogo());
+				if (base64ImageString == null) {
+					throw new BadRequestException("Could not encode image.");
+				}
+				session.setPpLogo(base64ImageString);
+			}
+
+			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
+			final int fileSize = (int) ((session.getPpLogo().length() - 814) / 1.37);
+			if (fileSize > uploadFileSizeByte) {
+				throw new PayloadTooLargeException("Could not save file. File is too large with " + fileSize + " Byte.");
+			}
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/StatisticsService.java b/src/main/java/de/thm/arsnova/services/StatisticsService.java
index ddf091b7250fcb066dd4661e399c479e69d33f97..f2742a5cdf74e912021c15d5e96923e092d35e23 100644
--- a/src/main/java/de/thm/arsnova/services/StatisticsService.java
+++ b/src/main/java/de/thm/arsnova/services/StatisticsService.java
@@ -18,33 +18,10 @@
 package de.thm.arsnova.services;
 
 import de.thm.arsnova.entities.Statistics;
-import de.thm.arsnova.persistance.StatisticsRepository;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Service;
 
 /**
- * Performs all statistics related operations. To reduce pressure on the database, data is cached for a fixed amount of
- * time.
+ * The functionality the statistics service should provide.
  */
-@Service
-public class StatisticsService implements IStatisticsService {
-	@Autowired
-	private StatisticsRepository statisticsRepository;
-
-	@Autowired
-	private IUserService userService;
-
-	private Statistics statistics = new Statistics();
-
-	@Scheduled(initialDelay = 0, fixedRate = 10000)
-	private void refreshStatistics() {
-		statistics = statisticsRepository.getStatistics();
-	}
-
-	@Override
-	public Statistics getStatistics() {
-		statistics.setActiveUsers(userService.loggedInUsers());
-		return statistics;
-	}
+public interface StatisticsService {
+	Statistics getStatistics();
 }
diff --git a/src/main/java/de/thm/arsnova/services/StatisticsServiceImpl.java b/src/main/java/de/thm/arsnova/services/StatisticsServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..8dda842380ff5141fbc4982958588028bcc40dec
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/StatisticsServiceImpl.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import de.thm.arsnova.entities.Statistics;
+import de.thm.arsnova.persistance.StatisticsRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+/**
+ * Performs all statistics related operations. To reduce pressure on the database, data is cached for a fixed amount of
+ * time.
+ */
+@Service
+public class StatisticsServiceImpl implements StatisticsService {
+	@Autowired
+	private StatisticsRepository statisticsRepository;
+
+	@Autowired
+	private UserService userService;
+
+	private Statistics statistics = new Statistics();
+
+	@Scheduled(initialDelay = 0, fixedRate = 10000)
+	private void refreshStatistics() {
+		statistics = statisticsRepository.getStatistics();
+	}
+
+	@Override
+	public Statistics getStatistics() {
+		statistics.setActiveUsers(userService.loggedInUsers());
+		return statistics;
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java
index fe8fdbc632103524944114306e8539c1c49eb44d..5052c4a1a7816d4a6fa3820c858fa4512942b307 100644
--- a/src/main/java/de/thm/arsnova/services/UserService.java
+++ b/src/main/java/de/thm/arsnova/services/UserService.java
@@ -17,529 +17,54 @@
  */
 package de.thm.arsnova.services;
 
-import com.codahale.metrics.annotation.Gauge;
 import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.persistance.UserRepository;
-import org.apache.commons.lang.RandomStringUtils;
-import org.apache.commons.lang.StringUtils;
-import org.pac4j.oauth.profile.facebook.FacebookProfile;
-import org.pac4j.oauth.profile.google2.Google2Profile;
-import org.pac4j.oauth.profile.twitter.TwitterProfile;
-import org.pac4j.springframework.security.authentication.Pac4jAuthenticationToken;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.mail.MailException;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.mail.javamail.MimeMessageHelper;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.cas.authentication.CasAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.keygen.BytesKeyGenerator;
-import org.springframework.security.crypto.keygen.KeyGenerators;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Isolation;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.util.UriUtils;
-import org.stagemonitor.core.metrics.MonitorGauges;
 
-import javax.annotation.PreDestroy;
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
-import java.io.UnsupportedEncodingException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
 
 /**
- * Performs all user related operations.
+ * The functionality the user service should provide.
  */
-@Service
-@MonitorGauges
-public class UserService implements IUserService {
+public interface UserService {
+	User getCurrentUser();
 
-	private static final int LOGIN_TRY_RESET_DELAY_MS = 30 * 1000;
+	boolean isBannedFromLogin(String addr);
 
-	private static final int LOGIN_BAN_RESET_DELAY_MS = 2 * 60 * 1000;
+	void increaseFailedLoginCount(String addr);
 
-	private static final int REPEATED_PASSWORD_RESET_DELAY_MS = 3 * 60 * 1000;
+	User getUser2SocketId(UUID socketId);
 
-	private static final int PASSWORD_RESET_KEY_DURABILITY_MS = 2 * 60 * 60 * 1000;
+	void putUser2SocketId(UUID socketId, User user);
 
-	private static final long ACTIVATION_KEY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
-	private static final long ACTIVATION_KEY_DURABILITY_MS = 6 * 60 * 60 * 1000L;
+	void removeUser2SocketId(UUID socketId);
 
-	private static final Logger logger = LoggerFactory.getLogger(UserService.class);
+	Set<Map.Entry<UUID, User>> socketId2User();
 
-	private static final ConcurrentHashMap<UUID, User> socketid2user = new ConcurrentHashMap<>();
+	boolean isUserInSession(User user, String keyword);
 
-	/* used for Socket.IO online check solution (new) */
-	private static final ConcurrentHashMap<User, String> user2session = new ConcurrentHashMap<>();
+	Set<User> getUsersInSession(String keyword);
 
-	@Autowired
-	private UserRepository userRepository;
+	String getSessionForUser(String username);
 
-	@Autowired
-	private JavaMailSender mailSender;
+	void addUserToSessionBySocketId(UUID socketId, String keyword);
 
-	@Value("${root-url}")
-	private String rootUrl;
+	void removeUserFromSessionBySocketId(UUID socketId);
 
-	@Value("${customization.path}")
-	private String customizationPath;
+	void removeUserFromMaps(User user);
 
-	@Value("${security.user-db.allowed-email-domains}")
-	private String allowedEmailDomains;
+	int loggedInUsers();
 
-	@Value("${security.user-db.activation-path}")
-	private String activationPath;
+	DbUser getDbUser(String username);
 
-	@Value("${security.user-db.reset-password-path}")
-	private String resetPasswordPath;
+	DbUser createDbUser(String username, String password);
 
-	@Value("${mail.sender.address}")
-	private String mailSenderAddress;
+	DbUser updateDbUser(DbUser dbUser);
 
-	@Value("${mail.sender.name}")
-	private String mailSenderName;
+	DbUser deleteDbUser(String username);
 
-	@Value("${security.user-db.registration-mail.subject}")
-	private String regMailSubject;
+	void initiatePasswordReset(String username);
 
-	@Value("${security.user-db.registration-mail.body}")
-	private String regMailBody;
-
-	@Value("${security.user-db.reset-password-mail.subject}")
-	private String resetPasswordMailSubject;
-
-	@Value("${security.user-db.reset-password-mail.body}")
-	private String resetPasswordMailBody;
-
-	@Value("${security.authentication.login-try-limit}")
-	private int loginTryLimit;
-
-	@Value("${security.admin-accounts}")
-	private String[] adminAccounts;
-
-	private Pattern mailPattern;
-	private BytesKeyGenerator keygen;
-	private BCryptPasswordEncoder encoder;
-	private ConcurrentHashMap<String, Byte> loginTries;
-	private Set<String> loginBans;
-
-	{
-		loginTries = new ConcurrentHashMap<>();
-		loginBans = Collections.synchronizedSet(new HashSet<String>());
-	}
-
-	@Scheduled(fixedDelay = LOGIN_TRY_RESET_DELAY_MS)
-	public void resetLoginTries() {
-		if (!loginTries.isEmpty()) {
-			logger.debug("Reset failed login counters.");
-			loginTries.clear();
-		}
-	}
-
-	@Scheduled(fixedDelay = LOGIN_BAN_RESET_DELAY_MS)
-	public void resetLoginBans() {
-		if (!loginBans.isEmpty()) {
-			logger.info("Reset temporary login bans.");
-			loginBans.clear();
-		}
-	}
-
-	@Scheduled(fixedDelay = ACTIVATION_KEY_CHECK_INTERVAL_MS)
-	public void deleteInactiveUsers() {
-		logger.info("Delete inactive users.");
-		long unixTime = System.currentTimeMillis();
-		long lastActivityBefore = unixTime - ACTIVATION_KEY_DURABILITY_MS;
-		userRepository.deleteInactiveUsers(lastActivityBefore);
-	}
-
-	@Override
-	public User getCurrentUser() {
-		final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-		if (authentication == null || authentication.getPrincipal() == null) {
-			return null;
-		}
-
-		User user = null;
-
-		if (authentication instanceof Pac4jAuthenticationToken) {
-			user = getOAuthUser(authentication);
-		} else if (authentication instanceof CasAuthenticationToken) {
-			final CasAuthenticationToken token = (CasAuthenticationToken) authentication;
-			user = new User(token.getAssertion().getPrincipal());
-		} else if (authentication instanceof AnonymousAuthenticationToken) {
-			final AnonymousAuthenticationToken token = (AnonymousAuthenticationToken) authentication;
-			user = new User(token);
-		} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
-			final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
-			user = new User(token);
-			if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_GUEST"))) {
-				user.setType(User.GUEST);
-			} else if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_DB_USER"))) {
-				user.setType(User.ARSNOVA);
-			}
-		}
-
-		if (user == null || "anonymous".equals(user.getUsername())) {
-			throw new UnauthorizedException();
-		}
-
-		user.setAdmin(Arrays.asList(adminAccounts).contains(user.getUsername()));
-
-		return user;
-	}
-
-	private User getOAuthUser(final Authentication authentication) {
-		User user = null;
-		final Pac4jAuthenticationToken token = (Pac4jAuthenticationToken) authentication;
-		if (token.getProfile() instanceof Google2Profile) {
-			final Google2Profile profile = (Google2Profile) token.getProfile();
-			user = new User(profile);
-		} else if (token.getProfile() instanceof TwitterProfile) {
-			final TwitterProfile profile = (TwitterProfile) token.getProfile();
-			user = new User(profile);
-		} else if (token.getProfile() instanceof FacebookProfile) {
-			final FacebookProfile profile = (FacebookProfile) token.getProfile();
-			user = new User(profile);
-		}
-		return user;
-	}
-
-	@Override
-	public boolean isBannedFromLogin(String addr) {
-		return loginBans.contains(addr);
-	}
-
-	@Override
-	public void increaseFailedLoginCount(String addr) {
-		Byte tries = loginTries.get(addr);
-		if (null == tries) {
-			tries = 0;
-		}
-		if (tries < loginTryLimit) {
-			loginTries.put(addr, ++tries);
-			if (loginTryLimit == tries) {
-				logger.info("Temporarily banned {} from login.", addr);
-				loginBans.add(addr);
-			}
-		}
-	}
-
-	@Override
-	public User getUser2SocketId(final UUID socketId) {
-		return socketid2user.get(socketId);
-	}
-
-	@Override
-	public void putUser2SocketId(final UUID socketId, final User user) {
-		socketid2user.put(socketId, user);
-	}
-
-	@Override
-	public Set<Map.Entry<UUID, User>> socketId2User() {
-		return socketid2user.entrySet();
-	}
-
-	@Override
-	public void removeUser2SocketId(final UUID socketId) {
-		socketid2user.remove(socketId);
-	}
-
-	@Override
-	public boolean isUserInSession(final User user, final String keyword) {
-		if (keyword == null) {
-			return false;
-		}
-		String session = user2session.get(user);
-
-		return session != null && keyword.equals(session);
-	}
-
-	@Override
-	public Set<User> getUsersInSession(final String keyword) {
-		final Set<User> result = new HashSet<>();
-		for (final Entry<User, String> e : user2session.entrySet()) {
-			if (e.getValue().equals(keyword)) {
-				result.add(e.getKey());
-			}
-		}
-
-		return result;
-	}
-
-	@Override
-	@Transactional(isolation = Isolation.READ_COMMITTED)
-	public void addUserToSessionBySocketId(final UUID socketId, final String keyword) {
-		final User user = socketid2user.get(socketId);
-		user2session.put(user, keyword);
-	}
-
-	@Override
-	@Transactional(isolation = Isolation.READ_COMMITTED)
-	public void removeUserFromSessionBySocketId(final UUID socketId) {
-		final User user = socketid2user.get(socketId);
-		if (null == user) {
-			logger.warn("No user exists for socket {}.", socketId);
-
-			return;
-		}
-		user2session.remove(user);
-	}
-
-	@Override
-	public String getSessionForUser(final String username) {
-		for (final Entry<User, String> entry  : user2session.entrySet()) {
-			if (entry.getKey().getUsername().equals(username)) {
-				return entry.getValue();
-			}
-		}
-
-		return null;
-	}
-
-	@PreDestroy
-	public void destroy() {
-		logger.error("Destroy UserService");
-	}
-
-	@Override
-	public void removeUserFromMaps(final User user) {
-		if (user != null) {
-			user2session.remove(user);
-		}
-	}
-
-	@Override
-	@Gauge
-	public int loggedInUsers() {
-		return user2session.size();
-	}
-
-	@Override
-	public DbUser getDbUser(String username) {
-		return userRepository.findUserByUsername(username.toLowerCase());
-	}
-
-	@Override
-	public DbUser createDbUser(String username, String password) {
-		String lcUsername = username.toLowerCase();
-
-		if (null == keygen) {
-			keygen = KeyGenerators.secureRandom(32);
-		}
-
-		if (null == mailPattern) {
-			parseMailAddressPattern();
-		}
-
-		if (null == mailPattern || !mailPattern.matcher(lcUsername).matches()) {
-			logger.info("User registration failed. {} does not match pattern.", lcUsername);
-
-			return null;
-		}
-
-		if (null != userRepository.findUserByUsername(lcUsername)) {
-			logger.info("User registration failed. {} already exists.", lcUsername);
-
-			return null;
-		}
-
-		DbUser dbUser = new DbUser();
-		dbUser.setUsername(lcUsername);
-		dbUser.setPassword(encodePassword(password));
-		dbUser.setActivationKey(RandomStringUtils.randomAlphanumeric(32));
-		dbUser.setCreation(System.currentTimeMillis());
-
-		DbUser result = userRepository.createOrUpdateUser(dbUser);
-		if (null != result) {
-			sendActivationEmail(result);
-		} else {
-			logger.error("User registration failed. {} could not be created.", lcUsername);
-		}
-
-		return result;
-	}
-
-	private String encodePassword(String password) {
-		if (null == encoder) {
-			encoder = new BCryptPasswordEncoder(12);
-		}
-
-		return encoder.encode(password);
-	}
-
-	private void sendActivationEmail(DbUser dbUser) {
-		String activationUrl;
-		try {
-			activationUrl = MessageFormat.format(
-				"{0}{1}/{2}?action=activate&username={3}&key={4}",
-				rootUrl,
-				customizationPath,
-				activationPath,
-				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
-				dbUser.getActivationKey()
-			);
-		} catch (UnsupportedEncodingException e) {
-			logger.error("Sending of activation mail failed.", e);
-
-			return;
-		}
-
-		sendEmail(dbUser, regMailSubject, MessageFormat.format(regMailBody, activationUrl));
-	}
-
-	private void parseMailAddressPattern() {
-		/* TODO: Add Unicode support */
-
-		List<String> domainList = Arrays.asList(allowedEmailDomains.split(","));
-
-		if (!domainList.isEmpty()) {
-			List<String> patterns = new ArrayList<>();
-			if (domainList.contains("*")) {
-				patterns.add("([a-z0-9-]+\\.)+[a-z0-9-]+");
-			} else {
-				Pattern patternPattern = Pattern.compile("[a-z0-9.*-]+", Pattern.CASE_INSENSITIVE);
-				for (String patternStr : domainList) {
-					if (patternPattern.matcher(patternStr).matches()) {
-						patterns.add(patternStr.replaceAll("[.]", "[.]").replaceAll("[*]", "[a-z0-9-]+?"));
-					}
-				}
-			}
-
-			mailPattern = Pattern.compile("[a-z0-9._-]+?@(" + StringUtils.join(patterns, "|") + ")", Pattern.CASE_INSENSITIVE);
-			logger.info("Allowed e-mail addresses (pattern) for registration: '{}'.", mailPattern.pattern());
-		}
-	}
-
-	@Override
-	public DbUser updateDbUser(DbUser dbUser) {
-		if (null != dbUser.getId()) {
-			return userRepository.createOrUpdateUser(dbUser);
-		}
-
-		return null;
-	}
-
-	@Override
-	public DbUser deleteDbUser(String username) {
-		User user = getCurrentUser();
-		if (!user.getUsername().equals(username.toLowerCase())
-				&& !SecurityContextHolder.getContext().getAuthentication().getAuthorities()
-						.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
-			throw new UnauthorizedException();
-		}
-
-		DbUser dbUser = getDbUser(username);
-		if (null == dbUser) {
-			throw new NotFoundException();
-		}
-
-		userRepository.deleteUser(dbUser);
-
-		return dbUser;
-	}
-
-	@Override
-	public void initiatePasswordReset(String username) {
-		DbUser dbUser = getDbUser(username);
-		if (null == dbUser) {
-			logger.info("Password reset failed. User {} does not exist.", username);
-
-			throw new NotFoundException();
-		}
-		if (System.currentTimeMillis() < dbUser.getPasswordResetTime() + REPEATED_PASSWORD_RESET_DELAY_MS) {
-			logger.info("Password reset failed. The reset delay for User {} is still active.", username);
-
-			throw new BadRequestException();
-		}
-
-		dbUser.setPasswordResetKey(RandomStringUtils.randomAlphanumeric(32));
-		dbUser.setPasswordResetTime(System.currentTimeMillis());
-
-		if (null == userRepository.createOrUpdateUser(dbUser)) {
-			logger.error("Password reset failed. {} could not be updated.", username);
-		}
-
-		String resetPasswordUrl;
-		try {
-			resetPasswordUrl = MessageFormat.format(
-				"{0}{1}/{2}?action=resetpassword&username={3}&key={4}",
-				rootUrl,
-				customizationPath,
-				resetPasswordPath,
-				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
-				dbUser.getPasswordResetKey()
-			);
-		} catch (UnsupportedEncodingException e) {
-			logger.error("Sending of password reset mail failed.", e);
-
-			return;
-		}
-
-		sendEmail(dbUser, resetPasswordMailSubject, MessageFormat.format(resetPasswordMailBody, resetPasswordUrl));
-	}
-
-	@Override
-	public boolean resetPassword(DbUser dbUser, String key, String password) {
-		if (null == key || "".equals(key) || !key.equals(dbUser.getPasswordResetKey())) {
-			logger.info("Password reset failed. Invalid key provided for User {}.", dbUser.getUsername());
-
-			return false;
-		}
-		if (System.currentTimeMillis() > dbUser.getPasswordResetTime() + PASSWORD_RESET_KEY_DURABILITY_MS) {
-			logger.info("Password reset failed. Key provided for User {} is no longer valid.", dbUser.getUsername());
-
-			dbUser.setPasswordResetKey(null);
-			dbUser.setPasswordResetTime(0);
-			updateDbUser(dbUser);
-
-			return false;
-		}
-
-		dbUser.setPassword(encodePassword(password));
-		dbUser.setPasswordResetKey(null);
-		if (null == updateDbUser(dbUser)) {
-			logger.error("Password reset failed. {} could not be updated.", dbUser.getUsername());
-		}
-
-		return true;
-	}
-
-	private void sendEmail(DbUser dbUser, String subject, String body) {
-		MimeMessage msg = mailSender.createMimeMessage();
-		MimeMessageHelper helper = new MimeMessageHelper(msg, "UTF-8");
-		try {
-			helper.setFrom(mailSenderName + "<" + mailSenderAddress + ">");
-			helper.setTo(dbUser.getUsername());
-			helper.setSubject(subject);
-			helper.setText(body);
-
-			logger.info("Sending mail \"{}\" from \"{}\" to \"{}\"", subject, msg.getFrom(), dbUser.getUsername());
-			mailSender.send(msg);
-		} catch (MailException | MessagingException e) {
-			logger.warn("Mail \"{}\" could not be sent.", subject, e);
-		}
-	}
+	boolean resetPassword(DbUser dbUser, String key, String password);
 }
diff --git a/src/main/java/de/thm/arsnova/services/UserServiceImpl.java b/src/main/java/de/thm/arsnova/services/UserServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..17ff05b0bd62df136b8445078824328418f3a7ac
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/services/UserServiceImpl.java
@@ -0,0 +1,545 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2017 The ARSnova Team
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.services;
+
+import com.codahale.metrics.annotation.Gauge;
+import de.thm.arsnova.entities.DbUser;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.exceptions.BadRequestException;
+import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.exceptions.UnauthorizedException;
+import de.thm.arsnova.persistance.UserRepository;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.StringUtils;
+import org.pac4j.oauth.profile.facebook.FacebookProfile;
+import org.pac4j.oauth.profile.google2.Google2Profile;
+import org.pac4j.oauth.profile.twitter.TwitterProfile;
+import org.pac4j.springframework.security.authentication.Pac4jAuthenticationToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.MailException;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.cas.authentication.CasAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.keygen.BytesKeyGenerator;
+import org.springframework.security.crypto.keygen.KeyGenerators;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.util.UriUtils;
+import org.stagemonitor.core.metrics.MonitorGauges;
+
+import javax.annotation.PreDestroy;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.io.UnsupportedEncodingException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+/**
+ * Performs all user related operations.
+ */
+@Service
+@MonitorGauges
+public class UserServiceImpl implements UserService {
+
+	private static final int LOGIN_TRY_RESET_DELAY_MS = 30 * 1000;
+
+	private static final int LOGIN_BAN_RESET_DELAY_MS = 2 * 60 * 1000;
+
+	private static final int REPEATED_PASSWORD_RESET_DELAY_MS = 3 * 60 * 1000;
+
+	private static final int PASSWORD_RESET_KEY_DURABILITY_MS = 2 * 60 * 60 * 1000;
+
+	private static final long ACTIVATION_KEY_CHECK_INTERVAL_MS = 30 * 60 * 1000L;
+	private static final long ACTIVATION_KEY_DURABILITY_MS = 6 * 60 * 60 * 1000L;
+
+	private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
+
+	private static final ConcurrentHashMap<UUID, User> socketid2user = new ConcurrentHashMap<>();
+
+	/* used for Socket.IO online check solution (new) */
+	private static final ConcurrentHashMap<User, String> user2session = new ConcurrentHashMap<>();
+
+	@Autowired
+	private UserRepository userRepository;
+
+	@Autowired
+	private JavaMailSender mailSender;
+
+	@Value("${root-url}")
+	private String rootUrl;
+
+	@Value("${customization.path}")
+	private String customizationPath;
+
+	@Value("${security.user-db.allowed-email-domains}")
+	private String allowedEmailDomains;
+
+	@Value("${security.user-db.activation-path}")
+	private String activationPath;
+
+	@Value("${security.user-db.reset-password-path}")
+	private String resetPasswordPath;
+
+	@Value("${mail.sender.address}")
+	private String mailSenderAddress;
+
+	@Value("${mail.sender.name}")
+	private String mailSenderName;
+
+	@Value("${security.user-db.registration-mail.subject}")
+	private String regMailSubject;
+
+	@Value("${security.user-db.registration-mail.body}")
+	private String regMailBody;
+
+	@Value("${security.user-db.reset-password-mail.subject}")
+	private String resetPasswordMailSubject;
+
+	@Value("${security.user-db.reset-password-mail.body}")
+	private String resetPasswordMailBody;
+
+	@Value("${security.authentication.login-try-limit}")
+	private int loginTryLimit;
+
+	@Value("${security.admin-accounts}")
+	private String[] adminAccounts;
+
+	private Pattern mailPattern;
+	private BytesKeyGenerator keygen;
+	private BCryptPasswordEncoder encoder;
+	private ConcurrentHashMap<String, Byte> loginTries;
+	private Set<String> loginBans;
+
+	{
+		loginTries = new ConcurrentHashMap<>();
+		loginBans = Collections.synchronizedSet(new HashSet<String>());
+	}
+
+	@Scheduled(fixedDelay = LOGIN_TRY_RESET_DELAY_MS)
+	public void resetLoginTries() {
+		if (!loginTries.isEmpty()) {
+			logger.debug("Reset failed login counters.");
+			loginTries.clear();
+		}
+	}
+
+	@Scheduled(fixedDelay = LOGIN_BAN_RESET_DELAY_MS)
+	public void resetLoginBans() {
+		if (!loginBans.isEmpty()) {
+			logger.info("Reset temporary login bans.");
+			loginBans.clear();
+		}
+	}
+
+	@Scheduled(fixedDelay = ACTIVATION_KEY_CHECK_INTERVAL_MS)
+	public void deleteInactiveUsers() {
+		logger.info("Delete inactive users.");
+		long unixTime = System.currentTimeMillis();
+		long lastActivityBefore = unixTime - ACTIVATION_KEY_DURABILITY_MS;
+		userRepository.deleteInactiveUsers(lastActivityBefore);
+	}
+
+	@Override
+	public User getCurrentUser() {
+		final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+		if (authentication == null || authentication.getPrincipal() == null) {
+			return null;
+		}
+
+		User user = null;
+
+		if (authentication instanceof Pac4jAuthenticationToken) {
+			user = getOAuthUser(authentication);
+		} else if (authentication instanceof CasAuthenticationToken) {
+			final CasAuthenticationToken token = (CasAuthenticationToken) authentication;
+			user = new User(token.getAssertion().getPrincipal());
+		} else if (authentication instanceof AnonymousAuthenticationToken) {
+			final AnonymousAuthenticationToken token = (AnonymousAuthenticationToken) authentication;
+			user = new User(token);
+		} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
+			final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
+			user = new User(token);
+			if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_GUEST"))) {
+				user.setType(User.GUEST);
+			} else if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_DB_USER"))) {
+				user.setType(User.ARSNOVA);
+			}
+		}
+
+		if (user == null || "anonymous".equals(user.getUsername())) {
+			throw new UnauthorizedException();
+		}
+
+		user.setAdmin(Arrays.asList(adminAccounts).contains(user.getUsername()));
+
+		return user;
+	}
+
+	private User getOAuthUser(final Authentication authentication) {
+		User user = null;
+		final Pac4jAuthenticationToken token = (Pac4jAuthenticationToken) authentication;
+		if (token.getProfile() instanceof Google2Profile) {
+			final Google2Profile profile = (Google2Profile) token.getProfile();
+			user = new User(profile);
+		} else if (token.getProfile() instanceof TwitterProfile) {
+			final TwitterProfile profile = (TwitterProfile) token.getProfile();
+			user = new User(profile);
+		} else if (token.getProfile() instanceof FacebookProfile) {
+			final FacebookProfile profile = (FacebookProfile) token.getProfile();
+			user = new User(profile);
+		}
+		return user;
+	}
+
+	@Override
+	public boolean isBannedFromLogin(String addr) {
+		return loginBans.contains(addr);
+	}
+
+	@Override
+	public void increaseFailedLoginCount(String addr) {
+		Byte tries = loginTries.get(addr);
+		if (null == tries) {
+			tries = 0;
+		}
+		if (tries < loginTryLimit) {
+			loginTries.put(addr, ++tries);
+			if (loginTryLimit == tries) {
+				logger.info("Temporarily banned {} from login.", addr);
+				loginBans.add(addr);
+			}
+		}
+	}
+
+	@Override
+	public User getUser2SocketId(final UUID socketId) {
+		return socketid2user.get(socketId);
+	}
+
+	@Override
+	public void putUser2SocketId(final UUID socketId, final User user) {
+		socketid2user.put(socketId, user);
+	}
+
+	@Override
+	public Set<Map.Entry<UUID, User>> socketId2User() {
+		return socketid2user.entrySet();
+	}
+
+	@Override
+	public void removeUser2SocketId(final UUID socketId) {
+		socketid2user.remove(socketId);
+	}
+
+	@Override
+	public boolean isUserInSession(final User user, final String keyword) {
+		if (keyword == null) {
+			return false;
+		}
+		String session = user2session.get(user);
+
+		return session != null && keyword.equals(session);
+	}
+
+	@Override
+	public Set<User> getUsersInSession(final String keyword) {
+		final Set<User> result = new HashSet<>();
+		for (final Entry<User, String> e : user2session.entrySet()) {
+			if (e.getValue().equals(keyword)) {
+				result.add(e.getKey());
+			}
+		}
+
+		return result;
+	}
+
+	@Override
+	@Transactional(isolation = Isolation.READ_COMMITTED)
+	public void addUserToSessionBySocketId(final UUID socketId, final String keyword) {
+		final User user = socketid2user.get(socketId);
+		user2session.put(user, keyword);
+	}
+
+	@Override
+	@Transactional(isolation = Isolation.READ_COMMITTED)
+	public void removeUserFromSessionBySocketId(final UUID socketId) {
+		final User user = socketid2user.get(socketId);
+		if (null == user) {
+			logger.warn("No user exists for socket {}.", socketId);
+
+			return;
+		}
+		user2session.remove(user);
+	}
+
+	@Override
+	public String getSessionForUser(final String username) {
+		for (final Entry<User, String> entry  : user2session.entrySet()) {
+			if (entry.getKey().getUsername().equals(username)) {
+				return entry.getValue();
+			}
+		}
+
+		return null;
+	}
+
+	@PreDestroy
+	public void destroy() {
+		logger.error("Destroy UserServiceImpl");
+	}
+
+	@Override
+	public void removeUserFromMaps(final User user) {
+		if (user != null) {
+			user2session.remove(user);
+		}
+	}
+
+	@Override
+	@Gauge
+	public int loggedInUsers() {
+		return user2session.size();
+	}
+
+	@Override
+	public DbUser getDbUser(String username) {
+		return userRepository.findUserByUsername(username.toLowerCase());
+	}
+
+	@Override
+	public DbUser createDbUser(String username, String password) {
+		String lcUsername = username.toLowerCase();
+
+		if (null == keygen) {
+			keygen = KeyGenerators.secureRandom(32);
+		}
+
+		if (null == mailPattern) {
+			parseMailAddressPattern();
+		}
+
+		if (null == mailPattern || !mailPattern.matcher(lcUsername).matches()) {
+			logger.info("User registration failed. {} does not match pattern.", lcUsername);
+
+			return null;
+		}
+
+		if (null != userRepository.findUserByUsername(lcUsername)) {
+			logger.info("User registration failed. {} already exists.", lcUsername);
+
+			return null;
+		}
+
+		DbUser dbUser = new DbUser();
+		dbUser.setUsername(lcUsername);
+		dbUser.setPassword(encodePassword(password));
+		dbUser.setActivationKey(RandomStringUtils.randomAlphanumeric(32));
+		dbUser.setCreation(System.currentTimeMillis());
+
+		DbUser result = userRepository.createOrUpdateUser(dbUser);
+		if (null != result) {
+			sendActivationEmail(result);
+		} else {
+			logger.error("User registration failed. {} could not be created.", lcUsername);
+		}
+
+		return result;
+	}
+
+	private String encodePassword(String password) {
+		if (null == encoder) {
+			encoder = new BCryptPasswordEncoder(12);
+		}
+
+		return encoder.encode(password);
+	}
+
+	private void sendActivationEmail(DbUser dbUser) {
+		String activationUrl;
+		try {
+			activationUrl = MessageFormat.format(
+				"{0}{1}/{2}?action=activate&username={3}&key={4}",
+				rootUrl,
+				customizationPath,
+				activationPath,
+				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
+				dbUser.getActivationKey()
+			);
+		} catch (UnsupportedEncodingException e) {
+			logger.error("Sending of activation mail failed.", e);
+
+			return;
+		}
+
+		sendEmail(dbUser, regMailSubject, MessageFormat.format(regMailBody, activationUrl));
+	}
+
+	private void parseMailAddressPattern() {
+		/* TODO: Add Unicode support */
+
+		List<String> domainList = Arrays.asList(allowedEmailDomains.split(","));
+
+		if (!domainList.isEmpty()) {
+			List<String> patterns = new ArrayList<>();
+			if (domainList.contains("*")) {
+				patterns.add("([a-z0-9-]+\\.)+[a-z0-9-]+");
+			} else {
+				Pattern patternPattern = Pattern.compile("[a-z0-9.*-]+", Pattern.CASE_INSENSITIVE);
+				for (String patternStr : domainList) {
+					if (patternPattern.matcher(patternStr).matches()) {
+						patterns.add(patternStr.replaceAll("[.]", "[.]").replaceAll("[*]", "[a-z0-9-]+?"));
+					}
+				}
+			}
+
+			mailPattern = Pattern.compile("[a-z0-9._-]+?@(" + StringUtils.join(patterns, "|") + ")", Pattern.CASE_INSENSITIVE);
+			logger.info("Allowed e-mail addresses (pattern) for registration: '{}'.", mailPattern.pattern());
+		}
+	}
+
+	@Override
+	public DbUser updateDbUser(DbUser dbUser) {
+		if (null != dbUser.getId()) {
+			return userRepository.createOrUpdateUser(dbUser);
+		}
+
+		return null;
+	}
+
+	@Override
+	public DbUser deleteDbUser(String username) {
+		User user = getCurrentUser();
+		if (!user.getUsername().equals(username.toLowerCase())
+				&& !SecurityContextHolder.getContext().getAuthentication().getAuthorities()
+						.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
+			throw new UnauthorizedException();
+		}
+
+		DbUser dbUser = getDbUser(username);
+		if (null == dbUser) {
+			throw new NotFoundException();
+		}
+
+		userRepository.deleteUser(dbUser);
+
+		return dbUser;
+	}
+
+	@Override
+	public void initiatePasswordReset(String username) {
+		DbUser dbUser = getDbUser(username);
+		if (null == dbUser) {
+			logger.info("Password reset failed. User {} does not exist.", username);
+
+			throw new NotFoundException();
+		}
+		if (System.currentTimeMillis() < dbUser.getPasswordResetTime() + REPEATED_PASSWORD_RESET_DELAY_MS) {
+			logger.info("Password reset failed. The reset delay for User {} is still active.", username);
+
+			throw new BadRequestException();
+		}
+
+		dbUser.setPasswordResetKey(RandomStringUtils.randomAlphanumeric(32));
+		dbUser.setPasswordResetTime(System.currentTimeMillis());
+
+		if (null == userRepository.createOrUpdateUser(dbUser)) {
+			logger.error("Password reset failed. {} could not be updated.", username);
+		}
+
+		String resetPasswordUrl;
+		try {
+			resetPasswordUrl = MessageFormat.format(
+				"{0}{1}/{2}?action=resetpassword&username={3}&key={4}",
+				rootUrl,
+				customizationPath,
+				resetPasswordPath,
+				UriUtils.encodeQueryParam(dbUser.getUsername(), "UTF-8"),
+				dbUser.getPasswordResetKey()
+			);
+		} catch (UnsupportedEncodingException e) {
+			logger.error("Sending of password reset mail failed.", e);
+
+			return;
+		}
+
+		sendEmail(dbUser, resetPasswordMailSubject, MessageFormat.format(resetPasswordMailBody, resetPasswordUrl));
+	}
+
+	@Override
+	public boolean resetPassword(DbUser dbUser, String key, String password) {
+		if (null == key || "".equals(key) || !key.equals(dbUser.getPasswordResetKey())) {
+			logger.info("Password reset failed. Invalid key provided for User {}.", dbUser.getUsername());
+
+			return false;
+		}
+		if (System.currentTimeMillis() > dbUser.getPasswordResetTime() + PASSWORD_RESET_KEY_DURABILITY_MS) {
+			logger.info("Password reset failed. Key provided for User {} is no longer valid.", dbUser.getUsername());
+
+			dbUser.setPasswordResetKey(null);
+			dbUser.setPasswordResetTime(0);
+			updateDbUser(dbUser);
+
+			return false;
+		}
+
+		dbUser.setPassword(encodePassword(password));
+		dbUser.setPasswordResetKey(null);
+		if (null == updateDbUser(dbUser)) {
+			logger.error("Password reset failed. {} could not be updated.", dbUser.getUsername());
+		}
+
+		return true;
+	}
+
+	private void sendEmail(DbUser dbUser, String subject, String body) {
+		MimeMessage msg = mailSender.createMimeMessage();
+		MimeMessageHelper helper = new MimeMessageHelper(msg, "UTF-8");
+		try {
+			helper.setFrom(mailSenderName + "<" + mailSenderAddress + ">");
+			helper.setTo(dbUser.getUsername());
+			helper.setSubject(subject);
+			helper.setText(body);
+
+			logger.info("Sending mail \"{}\" from \"{}\" to \"{}\"", subject, msg.getFrom(), dbUser.getUsername());
+			mailSender.send(msg);
+		} catch (MailException | MessagingException e) {
+			logger.warn("Mail \"{}\" could not be sent.", subject, e);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java b/src/main/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculator.java
similarity index 87%
rename from src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java
rename to src/main/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculator.java
index 37b6c63974f639cca4e76bdf8563b25f5e9dada7..2e28d96b4507fffdb6726b1f1516a205a8be3fc9 100644
--- a/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculator.java
@@ -15,28 +15,28 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
  * Calculates learning progress based on overall correctness of an answer. A question is answered correctly if and
  * only if the maximum question value possible has been achieved.
  */
-public class QuestionBasedLearningProgress extends VariantLearningProgress {
+public class QuestionBasedScoreCalculator extends VariantScoreCalculator {
 
-	public QuestionBasedLearningProgress(SessionStatisticsRepository sessionStatisticsRepository) {
+	public QuestionBasedScoreCalculator(SessionStatisticsRepository sessionStatisticsRepository) {
 		super(sessionStatisticsRepository);
 	}
 
 	@Override
-	protected LearningProgressValues createCourseProgress() {
+	protected ScoreStatistics createCourseProgress() {
 		final int courseProgress = calculateCourseProgress();
 		final int numerator = courseScore.getQuestionCount() * courseProgress / 100;
 		final int denominator = courseScore.getQuestionCount();
-		LearningProgressValues lpv = new LearningProgressValues();
+		ScoreStatistics lpv = new ScoreStatistics();
 		lpv.setCourseProgress(courseProgress);
 		lpv.setNumQuestions(courseScore.getQuestionCount());
 		lpv.setNumUsers(courseScore.getTotalUserCount());
@@ -71,10 +71,10 @@ public class QuestionBasedLearningProgress extends VariantLearningProgress {
 	}
 
 	@Override
-	protected LearningProgressValues createMyProgress(User user) {
+	protected ScoreStatistics createMyProgress(User user) {
 		final int numerator = numQuestionsCorrectForUser(user);
 		final int denominator = courseScore.getQuestionCount();
-		LearningProgressValues lpv = new LearningProgressValues();
+		ScoreStatistics lpv = new ScoreStatistics();
 		lpv.setCourseProgress(calculateCourseProgress());
 		lpv.setMyProgress(myPercentage(numerator, denominator));
 		lpv.setNumQuestions(courseScore.getQuestionCount());
diff --git a/src/main/java/de/thm/arsnova/domain/QuestionScore.java b/src/main/java/de/thm/arsnova/services/score/QuestionScore.java
similarity index 96%
rename from src/main/java/de/thm/arsnova/domain/QuestionScore.java
rename to src/main/java/de/thm/arsnova/services/score/QuestionScore.java
index 32b5b6f52d6d89331a5eee267292a8543c9bfdff..88ecf31bdcebf055ae412bd02d37d15966b187ed 100644
--- a/src/main/java/de/thm/arsnova/domain/QuestionScore.java
+++ b/src/main/java/de/thm/arsnova/services/score/QuestionScore.java
@@ -15,7 +15,7 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.User;
 
@@ -25,7 +25,7 @@ import java.util.List;
 import java.util.Set;
 
 /**
- * Calculates learning progress score for a specific question.
+ * Calculates score for a specific question.
  */
 public class QuestionScore implements Iterable<UserScore> {
 
diff --git a/src/main/java/de/thm/arsnova/domain/CourseScore.java b/src/main/java/de/thm/arsnova/services/score/Score.java
similarity index 87%
rename from src/main/java/de/thm/arsnova/domain/CourseScore.java
rename to src/main/java/de/thm/arsnova/services/score/Score.java
index 40a3cf091767162acab5f1ccce971564b8fe3291..f7fdcb69d444090abfbd08f2232362a30cdc23da 100644
--- a/src/main/java/de/thm/arsnova/domain/CourseScore.java
+++ b/src/main/java/de/thm/arsnova/services/score/Score.java
@@ -15,7 +15,7 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.User;
 
@@ -27,17 +27,17 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 /**
- * Calculates the learning progress for users and their courses.
+ * Calculates the score for users and their sessions.
  */
-public class CourseScore implements Iterable<QuestionScore> {
+public class Score implements Iterable<QuestionScore> {
 
 	private final Map<String, QuestionScore> scores;
 
-	public CourseScore() {
+	public Score() {
 		this(new HashMap<String, QuestionScore>());
 	}
 
-	public CourseScore(Map<String, QuestionScore> theScores) {
+	public Score(Map<String, QuestionScore> theScores) {
 		this.scores = theScores;
 	}
 
@@ -51,7 +51,7 @@ public class CourseScore implements Iterable<QuestionScore> {
 	public void addAnswer(String questionId, int piRound, String username, int userscore) {
 		if (!scores.containsKey(questionId)) {
 			// Precondition failed, ignore this element.
-			// Most likely this is a question that has no learning progress value.
+			// Most likely this is a question that has no score value.
 			return;
 		}
 		if (username == null || username.isEmpty()) {
@@ -62,7 +62,7 @@ public class CourseScore implements Iterable<QuestionScore> {
 		questionScore.add(piRound, username, userscore);
 	}
 
-	public CourseScore filterVariant(String questionVariant) {
+	public Score filterVariant(String questionVariant) {
 		Map<String, QuestionScore> newScores = new HashMap<>();
 		for (Entry<String, QuestionScore> entry : this.scores.entrySet()) {
 			String questionId = entry.getKey();
@@ -71,7 +71,7 @@ public class CourseScore implements Iterable<QuestionScore> {
 				newScores.put(questionId, questionScore);
 			}
 		}
-		return new CourseScore(newScores);
+		return new Score(newScores);
 	}
 
 	public int getMaximumScore() {
diff --git a/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java b/src/main/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculator.java
similarity index 80%
rename from src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java
rename to src/main/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculator.java
index 21f2fbd3d966ccd6b92749ddde815fb184a15fae..309c9bef05500783130a7796d139a6e9e64bc270 100644
--- a/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculator.java
@@ -15,24 +15,24 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
- * Calculates learning progress based on a question's value.
+ * Calculates score based on a question's value.
  */
-public class PointBasedLearningProgress extends VariantLearningProgress {
+public class ScoreBasedScoreCalculator extends VariantScoreCalculator {
 
-	public PointBasedLearningProgress(SessionStatisticsRepository sessionStatisticsRepository) {
+	public ScoreBasedScoreCalculator(SessionStatisticsRepository sessionStatisticsRepository) {
 		super(sessionStatisticsRepository);
 	}
 
 	@Override
-	protected LearningProgressValues createCourseProgress() {
-		LearningProgressValues lpv = new LearningProgressValues();
+	protected ScoreStatistics createCourseProgress() {
+		ScoreStatistics lpv = new ScoreStatistics();
 		lpv.setCourseProgress(coursePercentage());
 		lpv.setNumQuestions(courseScore.getQuestionCount());
 		lpv.setNumUsers(courseScore.getTotalUserCount());
@@ -54,8 +54,8 @@ public class PointBasedLearningProgress extends VariantLearningProgress {
 	}
 
 	@Override
-	protected LearningProgressValues createMyProgress(User user) {
-		LearningProgressValues lpv = new LearningProgressValues();
+	protected ScoreStatistics createMyProgress(User user) {
+		ScoreStatistics lpv = new ScoreStatistics();
 		lpv.setCourseProgress(coursePercentage());
 		lpv.setNumQuestions(courseScore.getQuestionCount());
 		lpv.setNumUsers(courseScore.getTotalUserCount());
diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgress.java b/src/main/java/de/thm/arsnova/services/score/ScoreCalculator.java
similarity index 70%
rename from src/main/java/de/thm/arsnova/domain/LearningProgress.java
rename to src/main/java/de/thm/arsnova/services/score/ScoreCalculator.java
index e3f0f3174f956c0712ac5002faa7349f5416f12d..8dbaa8a7a817b1d61ca3b967f82db7e23d18cd9d 100644
--- a/src/main/java/de/thm/arsnova/domain/LearningProgress.java
+++ b/src/main/java/de/thm/arsnova/services/score/ScoreCalculator.java
@@ -15,18 +15,18 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 
 /**
- * Defines the core functionality which the learning progress calculation should provide.
+ * Defines the core functionality which the score calculation should provide.
  */
-public interface LearningProgress {
+public interface ScoreCalculator {
 
-	LearningProgressValues getCourseProgress(Session session);
+	ScoreStatistics getCourseProgress(Session session);
 
-	LearningProgressValues getMyProgress(Session session, User user);
+	ScoreStatistics getMyProgress(Session session, User user);
 }
diff --git a/src/main/java/de/thm/arsnova/domain/ILearningProgressFactory.java b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactory.java
similarity index 84%
rename from src/main/java/de/thm/arsnova/domain/ILearningProgressFactory.java
rename to src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactory.java
index 072682946fc1b6c489c79b38edbf9cfc4155303f..f7125e807491fea6a71b8c7e4b1681f65b15a6de 100644
--- a/src/main/java/de/thm/arsnova/domain/ILearningProgressFactory.java
+++ b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactory.java
@@ -15,13 +15,13 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 /**
  * Interface for Spring dependency injection.
  */
-public interface ILearningProgressFactory {
+public interface ScoreCalculatorFactory {
 
-	LearningProgress create(String progressType, String questionVariant);
+	ScoreCalculator create(String type, String questionVariant);
 
 }
diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactoryImpl.java
similarity index 68%
rename from src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java
rename to src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactoryImpl.java
index a5596c0b73bafa8a37d2ed287ce4b9e66853606b..c6214cd66619094261e41864966217085eebb405 100644
--- a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java
+++ b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorFactoryImpl.java
@@ -15,7 +15,7 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.events.*;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
@@ -26,12 +26,12 @@ import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.stereotype.Component;
 
 /**
- * Creates a learning progress implementation.
+ * Creates a score calculator implementation.
  *
- * This class additionally clears all learning progress caches and reports this via event system.
+ * This class additionally clears all score caches and reports this via event system.
  */
 @Component
-public class LearningProgressFactory implements NovaEventVisitor, ILearningProgressFactory, ApplicationEventPublisherAware {
+public class ScoreCalculatorFactoryImpl implements ArsnovaEventVisitor, ScoreCalculatorFactory, ApplicationEventPublisherAware {
 
 	@Autowired
 	private SessionStatisticsRepository sessionStatisticsRepository;
@@ -39,15 +39,15 @@ public class LearningProgressFactory implements NovaEventVisitor, ILearningProgr
 	private ApplicationEventPublisher publisher;
 
 	@Override
-	public LearningProgress create(String progressType, String questionVariant) {
-		VariantLearningProgress learningProgress;
-		if ("questions".equals(progressType)) {
-			learningProgress = new QuestionBasedLearningProgress(sessionStatisticsRepository);
+	public ScoreCalculator create(String type, String questionVariant) {
+		VariantScoreCalculator scoreCalculator;
+		if ("questions".equals(type)) {
+			scoreCalculator = new QuestionBasedScoreCalculator(sessionStatisticsRepository);
 		} else {
-			learningProgress = new PointBasedLearningProgress(sessionStatisticsRepository);
+			scoreCalculator = new ScoreBasedScoreCalculator(sessionStatisticsRepository);
 		}
-		learningProgress.setQuestionVariant(questionVariant);
-		return learningProgress;
+		scoreCalculator.setQuestionVariant(questionVariant);
+		return scoreCalculator;
 	}
 
 	@Override
@@ -59,79 +59,79 @@ public class LearningProgressFactory implements NovaEventVisitor, ILearningProgr
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(NewQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(UnlockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(UnlockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(LockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(LockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(NewAnswerEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteAnswerEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteAllQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteAllQuestionsAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteAllPreparationAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(DeleteAllLectureAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@CacheEvict(value = "learningprogress", key = "#event.Session")
 	@Override
 	public void visit(PiRoundResetEvent event) {
-		this.publisher.publishEvent(new ChangeLearningProgressEvent(this, event.getSession()));
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getSession()));
 	}
 
 	@Override
@@ -144,7 +144,7 @@ public class LearningProgressFactory implements NovaEventVisitor, ILearningProgr
 	public void visit(StatusSessionEvent statusSessionEvent) { }
 
 	@Override
-	public void visit(ChangeLearningProgressEvent changeLearningProgress) { }
+	public void visit(ChangeScoreEvent changeLearningProgress) { }
 
 	@Override
 	public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) { }
diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgressListener.java b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorListener.java
similarity index 70%
rename from src/main/java/de/thm/arsnova/domain/LearningProgressListener.java
rename to src/main/java/de/thm/arsnova/services/score/ScoreCalculatorListener.java
index 2a11b6d4cb61ea2d1c5033509ee8abbb2cd16fe5..ded6ef1d05dcc215cd3026115c1d703a0d4aab43 100644
--- a/src/main/java/de/thm/arsnova/domain/LearningProgressListener.java
+++ b/src/main/java/de/thm/arsnova/services/score/ScoreCalculatorListener.java
@@ -15,28 +15,28 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
-import de.thm.arsnova.events.NovaEvent;
-import de.thm.arsnova.events.NovaEventVisitor;
+import de.thm.arsnova.events.ArsnovaEvent;
+import de.thm.arsnova.events.ArsnovaEventVisitor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
 
 /**
- * Listener registration for the learning progress.
+ * Listener registration for the score.
  *
  * Note that this class is necessary in order for the annotations to work.
  */
 @Component
-public class LearningProgressListener implements ApplicationListener<NovaEvent> {
+public class ScoreCalculatorListener implements ApplicationListener<ArsnovaEvent> {
 
 	@Autowired
-	private ILearningProgressFactory learningProgressFactory;
+	private ScoreCalculatorFactory scoreCalculatorFactory;
 
 	@Override
-	public void onApplicationEvent(NovaEvent event) {
-		event.accept((NovaEventVisitor) learningProgressFactory);
+	public void onApplicationEvent(ArsnovaEvent event) {
+		event.accept((ArsnovaEventVisitor) scoreCalculatorFactory);
 	}
 
 }
diff --git a/src/main/java/de/thm/arsnova/domain/UserScore.java b/src/main/java/de/thm/arsnova/services/score/UserScore.java
similarity index 97%
rename from src/main/java/de/thm/arsnova/domain/UserScore.java
rename to src/main/java/de/thm/arsnova/services/score/UserScore.java
index dddd611dfa4590df07ecbeeb13ad27eb6633251e..9a31de8e2d0b1b7eb0617d79527d46599aee12b5 100644
--- a/src/main/java/de/thm/arsnova/domain/UserScore.java
+++ b/src/main/java/de/thm/arsnova/services/score/UserScore.java
@@ -15,7 +15,7 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.User;
 
diff --git a/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java b/src/main/java/de/thm/arsnova/services/score/VariantScoreCalculator.java
similarity index 71%
rename from src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java
rename to src/main/java/de/thm/arsnova/services/score/VariantScoreCalculator.java
index 7e1c069701fa3fe336d6f7a1042185a69d080ac0..3fb7266b17f1fae630154fc5a9fd1de01bdfe5d1 100644
--- a/src/main/java/de/thm/arsnova/domain/VariantLearningProgress.java
+++ b/src/main/java/de/thm/arsnova/services/score/VariantScoreCalculator.java
@@ -15,25 +15,25 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
 
 /**
- * Base class for the learning progress feature that allows filtering on the question variant.
+ * Base class for the score feature that allows filtering on the question variant.
  */
-abstract class VariantLearningProgress implements LearningProgress {
+abstract class VariantScoreCalculator implements ScoreCalculator {
 
-	protected CourseScore courseScore;
+	protected Score courseScore;
 
 	private String questionVariant;
 
 	private final SessionStatisticsRepository sessionStatisticsRepository;
 
-	public VariantLearningProgress(final SessionStatisticsRepository sessionStatisticsRepository) {
+	public VariantScoreCalculator(final SessionStatisticsRepository sessionStatisticsRepository) {
 		this.sessionStatisticsRepository = sessionStatisticsRepository;
 	}
 
@@ -46,16 +46,16 @@ abstract class VariantLearningProgress implements LearningProgress {
 	}
 
 	@Override
-	public LearningProgressValues getCourseProgress(Session session) {
+	public ScoreStatistics getCourseProgress(Session session) {
 		this.loadProgress(session);
 		this.filterVariant();
 		return this.createCourseProgress();
 	}
 
-	protected abstract LearningProgressValues createCourseProgress();
+	protected abstract ScoreStatistics createCourseProgress();
 
 	@Override
-	public LearningProgressValues getMyProgress(Session session, User user) {
+	public ScoreStatistics getMyProgress(Session session, User user) {
 		this.loadProgress(session);
 		this.filterVariant();
 		return this.createMyProgress(user);
@@ -67,6 +67,6 @@ abstract class VariantLearningProgress implements LearningProgress {
 		}
 	}
 
-	protected abstract LearningProgressValues createMyProgress(User user);
+	protected abstract ScoreStatistics createMyProgress(User user);
 
 }
diff --git a/src/main/java/de/thm/arsnova/ImageUtils.java b/src/main/java/de/thm/arsnova/util/ImageUtils.java
similarity index 99%
rename from src/main/java/de/thm/arsnova/ImageUtils.java
rename to src/main/java/de/thm/arsnova/util/ImageUtils.java
index 6349cc8444a3800897cbb49275e848bfbcd10643..4c676add1d22953286321535c6f22df2549a2823 100644
--- a/src/main/java/de/thm/arsnova/ImageUtils.java
+++ b/src/main/java/de/thm/arsnova/util/ImageUtils.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.util;
 
 import de.thm.arsnova.entities.Answer;
 import org.apache.commons.codec.binary.Base64;
diff --git a/src/main/java/de/thm/arsnova/PaginationListDecorator.java b/src/main/java/de/thm/arsnova/util/PaginationListDecorator.java
similarity index 99%
rename from src/main/java/de/thm/arsnova/PaginationListDecorator.java
rename to src/main/java/de/thm/arsnova/util/PaginationListDecorator.java
index 6f9fcf448f962b93d83d52b59f2a5b593c83a479..1a764ce6cb711a4d2ff5b1ff80506cc0727be0ea 100644
--- a/src/main/java/de/thm/arsnova/PaginationListDecorator.java
+++ b/src/main/java/de/thm/arsnova/util/PaginationListDecorator.java
@@ -15,7 +15,7 @@
  * 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;
+package de.thm.arsnova.util;
 
 import java.util.Collection;
 import java.util.Iterator;
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocket.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServer.java
similarity index 92%
rename from src/main/java/de/thm/arsnova/socket/ARSnovaSocket.java
rename to src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServer.java
index 313e330cd6628d70f771b8ee14b5cd83bce79cbe..800b9314d17d3360a04b7e7ede303df35a725715 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocket.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServer.java
@@ -15,14 +15,14 @@
  * 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.socket;
+package de.thm.arsnova.websocket;
 
 /**
  * This interface is used to auto-wire the Socket Server.
  *
  * Extend this interface as you see fit.
  */
-public interface ARSnovaSocket {
+public interface ArsnovaSocketioServer {
 
 	boolean isUseSSL();
 
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
similarity index 95%
rename from src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
rename to src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
index b348dc32cc48c729bde2d61fbad4c0b28c0d26ff..6cd3bf2a8df7e9be7211318752627cb2ac3eb5b9 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
@@ -15,7 +15,7 @@
  * 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.socket;
+package de.thm.arsnova.websocket;
 
 import com.codahale.metrics.annotation.Timed;
 import com.corundumstudio.socketio.AckRequest;
@@ -30,18 +30,18 @@ import com.corundumstudio.socketio.protocol.Packet;
 import com.corundumstudio.socketio.protocol.PacketType;
 import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressOptions;
+import de.thm.arsnova.entities.transport.ScoreOptions;
 import de.thm.arsnova.events.*;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
-import de.thm.arsnova.services.IFeedbackService;
-import de.thm.arsnova.services.IContentService;
-import de.thm.arsnova.services.ISessionService;
-import de.thm.arsnova.services.IUserService;
-import de.thm.arsnova.socket.message.Feedback;
-import de.thm.arsnova.socket.message.Content;
-import de.thm.arsnova.socket.message.Session;
+import de.thm.arsnova.services.FeedbackService;
+import de.thm.arsnova.services.ContentService;
+import de.thm.arsnova.services.SessionService;
+import de.thm.arsnova.services.UserService;
+import de.thm.arsnova.websocket.message.Feedback;
+import de.thm.arsnova.websocket.message.Content;
+import de.thm.arsnova.websocket.message.Session;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -64,21 +64,21 @@ import java.util.UUID;
  * Web socket implementation based on Socket.io.
  */
 @Component
-public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
+public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, ArsnovaEventVisitor {
 
 	@Autowired
-	private IFeedbackService feedbackService;
+	private FeedbackService feedbackService;
 
 	@Autowired
-	private IUserService userService;
+	private UserService userService;
 
 	@Autowired
-	private ISessionService sessionService;
+	private SessionService sessionService;
 
 	@Autowired
-	private IContentService contentService;
+	private ContentService contentService;
 
-	private static final Logger logger = LoggerFactory.getLogger(ARSnovaSocketIOServer.class);
+	private static final Logger logger = LoggerFactory.getLogger(ArsnovaSocketioServerImpl.class);
 
 	private int portNumber;
 	private String hostIp;
@@ -88,7 +88,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	private final Configuration config;
 	private SocketIOServer server;
 
-	public ARSnovaSocketIOServer() {
+	public ArsnovaSocketioServerImpl() {
 		config = new Configuration();
 	}
 
@@ -212,17 +212,17 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 
 		server.addEventListener(
 				"setLearningProgressOptions",
-				LearningProgressOptions.class,
-				new DataListener<LearningProgressOptions>() {
+				ScoreOptions.class,
+				new DataListener<ScoreOptions>() {
 			@Override
 			@Timed(name = "setLearningProgressOptionsEvent.onData")
-			public void onData(SocketIOClient client, LearningProgressOptions progressOptions, AckRequest ack) {
+			public void onData(SocketIOClient client, ScoreOptions scoreOptions, AckRequest ack) {
 				final User user = userService.getUser2SocketId(client.getSessionId());
-				final de.thm.arsnova.entities.Session session = sessionService.getSessionInternal(progressOptions.getSessionKeyword(), user);
+				final de.thm.arsnova.entities.Session session = sessionService.getSessionInternal(scoreOptions.getSessionKeyword(), user);
 				if (session.isCreator(user)) {
-					session.setLearningProgressOptions(progressOptions.toEntity());
+					session.setLearningProgressOptions(scoreOptions.toEntity());
 					sessionService.updateSessionInternal(session, user);
-					broadcastInSession(session.getKeyword(), "learningProgressOptions", progressOptions.toEntity());
+					broadcastInSession(session.getKeyword(), "learningProgressOptions", scoreOptions.toEntity());
 				}
 			}
 		});
@@ -665,7 +665,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	}
 
 	@Override
-	public void visit(ChangeLearningProgressEvent event) {
+	public void visit(ChangeScoreEvent event) {
 		broadcastInSession(event.getSession().getKeyword(), "learningProgressChange", null);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketListener.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerListener.java
similarity index 75%
rename from src/main/java/de/thm/arsnova/socket/ARSnovaSocketListener.java
rename to src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerListener.java
index 6c740888f5a7d433f9fd151acebb131702f568ba..8bcfb88e045948e72f364d35720416d406a61ed0 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketListener.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerListener.java
@@ -15,10 +15,10 @@
  * 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.socket;
+package de.thm.arsnova.websocket;
 
-import de.thm.arsnova.events.NovaEvent;
-import de.thm.arsnova.events.NovaEventVisitor;
+import de.thm.arsnova.events.ArsnovaEvent;
+import de.thm.arsnova.events.ArsnovaEventVisitor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
@@ -28,14 +28,14 @@ import org.springframework.stereotype.Component;
  * This would result in Spring method annotations not working.
  */
 @Component
-public class ARSnovaSocketListener implements ApplicationListener<NovaEvent> {
+public class ArsnovaSocketioServerListener implements ApplicationListener<ArsnovaEvent> {
 
 	@Autowired
-	private ARSnovaSocket socketServer;
+	private ArsnovaSocketioServer socketioServer;
 
 	@Override
-	public void onApplicationEvent(NovaEvent event) {
-		event.accept((NovaEventVisitor) socketServer);
+	public void onApplicationEvent(ArsnovaEvent event) {
+		event.accept((ArsnovaEventVisitor) socketioServer);
 	}
 
 }
diff --git a/src/main/java/de/thm/arsnova/socket/message/Content.java b/src/main/java/de/thm/arsnova/websocket/message/Content.java
similarity index 96%
rename from src/main/java/de/thm/arsnova/socket/message/Content.java
rename to src/main/java/de/thm/arsnova/websocket/message/Content.java
index f462baa7bb7e62b81f15e512774f630d9e848d80..73e84c3ec3b9a6ed2a5861bb9088d5b82dbbd741 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Content.java
+++ b/src/main/java/de/thm/arsnova/websocket/message/Content.java
@@ -15,7 +15,7 @@
  * 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.socket.message;
+package de.thm.arsnova.websocket.message;
 
 /**
  * Represents a question.
diff --git a/src/main/java/de/thm/arsnova/socket/message/Feedback.java b/src/main/java/de/thm/arsnova/websocket/message/Feedback.java
similarity index 96%
rename from src/main/java/de/thm/arsnova/socket/message/Feedback.java
rename to src/main/java/de/thm/arsnova/websocket/message/Feedback.java
index 211e10d6a6a39a4b3ac036b1db46d9c47a0da639..0f7966b14bb2aefa953714f0b24d9ddecf43f0ae 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Feedback.java
+++ b/src/main/java/de/thm/arsnova/websocket/message/Feedback.java
@@ -15,7 +15,7 @@
  * 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.socket.message;
+package de.thm.arsnova.websocket.message;
 
 /**
  * The feedback values.
diff --git a/src/main/java/de/thm/arsnova/socket/message/Session.java b/src/main/java/de/thm/arsnova/websocket/message/Session.java
similarity index 95%
rename from src/main/java/de/thm/arsnova/socket/message/Session.java
rename to src/main/java/de/thm/arsnova/websocket/message/Session.java
index 88f7c53d27e342ff6f0e804cd2943e03d4d97ec1..9e13bb2233dcd5c5863c7099e5ea0ef5470fca96 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Session.java
+++ b/src/main/java/de/thm/arsnova/websocket/message/Session.java
@@ -15,7 +15,7 @@
  * 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.socket.message;
+package de.thm.arsnova.websocket.message;
 
 /**
  * Represents a session.
diff --git a/src/main/java/de/thm/arsnova/socket/message/package-info.java b/src/main/java/de/thm/arsnova/websocket/message/package-info.java
similarity index 60%
rename from src/main/java/de/thm/arsnova/socket/message/package-info.java
rename to src/main/java/de/thm/arsnova/websocket/message/package-info.java
index 98beea04a699cb10d38d1a3d1cac7edf0cef847c..0af4ab419bcf0c192d252606e1b41c0c15afb63d 100644
--- a/src/main/java/de/thm/arsnova/socket/message/package-info.java
+++ b/src/main/java/de/thm/arsnova/websocket/message/package-info.java
@@ -1,4 +1,4 @@
 /**
  * Contains classes that are used as web socket messages
  */
-package de.thm.arsnova.socket.message;
+package de.thm.arsnova.websocket.message;
diff --git a/src/main/java/de/thm/arsnova/socket/package-info.java b/src/main/java/de/thm/arsnova/websocket/package-info.java
similarity index 66%
rename from src/main/java/de/thm/arsnova/socket/package-info.java
rename to src/main/java/de/thm/arsnova/websocket/package-info.java
index d4f65e727417e57da0546892bab342168a7d09d4..3fd6e43c013b192de303d345594ca8382b09eec3 100644
--- a/src/main/java/de/thm/arsnova/socket/package-info.java
+++ b/src/main/java/de/thm/arsnova/websocket/package-info.java
@@ -1,4 +1,4 @@
 /**
  * Classes and interfaces for communication over web sockets
  */
-package de.thm.arsnova.socket;
+package de.thm.arsnova.websocket;
diff --git a/src/site/markdown/development/event-system.md b/src/site/markdown/development/event-system.md
index d9f4459a1066df3a0ba403feeafd9fe9dcc77dd0..71132e71bbc0d34254e47be3f277bf7090ec352e 100644
--- a/src/site/markdown/development/event-system.md
+++ b/src/site/markdown/development/event-system.md
@@ -11,18 +11,18 @@ A class is able to send events by implementing the `ApplicationEventPublisherAwa
 ```java
 publisher.publishEvent(theEvent);
 ```
-where `theEvent` is an object of type `ApplicationEvent`. For ARSnova, the base class `NovaEvent` should be used instead. All of ARSnova's internal events are subtypes of `NovaEvent`.
+where `theEvent` is an object of type `ApplicationEvent`. For ARSnova, the base class `ArsovaEvent` should be used instead. All of ARSnova's internal events are subtypes of `ArsovaEvent`.
 
 _Note_: Events are sent and received on the same thread, i.e., it is a synchronous operation.
 
 
 ## How to receive events?
 
-Events are received by implementing the `ApplicationListener<NovaEvent>` interface. The associated method gets passed in a `NovaEvent`, which is the base class of all of ARSnova's events. However, this type itself is not very useful. The real type can be revealed using double dispatch, which is the basis of the Visitor pattern. Therefore, the event should be forwarded to a class that implements the `NovaEventVisitor` interface. This could be the same class that received the event.
+Events are received by implementing the `ApplicationListener<ArsovaEvent>` interface. The associated method gets passed in a `ArsovaEvent`, which is the base class of all of ARSnova's events. However, this type itself is not very useful. The real type can be revealed using double dispatch, which is the basis of the Visitor pattern. Therefore, the event should be forwarded to a class that implements the `ArsovaEvent` interface. This could be the same class that received the event.
 
 _Note_: If the class implementing the Visitor needs to have some of Spring's annotations on the event methods, like, for example, to cache some values using `@Cacheable`, the Listener and the Visitor must be different objects.
 
 
 ## How to create custom events?
 
-Subclass either `NovaEvent` or `SessionEvent`. The former is for generic events that are not tied to a specific session, while the latter is for cases where the event only makes sense in the context of a session.
+Subclass either `ArsovaEvent` or `SessionEvent`. The former is for generic events that are not tied to a specific session, while the latter is for cases where the event only makes sense in the context of a session.
diff --git a/src/test/java/de/thm/arsnova/config/TestAppConfig.java b/src/test/java/de/thm/arsnova/config/TestAppConfig.java
index f2759881edb031f59b10e8070fe79f706b9400ad..c3517272dc82ac670dab6f4708b561452eba08f4 100644
--- a/src/test/java/de/thm/arsnova/config/TestAppConfig.java
+++ b/src/test/java/de/thm/arsnova/config/TestAppConfig.java
@@ -1,8 +1,8 @@
 package de.thm.arsnova.config;
 
 import de.thm.arsnova.services.StubUserService;
-import de.thm.arsnova.socket.ARSnovaSocket;
-import de.thm.arsnova.socket.ARSnovaSocketIOServer;
+import de.thm.arsnova.websocket.ArsnovaSocketioServer;
+import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.config.CustomScopeConfigurer;
 import org.springframework.cache.annotation.EnableCaching;
@@ -10,6 +10,7 @@ import org.springframework.context.annotation.AdviceMode;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
 import org.springframework.context.annotation.Profile;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
@@ -23,7 +24,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 		"de.thm.arsnova.aop",
 		"de.thm.arsnova.cache",
 		"de.thm.arsnova.controller",
-		"de.thm.arsnova.domain",
 		"de.thm.arsnova.dao",
 		"de.thm.arsnova.events",
 		"de.thm.arsnova.security",
@@ -59,9 +59,9 @@ public class TestAppConfig {
 	}
 
 	@Bean(name = "socketServer", initMethod = "startServer", destroyMethod = "stopServer")
-	public ARSnovaSocket socketTestServer() {
+	public ArsnovaSocketioServer socketTestServer() {
 		final int testSocketPort = 1234 + testPortOffset++ % 10;
-		final ARSnovaSocketIOServer socketServer = new ARSnovaSocketIOServer();
+		final ArsnovaSocketioServerImpl socketServer = new ArsnovaSocketioServerImpl();
 		socketServer.setHostIp(socketAddress);
 		socketServer.setPortNumber(socketPort + testSocketPort);
 
@@ -69,6 +69,7 @@ public class TestAppConfig {
 	}
 
 	@Bean
+	@Primary
 	public StubUserService stubUserService() {
 		return new StubUserService();
 	}
diff --git a/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java b/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java
index 6ad50678aca2eccf4883f5bfaffc82e5d3e98d58..17716f734f469fa3f553bc96ff864adfd91d9026 100644
--- a/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java
+++ b/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.config;
 
-import de.thm.arsnova.CasUserDetailsService;
+import de.thm.arsnova.security.CasUserDetailsService;
 import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
 import org.pac4j.oauth.client.FacebookClient;
 import org.pac4j.oauth.client.Google2Client;
diff --git a/src/test/java/de/thm/arsnova/services/StubUserService.java b/src/test/java/de/thm/arsnova/services/StubUserService.java
index b072375b65222022d1a67282e5528308d0e62269..6a923b213d6c493a2a8901932b7ca576b419aec1 100644
--- a/src/test/java/de/thm/arsnova/services/StubUserService.java
+++ b/src/test/java/de/thm/arsnova/services/StubUserService.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.services;
 import de.thm.arsnova.entities.User;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 
-public class StubUserService extends UserService {
+public class StubUserService extends UserServiceImpl {
 
 	private User stubUser = null;
 
diff --git a/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java b/src/test/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculatorTest.java
similarity index 87%
rename from src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java
rename to src/test/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculatorTest.java
index 8adf79257cd12f28dafe08d6f6e91f6524628e3e..d46874fc7d0a7f5e82cc50fddd981bf06faa34cf 100644
--- a/src/test/java/de/thm/arsnova/domain/QuestionBasedLearningProgressTest.java
+++ b/src/test/java/de/thm/arsnova/services/score/QuestionBasedScoreCalculatorTest.java
@@ -15,12 +15,15 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.TestUser;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
+import de.thm.arsnova.services.score.QuestionBasedScoreCalculator;
+import de.thm.arsnova.services.score.Score;
+import de.thm.arsnova.services.score.VariantScoreCalculator;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -28,10 +31,10 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-public class QuestionBasedLearningProgressTest {
+public class QuestionBasedScoreCalculatorTest {
 
-	private CourseScore courseScore;
-	private VariantLearningProgress lp;
+	private Score courseScore;
+	private VariantScoreCalculator lp;
 
 	private int id = 1;
 
@@ -49,10 +52,10 @@ public class QuestionBasedLearningProgressTest {
 
 	@Before
 	public void setUp() {
-		this.courseScore = new CourseScore();
+		this.courseScore = new Score();
 		SessionStatisticsRepository db = mock(SessionStatisticsRepository.class);
 		when(db.getLearningProgress(null)).thenReturn(courseScore);
-		this.lp = new QuestionBasedLearningProgress(db);
+		this.lp = new QuestionBasedScoreCalculator(db);
 	}
 
 	/**
@@ -66,11 +69,11 @@ public class QuestionBasedLearningProgressTest {
 		String questionId = this.addQuestion("lecture", questionMaxValue);
 		this.addAnswer(questionId, user, userScore);
 
-		LearningProgressValues expected = new LearningProgressValues();
+		ScoreStatistics expected = new ScoreStatistics();
 		expected.setCourseProgress(0);
 		expected.setMyProgress(0);
 		expected.setNumQuestions(0);
-		LearningProgressValues actual = lp.getMyProgress(null, user);
+		ScoreStatistics actual = lp.getMyProgress(null, user);
 
 		assertEquals(expected, actual);
 	}
@@ -83,11 +86,11 @@ public class QuestionBasedLearningProgressTest {
 		courseScore.addAnswer("question-without-correct-answers", 1, user.getUsername(), 0);
 		courseScore.addAnswer("question-with-correct-answers", 1, user.getUsername(), 50);
 
-		LearningProgressValues expected = new LearningProgressValues();
+		ScoreStatistics expected = new ScoreStatistics();
 		expected.setCourseProgress(100);
 		expected.setMyProgress(100);
 		expected.setNumQuestions(1);
-		LearningProgressValues actual = lp.getMyProgress(null, user);
+		ScoreStatistics actual = lp.getMyProgress(null, user);
 
 		assertEquals(expected, actual);
 	}
@@ -165,11 +168,11 @@ public class QuestionBasedLearningProgressTest {
 		this.addAnswer(q2, u2, 0);
 
 		lp.setQuestionVariant("lecture");
-		LearningProgressValues lectureProgress = lp.getCourseProgress(null);
-		LearningProgressValues myLectureProgress = lp.getMyProgress(null, u1);
+		ScoreStatistics lectureProgress = lp.getCourseProgress(null);
+		ScoreStatistics myLectureProgress = lp.getMyProgress(null, u1);
 		lp.setQuestionVariant("preparation");
-		LearningProgressValues prepProgress = lp.getCourseProgress(null);
-		LearningProgressValues myPrepProgress = lp.getMyProgress(null, u1);
+		ScoreStatistics prepProgress = lp.getCourseProgress(null);
+		ScoreStatistics myPrepProgress = lp.getMyProgress(null, u1);
 
 		assertEquals(100, lectureProgress.getCourseProgress());
 		assertEquals(100, myLectureProgress.getMyProgress());
@@ -190,8 +193,8 @@ public class QuestionBasedLearningProgressTest {
 		courseScore.addAnswer("q1", 1, u2.getUsername(), 100);
 		courseScore.addAnswer("q1", 2, u2.getUsername(), 25);
 
-		LearningProgressValues u1Progress = lp.getMyProgress(null, u1);
-		LearningProgressValues u2Progress = lp.getMyProgress(null, u2);
+		ScoreStatistics u1Progress = lp.getMyProgress(null, u1);
+		ScoreStatistics u2Progress = lp.getMyProgress(null, u2);
 
 		// only the answer for round 2 should be considered
 		assertEquals(50, u1Progress.getCourseProgress());
diff --git a/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java b/src/test/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculatorTest.java
similarity index 84%
rename from src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java
rename to src/test/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculatorTest.java
index 6f42527dc61881ea06d533e29c7932e85c464317..0909300b0764d1f9dccb9eaef42acf8163610e2f 100644
--- a/src/test/java/de/thm/arsnova/domain/PointBasedLearningProgressTest.java
+++ b/src/test/java/de/thm/arsnova/services/score/ScoreBasedScoreCalculatorTest.java
@@ -15,12 +15,15 @@
  * 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.domain;
+package de.thm.arsnova.services.score;
 
 import de.thm.arsnova.entities.TestUser;
 import de.thm.arsnova.entities.User;
-import de.thm.arsnova.entities.transport.LearningProgressValues;
+import de.thm.arsnova.entities.transport.ScoreStatistics;
 import de.thm.arsnova.persistance.SessionStatisticsRepository;
+import de.thm.arsnova.services.score.Score;
+import de.thm.arsnova.services.score.ScoreBasedScoreCalculator;
+import de.thm.arsnova.services.score.VariantScoreCalculator;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -28,10 +31,10 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-public class PointBasedLearningProgressTest {
+public class ScoreBasedScoreCalculatorTest {
 
-	private CourseScore courseScore;
-	private VariantLearningProgress lp;
+	private Score courseScore;
+	private VariantScoreCalculator lp;
 
 	private int id = 1;
 
@@ -49,10 +52,10 @@ public class PointBasedLearningProgressTest {
 
 	@Before
 	public void setUp() {
-		this.courseScore = new CourseScore();
+		this.courseScore = new Score();
 		SessionStatisticsRepository db = mock(SessionStatisticsRepository.class);
 		when(db.getLearningProgress(null)).thenReturn(courseScore);
-		this.lp = new PointBasedLearningProgress(db);
+		this.lp = new ScoreBasedScoreCalculator(db);
 	}
 
 	@Test
@@ -68,11 +71,11 @@ public class PointBasedLearningProgressTest {
 		this.addAnswer(q2, u2, 0);
 
 		lp.setQuestionVariant("lecture");
-		LearningProgressValues lectureProgress = lp.getCourseProgress(null);
-		LearningProgressValues myLectureProgress = lp.getMyProgress(null, u1);
+		ScoreStatistics lectureProgress = lp.getCourseProgress(null);
+		ScoreStatistics myLectureProgress = lp.getMyProgress(null, u1);
 		lp.setQuestionVariant("preparation");
-		LearningProgressValues prepProgress = lp.getCourseProgress(null);
-		LearningProgressValues myPrepProgress = lp.getMyProgress(null, u1);
+		ScoreStatistics prepProgress = lp.getCourseProgress(null);
+		ScoreStatistics myPrepProgress = lp.getMyProgress(null, u1);
 
 		assertEquals(100, lectureProgress.getCourseProgress());
 		assertEquals(100, myLectureProgress.getMyProgress());
@@ -97,7 +100,7 @@ public class PointBasedLearningProgressTest {
 		this.addAnswer(q3, u2, 100);
 
 		lp.setQuestionVariant("lecture");
-		LearningProgressValues u1LectureProgress = lp.getMyProgress(null, u1);
+		ScoreStatistics u1LectureProgress = lp.getMyProgress(null, u1);
 		// 200 / 300 = 0,67
 		assertEquals(67, u1LectureProgress.getCourseProgress());
 		assertEquals(67, u1LectureProgress.getMyProgress());
@@ -116,8 +119,8 @@ public class PointBasedLearningProgressTest {
 		courseScore.addAnswer("q1", 1, u2.getUsername(), 75);
 		courseScore.addAnswer("q1", 2, u2.getUsername(), 25);
 
-		LearningProgressValues u1Progress = lp.getMyProgress(null, u1);
-		LearningProgressValues u2Progress = lp.getMyProgress(null, u2);
+		ScoreStatistics u1Progress = lp.getMyProgress(null, u1);
+		ScoreStatistics u2Progress = lp.getMyProgress(null, u2);
 
 		// only the answer for round 2 should be considered
 		assertEquals(50, u1Progress.getCourseProgress());
diff --git a/src/test/java/de/thm/arsnova/ImageUtilsTest.java b/src/test/java/de/thm/arsnova/util/ImageUtilsTest.java
similarity index 94%
rename from src/test/java/de/thm/arsnova/ImageUtilsTest.java
rename to src/test/java/de/thm/arsnova/util/ImageUtilsTest.java
index 5c27e1eb596eaf7dbd2809fd3154dd6c797bb232..a7f9c67d0b609489e6d9c5213b7025f7a7685bd2 100644
--- a/src/test/java/de/thm/arsnova/ImageUtilsTest.java
+++ b/src/test/java/de/thm/arsnova/util/ImageUtilsTest.java
@@ -15,12 +15,13 @@
  * 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;
+package de.thm.arsnova.util;
 
 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.util.ImageUtils;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.test.context.ActiveProfiles;
@@ -28,8 +29,8 @@ import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.context.web.WebAppConfiguration;
 
-import static de.thm.arsnova.ImageUtils.IMAGE_PREFIX_MIDDLE;
-import static de.thm.arsnova.ImageUtils.IMAGE_PREFIX_START;
+import static de.thm.arsnova.util.ImageUtils.IMAGE_PREFIX_MIDDLE;
+import static de.thm.arsnova.util.ImageUtils.IMAGE_PREFIX_START;
 import static org.junit.Assert.*;
 
 @RunWith(SpringJUnit4ClassRunner.class)