diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 15ba074e1d1fbf71c5542784ca5985a510aaf3d2..90a34ad73be88f8f413f5caf51988d445b8c00a6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,78 +3,96 @@ image: 'maven:3.6.3-jdk-11-slim'
 variables:
   PROJECT_ARTIFACT_ID: kypo-adaptive-training
   DEPLOYMENT_INFO_VERSION_FILE: VERSION.txt
-  
+
 stages:
   - create_tag
   - build
   - tests
+  - docker_image_push
 
 create_tag:
   stage: create_tag
   before_script:
-  # ssh config
-  - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )
-  - eval $(ssh-agent -s)
-  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
-  - mkdir -p ~/.ssh
-  - chmod 700 ~/.ssh
-  - ssh -o 'StrictHostKeyChecking no' "$SSH_HOST_CHECKING_DOMAIN"
-  # git config
-  - apt-get install git -y
-  - git config --global user.email "$GIT_USER_EMAIL"
-  - git config --global user.name "$GIT_USER_NAME"
-  - git remote set-url origin "$GIT_CLONE_URL"
-  - git fetch
-  - git checkout master
+    # ssh config
+    - which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )
+    - eval $(ssh-agent -s)
+    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
+    - mkdir -p ~/.ssh
+    - chmod 700 ~/.ssh
+    - ssh -o 'StrictHostKeyChecking no' "$SSH_HOST_CHECKING_DOMAIN"
+    # git config
+    - apt-get install git -y
+    - git config --global user.email "$GIT_USER_EMAIL"
+    - git config --global user.name "$GIT_USER_NAME"
+    - git remote set-url origin "$GIT_CLONE_URL"
+    - git fetch
+    - git checkout master
   script:
-  - export VERSION=$(grep -oP '^([^\s]*)' $DEPLOYMENT_INFO_VERSION_FILE)
-  - export TAG_MESSAGE=$(grep -oP '(?<=\s)[^\s].*' $DEPLOYMENT_INFO_VERSION_FILE)
-  # update project in this project
-  - mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
-  - git commit -am "Update pom.xml version based on GitLab tag. Done by CI."
-  # generate swagger documentation
-  - mvn clean compile -DskipTests -Dswagger.skip=false -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
-  - git add doc-files/kypo-adaptive-training-open-api.yaml
-  - git commit -m "Updated Swagger documentation generated."
-  # update change log
-  - LAST_TAG=$(git tag | head -1)
-  - git log  --pretty=format:'%h -- %s' $LAST_TAG..HEAD --graph > CHANGELOG.md
-  - git add CHANGELOG.md
-  - git commit -m "CHANGELOG.md file updated with commits between the current and previous tag. Done by CI."
-  # create new tag
-  - git tag -a v$VERSION -m "$TAG_MESSAGE"
-  - git config --global push.followTags true
-  - git push
+    - export VERSION=$(grep -oP '^([^\s]*)' $DEPLOYMENT_INFO_VERSION_FILE)
+    - export TAG_MESSAGE=$(grep -oP '(?<=\s)[^\s].*' $DEPLOYMENT_INFO_VERSION_FILE)
+    # update project in this project
+    - mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+    - git commit -am "Update pom.xml version based on GitLab tag. Done by CI."
+    # generate swagger documentation
+    - mvn clean compile -DskipTests -Dswagger.skip=false -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+    - git add doc-files/kypo-adaptive-training-open-api.yaml
+    - git commit -m "Updated Swagger documentation generated."
+    # update change log
+    - LAST_TAG=$(git tag | head -1)
+    - git log  --pretty=format:'%h -- %s' $LAST_TAG..HEAD --graph > CHANGELOG.md
+    - git add CHANGELOG.md
+    - git commit -m "CHANGELOG.md file updated with commits between the current and previous tag. Done by CI."
+    # create new tag
+    - git tag -a v$VERSION -m "$TAG_MESSAGE"
+    - git config --global push.followTags true
+    - git push
   rules:
-  - if: '$CI_COMMIT_BRANCH == "master"'
-    changes:
-      - VERSION.txt
+    - if: '$CI_COMMIT_BRANCH == "master"'
+      changes:
+        - VERSION.txt
   artifacts:
     paths:
-    - ./pom.xml
-    - ./*/pom.xml
+      - ./pom.xml
+      - ./*/pom.xml
     expire_in: 1 hour
     when: on_success
-    
+
 build:
   stage: build
   script:
-  - mvn clean install -DskipTests -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+    - mvn clean install -DskipTests -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
   rules:
-  - if: '$CI_COMMIT_BRANCH'
+    - if: '$CI_COMMIT_BRANCH'
   artifacts:
     paths:
-    - target/$PROJECT_ARTIFACT_ID-*.jar
+      - target/$PROJECT_ARTIFACT_ID-*.jar
     expire_in: 1 hour
     when: on_success
-    
+
 tests:
   stage: tests
   script:
-  - mvn test -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+    - mvn test -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "master"'
+      changes:
+        - VERSION.txt
+      when: never
+    - if: '$CI_COMMIT_BRANCH'
+
+docker_image_push:
+  before_script: [ ] #prevent global before_script from running
+  variables:
+    CI_CUSTOM_IMAGE_NAME: kypo-adaptive-training-service
+  stage: docker_image_push
+  image:
+    name: gcr.io/kaniko-project/executor:v1.3.0-debug
+    entrypoint: [ "" ]
+  script:
+    - export CI_CUSTOM_REGISTRY_IMAGE="${CI_REGISTRY}/${CI_CUSTOM_REGISTRY_PATH}/${CI_CUSTOM_IMAGE_NAME}"
+    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_CUSTOM_REGISTRY_USER\",\"password\":\"$CI_CUSTOM_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_CUSTOM_REGISTRY_IMAGE:$CI_COMMIT_TAG --build-arg PROPRIETARY_REPO_URL=$PROPRIETARY_REPO_URL
   rules:
-  - if: '$CI_COMMIT_BRANCH == "master"'
-    changes:
-      - VERSION.txt
-    when: never
-  - if: '$CI_COMMIT_BRANCH'
+    - if: '$CI_COMMIT_TAG'
+    - if: '$CI_COMMIT_BRANCH'
+      when: never
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
index e76d1f3241d38db9b28f05133823bbed1ad289ff..a45eb6ba269cd38f8965cef786729790945d9537 100644
--- a/.mvn/wrapper/MavenWrapperDownloader.java
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 import java.net.*;
 import java.io.*;
 import java.nio.channels.*;
@@ -25,7 +26,7 @@ public class MavenWrapperDownloader {
      * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
      */
     private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
-        + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+            + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
 
     /**
      * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
@@ -54,7 +55,7 @@ public class MavenWrapperDownloader {
         // wrapperUrl parameter.
         File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
         String url = DEFAULT_DOWNLOAD_URL;
-        if(mavenWrapperPropertyFile.exists()) {
+        if (mavenWrapperPropertyFile.exists()) {
             FileInputStream mavenWrapperPropertyFileInputStream = null;
             try {
                 mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
@@ -65,7 +66,7 @@ public class MavenWrapperDownloader {
                 System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
             } finally {
                 try {
-                    if(mavenWrapperPropertyFileInputStream != null) {
+                    if (mavenWrapperPropertyFileInputStream != null) {
                         mavenWrapperPropertyFileInputStream.close();
                     }
                 } catch (IOException e) {
@@ -76,8 +77,8 @@ public class MavenWrapperDownloader {
         System.out.println("- Downloading from: " + url);
 
         File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
-        if(!outputFile.getParentFile().exists()) {
-            if(!outputFile.getParentFile().mkdirs()) {
+        if (!outputFile.getParentFile().exists()) {
+            if (!outputFile.getParentFile().mkdirs()) {
                 System.out.println(
                         "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
             }
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..43ad436edafed4f3affa65ada532e885df905d54
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,37 @@
+FROM maven:3.6.2-jdk-11-slim AS build
+
+## default environment variables for database settings
+ARG USERNAME=postgres
+ARG PASSWORD=postgres
+ARG POSTGRES_DB=adaptive-training
+## rename the artifact id to something else, e.g. kypo-adaptive-training
+ARG PROJECT_ARTIFACT_ID=kypo-adaptive-training
+
+## default link to proprietary repository, e.g., Gitlab repository
+ARG PROPRIETARY_REPO_URL=YOUR-PATH-TO-PROPRIETARY_REPO
+
+# install
+RUN apt-get update && apt-get install -y supervisor postgresql rsyslog
+
+# configure supervisor
+RUN mkdir -p /var/log/supervisor
+
+# configure postgres
+RUN /etc/init.d/postgresql start && \
+    su postgres -c "createdb -O \"$USERNAME\" $POSTGRES_DB" && \
+    su postgres -c "psql -c \"ALTER USER $USERNAME PASSWORD '$PASSWORD';\"" && \
+    /etc/init.d/postgresql stop
+
+# copy only essential parts
+COPY /etc/adaptive-training.properties /app/etc/adaptive-training.properties
+COPY supervisord.conf /app/supervisord.conf
+COPY pom.xml /app/pom.xml
+COPY src /app/src
+
+# build training
+WORKDIR /app
+RUN mvn clean install -DskipTests -Dproprietary-repo-url=$PROPRIETARY_REPO_URL
+COPY /target/$PROJECT_ARTIFACT_ID-*.jar kypo-adaptive-training.jar
+
+EXPOSE 8086
+ENTRYPOINT ["/usr/bin/supervisord", "-c", "/app/supervisord.conf"]
diff --git a/etc/adaptive-training.properties b/etc/adaptive-training.properties
new file mode 100644
index 0000000000000000000000000000000000000000..be6fe2ee685e113a91119681835cf6da74c07591
--- /dev/null
+++ b/etc/adaptive-training.properties
@@ -0,0 +1,37 @@
+# context
+server.servlet.context-path=/kypo-adaptive-training/api/v1
+server.port=8082
+## microservice name which is used in user management service for identification of this service, e.g. kypo2-training
+microservice.name=kypo-adaptive-training
+# calling user-and-group project
+user-and-group-server.uri=http://localhost:8084/kypo2-rest-user-and-group/api/v1
+# calling kypo2-openstack
+openstack-server.uri=http://localhost:8080/kypo-openstack/api/v1
+# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
+spring.datasource.url=jdbc:postgresql://localhost:5432/adaptive-training
+spring.datasource.username=postgres
+spring.datasource.password=postgres
+spring.datasource.driverClassName=org.postgresql.Driver
+# Jackson (e.g. converting Java 8 dates to ISO format
+spring.jackson.serialization.write_dates_as_timestamps=false
+spring.jackson.property-naming-strategy=SNAKE_CASE
+# spring-cloud
+spring.cloud.refresh.enabled=false
+# to fix: Method jmxMBeanExporter in org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration required a single bean, but 2 were found: (objMapperESClient,objectMapperForRestAPI)
+spring.jmx.enabled=false
+# OpenID Connect CSIRT-MU OIDC
+kypo.idp.4oauth.resource.clientIds=7b4bc97f-74cb-4b1a-8ee5-3d7f016c62a6
+kypo.idp.4oauth.resource.clientSecrets=EDzR7G_F15RvKME8BSdt-ZGiUUnNVQgzwNvGoTI-OFSfoxFputJ4dZ0ZLu4tCqcYNglBN3LYRwgjbvgdboN-7g
+kypo.idp.4oauth.scopes=openid,email,profile
+kypo.idp.4oauth.issuers=http://localhost:8080/csirtmu-dummy-issuer-server/
+management.health.refresh.enabled=false
+security.require-ssl=false
+javax.net.debug=ssl
+## overwrite default logback configuration file, e.g., /etc/kypo/logback.xml, NOT REQUIRED
+#logging.config={path to logback config file}
+## set logger levels using pattern logging.level.<logger-name>=<level>, NOT REQUIRED
+logging.level.cz.muni.ics=WARN
+kypo.audit.syslog.host=localhost
+kypo.audit.syslog.port=514
+kypo.audit.messages.format=KYPO_PORTAL_EVENTS_AUDIT --- [%thread] %logger{5} --- %msg%n
+spring.flyway.enabled=false
\ No newline at end of file
diff --git a/etc/logback-spring.xml b/etc/logback-spring.xml
new file mode 100644
index 0000000000000000000000000000000000000000..03926fe89a9a40064bb3af14783ce3b806d605fd
--- /dev/null
+++ b/etc/logback-spring.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml"/>
+
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%-5level --- [%thread] %logger{5} --- %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="guru.springframework.controllers" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="guru.springframework.helpers" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="org.springframework.boot" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="cz.muni.ics" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+
+    <appender name="AUDIT_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>AUDIT --- [%thread] %logger{5} --- %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="cz.muni.ics.kypo.training.adaptive.service.audit" level="INFO" additivity="false">
+        <appender-ref ref="AUDIT_CONSOLE"/>
+    </logger>
+    <logger name="org.springframework.boot" level="ERROR" additivity="false"/>
+
+</configuration>
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 5bb291055b54440007b0ac22da89dc606029bf9f..e23acc354a3e3d150f6f395c9099a669eb6d937a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
@@ -9,7 +9,7 @@
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
     <groupId>cz.muni.ics.kypo</groupId>
-    <artifactId>kypo2-adaptive-training</artifactId>
+    <artifactId>kypo-adaptive-training</artifactId>
     <version>1.0.0</version>
     <packaging>jar</packaging>
     <name>KYPO2 Adaptive Training</name>
@@ -19,14 +19,49 @@
         <java.version>11</java.version>
 
         <org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
-        <swagger.version>2.9.2</swagger.version>
+        <swagger.version>3.0.0</swagger.version>
+        <proprietary-repo-id>gitlab-maven</proprietary-repo-id>
+        <apt-maven-plugin.version>1.1.3</apt-maven-plugin.version>
+        <squiggly.filter.jackson.version>1.3.18</squiggly.filter.jackson.version>
     </properties>
 
+    <repositories>
+        <repository>
+            <id>${proprietary-repo-id}</id>
+            <url>${proprietary-repo-url}</url>
+        </repository>
+    </repositories>
+
+    <dependencyManagement>
+        <!--fix springfox error-->
+        <dependencies>
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>20.0</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <dependencies>
+        <dependency>
+            <groupId>cz.muni.ics.kypo</groupId>
+            <artifactId>kypo2-security-commons</artifactId>
+            <version>1.0.40</version>
+        </dependency>
+        <dependency>
+            <groupId>cz.muni.ics.kypo</groupId>
+            <artifactId>kypo-elasticsearch-documents</artifactId>
+            <version>1.0.18</version>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
@@ -54,23 +89,52 @@
             <artifactId>h2</artifactId>
             <scope>runtime</scope>
         </dependency>
+        <!-- PostgreSQL driver -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.flywaydb</groupId>
+            <artifactId>flyway-core</artifactId>
+        </dependency>
+
+        <!-- Query DSL -->
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-apt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.querydsl</groupId>
+            <artifactId>querydsl-core</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.mapstruct</groupId>
             <artifactId>mapstruct</artifactId>
             <version>${org.mapstruct.version}</version>
         </dependency>
-
-        <!-- swagger -->
+        <!--RandomStringUtils-->
         <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger2</artifactId>
-            <version>${swagger.version}</version>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.11</version>
         </dependency>
+        <!-- filtering REST responses -->
         <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger-ui</artifactId>
-            <version>${swagger.version}</version>
+            <groupId>com.github.bohnman</groupId>
+            <artifactId>squiggly-filter-jackson</artifactId>
+            <version>${squiggly.filter.jackson.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
 
@@ -84,7 +148,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.5.1</version>
+                <version>3.8.1</version>
                 <configuration>
                     <source>11</source>
                     <target>11</target>
@@ -97,6 +161,30 @@
                     </annotationProcessorPaths>
                 </configuration>
             </plugin>
+
+            <!--Generate so called Q-types — classes for JPA-->
+            <plugin>
+                <groupId>com.mysema.maven</groupId>
+                <artifactId>apt-maven-plugin</artifactId>
+                <version>${apt-maven-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>process</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>target/generated-sources/java</outputDirectory>
+                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <!--Run migrations during startup-->
+            <plugin>
+                <groupId>org.flywaydb</groupId>
+                <artifactId>flyway-maven-plugin</artifactId>
+                <version>${flyway.version}</version>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/DemoApplication.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/DemoApplication.java
index f27e2306569feea5da18832de5bbc9f883e62d67..0afc205b380aac03d4b9ff747389b9b8e02d5c17 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/DemoApplication.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/DemoApplication.java
@@ -1,12 +1,26 @@
 package cz.muni.ics.kypo.training.adaptive;
 
+import cz.muni.ics.kypo.commons.security.config.ResourceServerSecurityConfig;
+import cz.muni.ics.kypo.commons.startup.config.MicroserviceRegistrationConfiguration;
+import cz.muni.ics.kypo.training.adaptive.config.AdaptiveTrainingWebMvcConfigurer;
+import cz.muni.ics.kypo.training.adaptive.config.ObjectMappersConfiguration;
+import cz.muni.ics.kypo.training.adaptive.config.ValidationMessagesConfig;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Import;
 
 @SpringBootApplication
+@Import(value = {
+        MicroserviceRegistrationConfiguration.class,
+        ValidationMessagesConfig.class,
+        ObjectMappersConfiguration.class,
+        AdaptiveTrainingWebMvcConfigurer.class,
+        ResourceServerSecurityConfig.class
+})
 public class DemoApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(DemoApplication.class, args);
     }
 }
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsAdmin.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsAdmin.java
new file mode 100644
index 0000000000000000000000000000000000000000..fafa117a3a3a2a535f7663deeba70948c36bc7fe
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsAdmin.java
@@ -0,0 +1,17 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsAdmin<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_ADMINISTRATOR<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)")
+public @interface IsAdmin {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrAdmin.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrAdmin.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea2ee69b1dfc23bfa14af15d393988dfd6d52576
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrAdmin.java
@@ -0,0 +1,19 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsDesignerOrAdmin<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_ADMINISTRATOR<strong/>
+ * or <strong>ROLE_TRAINING_DESIGNER<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAnyAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR, " +
+        "T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_DESIGNER)")
+public @interface IsDesignerOrAdmin {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrOrganizerOrAdmin.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrOrganizerOrAdmin.java
new file mode 100644
index 0000000000000000000000000000000000000000..9cae35a32029d09cf6697c3687169f8e2f0a90c1
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsDesignerOrOrganizerOrAdmin.java
@@ -0,0 +1,21 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsDesignerOrOrganizerOrAdmin<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_ADMINISTRATOR<strong/>
+ * or <strong>ROLE_TRAINING_ORGANIZER<strong/> or <strong>ROLE_TRAINING_DESIGNER<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAnyAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR, " +
+        "T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_DESIGNER, " +
+        "T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ORGANIZER)")
+public @interface IsDesignerOrOrganizerOrAdmin {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsOrganizerOrAdmin.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsOrganizerOrAdmin.java
new file mode 100644
index 0000000000000000000000000000000000000000..b51b762635215640236b8569c40c50b8092c2c54
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsOrganizerOrAdmin.java
@@ -0,0 +1,20 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsOrganizerOrAdmin<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_ADMINISTRATOR<strong/>
+ * or <strong>ROLE_TRAINING_ORGANIZER<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAnyAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR, " +
+        "T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ORGANIZER)")
+public @interface IsOrganizerOrAdmin {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTrainee.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTrainee.java
new file mode 100644
index 0000000000000000000000000000000000000000..30e5710b431df9fe05dca69e5d1fe8dd8db58bfa
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTrainee.java
@@ -0,0 +1,17 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsTrainee<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_TRAINEE<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_TRAINEE)")
+public @interface IsTrainee {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTraineeOrAdmin.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTraineeOrAdmin.java
new file mode 100644
index 0000000000000000000000000000000000000000..93103c7a51e4230220d45fc796c46f1d18958259
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/security/IsTraineeOrAdmin.java
@@ -0,0 +1,19 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.security;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The custom annotation <i>@IsTraineeOrAdmin<i/>. All methods annotated with this annotation expect the user has a role <strong>ROLE_TRAINING_ADMINISTRATOR<strong/>
+ * or <strong>ROLE_TRAINING_TRAINEE<strong/>.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasAnyAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_TRAINEE, " +
+        "T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)")
+public @interface IsTraineeOrAdmin {
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/swagger/ApiPageableSwagger.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/swagger/ApiPageableSwagger.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ef2a60195fc126c7a6b0d6e850cf6e314ed9323
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/swagger/ApiPageableSwagger.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.swagger;
+
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * The interface Api pageable swagger.
+ */
+@Target({METHOD, ANNOTATION_TYPE, TYPE})
+@Retention(RUNTIME)
+@ApiImplicitParams({
+        @ApiImplicitParam(name = "page", dataType = "integer", dataTypeClass = Integer.class, paramType = "query", value = "Results page you want to retrieve (0..N)", example = "0"),
+        @ApiImplicitParam(name = "size", dataType = "integer", dataTypeClass = Integer.class, paramType = "query", value = "Number of records per page.", example = "20"),
+        @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", dataTypeClass = String.class, paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
+                + "Default sort order is ascending. " + "Multiple sort criteria are supported.", example = "asc")})
+public @interface ApiPageableSwagger {
+}
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalRO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalRO.java
new file mode 100644
index 0000000000000000000000000000000000000000..c571364afbeab69291a339ebd276ebbc04c49434
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalRO.java
@@ -0,0 +1,56 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.transactions;
+
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.annotation.*;
+
+/**
+ * Extending of the class {@link Transactional} which has <i>read-only</i> set to true.
+ */
+@Transactional(rollbackFor = Exception.class, readOnly = true)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface TransactionalRO {
+
+    /**
+     * Value string.
+     *
+     * @return the string
+     */
+    @AliasFor("transactionManager")
+    String value() default "";
+
+    /**
+     * Transaction manager string.
+     *
+     * @return the string
+     */
+    @AliasFor("value")
+    String transactionManager() default "";
+
+    /**
+     * Propagation propagation.
+     *
+     * @return the propagation
+     */
+    Propagation propagation() default Propagation.REQUIRED;
+
+    /**
+     * Isolation isolation.
+     *
+     * @return the isolation
+     */
+    Isolation isolation() default Isolation.DEFAULT;
+
+    /**
+     * Timeout int.
+     *
+     * @return the int
+     */
+    int timeout() default -1;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalWO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalWO.java
new file mode 100644
index 0000000000000000000000000000000000000000..e163b15a4cd5f304415c83eb8a7f8590b8736f5b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/transactions/TransactionalWO.java
@@ -0,0 +1,56 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.transactions;
+
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.annotation.*;
+
+/**
+ * Extending of the class {@link Transactional} which has <i>read-only</i> set to false.
+ */
+@Transactional(rollbackFor = Exception.class)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+public @interface TransactionalWO {
+
+    /**
+     * Value string.
+     *
+     * @return the string
+     */
+    @AliasFor("transactionManager")
+    String value() default "";
+
+    /**
+     * Transaction manager string.
+     *
+     * @return the string
+     */
+    @AliasFor("value")
+    String transactionManager() default "";
+
+    /**
+     * Propagation propagation.
+     *
+     * @return the propagation
+     */
+    Propagation propagation() default Propagation.REQUIRED;
+
+    /**
+     * Isolation isolation.
+     *
+     * @return the isolation
+     */
+    Isolation isolation() default Isolation.DEFAULT;
+
+    /**
+     * Timeout int.
+     *
+     * @return the int
+     */
+    int timeout() default -1;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireType.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireType.java
new file mode 100644
index 0000000000000000000000000000000000000000..033fef6350283c0b06dd9ea49262cf3c263d0fc5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireType.java
@@ -0,0 +1,25 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({TYPE, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+@Constraint(validatedBy = NotNullQuestionnaireTypeValidator.class)
+@Documented
+public @interface NotNullQuestionnaireType {
+
+    String message() default "Questionnaire type cannot be null.";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireTypeValidator.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireTypeValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..a589416394b283099d311367174dc4b96ba79296
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/annotations/validation/NotNullQuestionnaireTypeValidator.java
@@ -0,0 +1,27 @@
+package cz.muni.ics.kypo.training.adaptive.annotations.validation;
+
+import cz.muni.ics.kypo.training.adaptive.dto.PhaseCreateDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class NotNullQuestionnaireTypeValidator implements ConstraintValidator<NotNullQuestionnaireType, PhaseCreateDTO> {
+
+    @Override
+    public void initialize(NotNullQuestionnaireType constraintAnnotation) {
+    }
+
+    @Override
+    public boolean isValid(PhaseCreateDTO phaseCreateDTO, ConstraintValidatorContext context) {
+        if (phaseCreateDTO == null) {
+            return true;
+        }
+        if (phaseCreateDTO.getPhaseType() == PhaseType.QUESTIONNAIRE && phaseCreateDTO.getQuestionnaireType() == null) {
+            context.disableDefaultConstraintViolation();
+            context.buildConstraintViolationWithTemplate("Field 'questionnaire_type' cannot be null.").addConstraintViolation();
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/AdaptiveTrainingWebMvcConfigurer.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/AdaptiveTrainingWebMvcConfigurer.java
new file mode 100644
index 0000000000000000000000000000000000000000..26fc959747a8e277465d9928db90b0e582cfaae8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/AdaptiveTrainingWebMvcConfigurer.java
@@ -0,0 +1,51 @@
+package cz.muni.ics.kypo.training.adaptive.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * <p>
+ * Supported media types for .yml files -> https://stackoverflow.com/a/38000954/2892314
+ */
+@Configuration
+public class AdaptiveTrainingWebMvcConfigurer implements WebMvcConfigurer {
+
+    private static final MediaType MEDIA_TYPE_YAML = MediaType.valueOf("text/yaml");
+    private static final MediaType MEDIA_TYPE_YML = MediaType.valueOf("text/yml");
+
+    private ObjectMapper objectMapper;
+
+    @Autowired
+    public AdaptiveTrainingWebMvcConfigurer(ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+    }
+
+    @Override
+    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
+        configurer
+                .favorPathExtension(true)
+                .favorParameter(false)
+                .ignoreAcceptHeader(false)
+                .defaultContentType(MediaType.APPLICATION_JSON)
+                .mediaType(MediaType.APPLICATION_JSON.getSubtype(),
+                        MediaType.APPLICATION_JSON)
+                .mediaType(MEDIA_TYPE_YML.getSubtype(), MEDIA_TYPE_YML)
+                .mediaType(MEDIA_TYPE_YAML.getSubtype(), MEDIA_TYPE_YAML);
+    }
+
+    @Override
+    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
+        MappingJackson2HttpMessageConverter yamlConverter =
+                new MappingJackson2HttpMessageConverter(objectMapper);
+        yamlConverter.setSupportedMediaTypes(List.of(MEDIA_TYPE_YAML, MEDIA_TYPE_YML));
+        converters.add(yamlConverter);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/BeanValidationDeserializer.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/BeanValidationDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..5cd104fe606a8360ddb8b75676a516736d1b9f52
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/BeanValidationDeserializer.java
@@ -0,0 +1,39 @@
+package cz.muni.ics.kypo.training.adaptive.config;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.BeanDeserializer;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
+
+import javax.validation.*;
+import java.io.IOException;
+import java.util.Set;
+
+public class BeanValidationDeserializer extends BeanDeserializer {
+
+    private final static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+    private final Validator validator = factory.getValidator();
+
+    public BeanValidationDeserializer(BeanDeserializerBase src) {
+        super(src);
+    }
+
+    @Override
+    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+        Object instance = super.deserialize(p, ctxt);
+        validate(instance);
+        return instance;
+    }
+
+    private void validate(Object instance) {
+        Set<ConstraintViolation<Object>> violations = validator.validate(instance);
+        if (!violations.isEmpty()) {
+            StringBuilder msg = new StringBuilder();
+            msg.append("JSON object is not valid. Reasons (").append(violations.size()).append("): ");
+            for (ConstraintViolation<Object> violation : violations) {
+                msg.append(violation.getMessage()).append(", ");
+            }
+            throw new ConstraintViolationException(msg.toString(), violations);
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ObjectMappersConfiguration.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ObjectMappersConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..c492dfa6808a3b4457060f01489298b109fa152b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ObjectMappersConfiguration.java
@@ -0,0 +1,80 @@
+package cz.muni.ics.kypo.training.adaptive.config;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.BeanDeserializer;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+/**
+ * The type Object mapper config elasticsearch.
+ */
+@Configuration
+public class ObjectMappersConfiguration {
+    /**
+     * General object mapper bean.
+     *
+     * @return the object mapper
+     */
+    @Bean
+    @Primary
+    public ObjectMapper objectMapper() {
+        return initializeBasicObjectMapperConfig();
+    }
+
+    /**
+     * Object mapper bean used in restTemplate calls. Basically, it contains validator settings to validate object in deserialization phase using Bean Validations.
+     *
+     * @return the object mapper
+     */
+    @Bean
+    @Qualifier("webClientObjectMapper")
+    public ObjectMapper webClientObjectMapper() {
+        ObjectMapper objectMapper = initializeBasicObjectMapperConfig();
+        objectMapper.registerModule(getSimpleValidationModule());
+        return objectMapper;
+    }
+
+    /**
+     * Object mapper object mapper.
+     *
+     * @return the object mapper
+     */
+    @Bean("objMapperForElasticsearch")
+    public ObjectMapper elasticsearchObjectMapper() {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        return objectMapper;
+    }
+
+    private ObjectMapper initializeBasicObjectMapperConfig() {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
+        return objectMapper;
+    }
+
+    private SimpleModule getSimpleValidationModule() {
+        SimpleModule validationModule = new SimpleModule();
+        validationModule.setDeserializerModifier(new BeanDeserializerModifier() {
+            @Override
+            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+                if (deserializer instanceof BeanDeserializer) {
+                    return new BeanValidationDeserializer((BeanDeserializer) deserializer);
+                }
+                return deserializer;
+            }
+        });
+        return validationModule;
+    }
+
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/SwaggerConfig.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/SwaggerConfig.java
index c1171b2ec05b168a46de13d88c484baec2496b82..d97ef00b934e5da868d7ba1fcdded076a9c62ec5 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/SwaggerConfig.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/SwaggerConfig.java
@@ -1,37 +1,109 @@
 package cz.muni.ics.kypo.training.adaptive.config;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.service.ApiInfo;
-import springfox.documentation.service.Contact;
+import springfox.documentation.builders.*;
+import springfox.documentation.service.*;
 import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
 import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.SecurityConfiguration;
+import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
-import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 
+/**
+ * Common configuration of Swagger for all projects that import this project.
+ */
 @Configuration
 @EnableSwagger2
 public class SwaggerConfig {
 
+    private static final Logger LOG = LoggerFactory.getLogger(SwaggerConfig.class);
+    private static final String NAME_OF_TOKEN = "bearer";
+    private static final String NAME_OF_SECURITY_SCHEME = "KYPO";
+    @Value("#{'${kypo.idp.4oauth.authorizationURIs}'.split(',')}")
+    private List<String> authorizationURIs;
+    @Value("#{'${kypo.idp.4oauth.client.clientIds}'.split(',')}")
+    private List<String> clientIds;
+    @Value("#{'${kypo.idp.4oauth.scopes}'.split(',')}")
+    private Set<String> scopes;
+    @Value("${swagger.enabled}")
+    private boolean swaggerEnabled;
+
+    /**
+     * The Docket bean is configured to give more control over the API documentation generation process.
+     *
+     * @return the docket
+     */
     @Bean
     public Docket api() {
-        return new Docket(DocumentationType.SWAGGER_2).select()
-            .apis(RequestHandlerSelectors.any())
-            .paths(PathSelectors.any())
-            .build()
-            .apiInfo(apiInfo());
+        LOG.debug("SwaggerConfig -> api()");
+        return new Docket(DocumentationType.SWAGGER_2)
+                .enable(swaggerEnabled)
+                .groupName("public-api")
+                .apiInfo(apiInfo()).useDefaultResponseMessages(false)
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+                .securitySchemes(List.of(securityScheme()))
+                .securityContexts(List.of(securityContext()));
     }
 
     private ApiInfo apiInfo() {
-        return new ApiInfo(
-                "Adaptive training definition",
-                "Swagger documentation of adaptive training definition REST endpoints v1",
-                "v1",
-                "https://docs.crp.kypo.muni.cz/license/",
-                new Contact("CSIRT team", "https://csirt.muni.cz/", "info@kypo.cz"),
-                "License", "https://docs.crp.kypo.muni.cz/license/", Collections.emptyList());
+        LOG.debug("SwaggerConfig -> apiInfo()");
+        return new ApiInfoBuilder()
+                .title("REST API documentation")
+                .description("Developed by CSIRT team")
+                .termsOfServiceUrl("Licensed by CSIRT team")
+                .build();
+    }
+
+    /**
+     * Security configuration.
+     *
+     * @return the security configuration
+     */
+    @Bean
+    public SecurityConfiguration security() {
+        return SecurityConfigurationBuilder.builder()
+                .clientId(clientIds.get(0).trim())
+                .scopeSeparator(" ")
+                .build();
+    }
+
+    private SecurityScheme securityScheme() {
+        GrantType grantType = new ImplicitGrantBuilder()
+                .loginEndpoint(new LoginEndpoint(authorizationURIs.get(0).trim()))
+                .tokenName(NAME_OF_TOKEN)
+                .build();
+
+        return new OAuthBuilder().name(NAME_OF_SECURITY_SCHEME)
+                .grantTypes(List.of(grantType))
+                .scopes(List.of(scopes()))
+                .build();
+    }
+
+    private AuthorizationScope[] scopes() {
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[scopes.size()];
+        int i = 0;
+        for (String scope : scopes) {
+            authorizationScopes[i] = new AuthorizationScope(scope, "");
+            i++;
+        }
+        return authorizationScopes;
+    }
+
+    private SecurityContext securityContext() {
+        return SecurityContext.builder()
+                .securityReferences(List.of(new SecurityReference(NAME_OF_SECURITY_SCHEME, scopes())))
+                .forPaths(PathSelectors.any())
+                .build();
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ValidationMessagesConfig.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ValidationMessagesConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f74ebd47706360ace7ce4337ff4c569865ccda3
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/ValidationMessagesConfig.java
@@ -0,0 +1,66 @@
+package cz.muni.ics.kypo.training.adaptive.config;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Locale;
+
+/**
+ * The type Validation messages config.
+ */
+@Configuration
+public class ValidationMessagesConfig {
+
+    /**
+     * Prints available locales. It is useful to set up appropriate ValidationMessages.properties file name,
+     * e.g. messages_en_US.properties
+     * <p>
+     * en_US
+     * <p>
+     * en -> language; US -> country
+     *
+     * @param args the input arguments
+     */
+    public static void main(String[] args) {
+        Locale[] locales = Locale.getAvailableLocales();
+        Arrays.sort(locales, Comparator.comparing(Locale::toString));
+        for (Locale l : locales) {
+            System.out.println(l.toString());
+        }
+    }
+
+    /**
+     * Message source validation message source.
+     *
+     * @return the message source
+     */
+    @Bean
+    public MessageSource messageSourceValidation() {
+        final ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
+        source.setBasename("classpath:locale/ValidationMessages");
+        source.setUseCodeAsDefaultMessage(true);
+        source.setDefaultEncoding("UTF-8");
+        source.setCacheSeconds(0);
+        return source;
+    }
+
+    /**
+     * Gets validator.
+     *
+     * @return the validator
+     */
+    @Bean
+    @Primary
+    public LocalValidatorFactoryBean getValidator() {
+        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
+        bean.setValidationMessageSource(messageSourceValidation());
+        return bean;
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/config/WebClientConfig.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/WebClientConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7f9352b3db765f35fa5e9e85368d6e49c9f0fee
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/config/WebClientConfig.java
@@ -0,0 +1,167 @@
+package cz.muni.ics.kypo.training.adaptive.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.JavaApiError;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.PythonApiError;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+
+/**
+ * The type Web client config.
+ */
+@Import(ObjectMappersConfiguration.class)
+@Configuration
+public class WebClientConfig {
+
+
+    @Value("${openstack-server.uri}")
+    private String kypoOpenStackURI;
+    @Value("${user-and-group-server.uri}")
+    private String userAndGroupURI;
+    @Value("${elasticsearch-service.uri}")
+    private String elasticsearchServiceURI;
+
+    private ObjectMapper objectMapper;
+
+    @Autowired
+    public WebClientConfig(@Qualifier("webClientObjectMapper") ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Openstack service web client web client.
+     *
+     * @return the web client
+     */
+    @Bean
+    @Qualifier("sandboxServiceWebClient")
+    public WebClient sandboxServiceWebClient() {
+        return WebClient.builder()
+                .baseUrl(kypoOpenStackURI)
+                .defaultHeaders(headers -> {
+                    headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
+                    headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+                })
+                .filters(exchangeFilterFunctions -> {
+                    exchangeFilterFunctions.add(addSecurityHeader());
+                    exchangeFilterFunctions.add(openStackSandboxServiceExceptionHandlingFunction());
+                })
+                .build();
+    }
+
+    /**
+     * User management service web client web client.
+     *
+     * @return the web client
+     */
+    @Bean
+    @Qualifier("userManagementServiceWebClient")
+    public WebClient userManagementServiceWebClient() {
+        return WebClient.builder()
+                .baseUrl(userAndGroupURI)
+                .defaultHeaders(headers -> {
+                    headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
+                    headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+                })
+                .filters(exchangeFilterFunctions -> {
+                    exchangeFilterFunctions.add(addSecurityHeader());
+                    exchangeFilterFunctions.add(javaMicroserviceExceptionHandlingFunction());
+                })
+                .build();
+    }
+
+    /**
+     * Elasticsearch service web client.
+     *
+     * @return the web client
+     */
+    @Bean
+    @Qualifier("elasticsearchServiceWebClient")
+    public WebClient elasticsearchServiceWebClient() {
+        return WebClient.builder()
+                .baseUrl(elasticsearchServiceURI)
+                .defaultHeaders(headers -> {
+                    headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
+                    headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+                })
+                .filters(exchangeFilterFunctions -> {
+                    exchangeFilterFunctions.add(addSecurityHeader());
+                    exchangeFilterFunctions.add(javaMicroserviceExceptionHandlingFunction());
+                })
+                .build();
+    }
+
+    private ExchangeFilterFunction addSecurityHeader() {
+        return (request, next) -> {
+            OAuth2Authentication authenticatedUser = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
+            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authenticatedUser.getDetails();
+            ClientRequest filtered = ClientRequest.from(request)
+                    .header("Authorization", "Bearer " + details.getTokenValue())
+                    .build();
+            return next.exchange(filtered);
+        };
+    }
+
+    private ExchangeFilterFunction openStackSandboxServiceExceptionHandlingFunction() {
+        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
+            if (clientResponse.statusCode().is4xxClientError() || clientResponse.statusCode().is5xxServerError()) {
+                return clientResponse.bodyToMono(String.class)
+                        .flatMap(errorBody -> {
+                            if (errorBody == null || errorBody.isBlank()) {
+                                throw new CustomWebClientException("Error from external microservice. No specific message provided.", clientResponse.statusCode());
+                            }
+                            PythonApiError pythonApiError = null;
+                            try {
+                                pythonApiError = objectMapper.readValue(errorBody, PythonApiError.class);
+                                pythonApiError.setStatus(clientResponse.statusCode());
+                            } catch (IOException e) {
+                                throw new CustomWebClientException("Error from external microservice. No specific message provided.", clientResponse.statusCode());
+                            }
+                            throw new CustomWebClientException(pythonApiError);
+                        });
+            } else {
+                return Mono.just(clientResponse);
+            }
+        });
+    }
+
+    private ExchangeFilterFunction javaMicroserviceExceptionHandlingFunction() {
+        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
+            if (clientResponse.statusCode().is4xxClientError() || clientResponse.statusCode().is5xxServerError()) {
+                return clientResponse.bodyToMono(String.class)
+                        .flatMap(errorBody -> {
+                            if (errorBody == null || errorBody.isBlank()) {
+                                throw new CustomWebClientException("Error from external microservice. No specific message provided.", clientResponse.statusCode());
+                            }
+                            JavaApiError javaApiError = null;
+                            try {
+                                javaApiError = objectMapper.readValue(errorBody, JavaApiError.class);
+                                javaApiError.setStatus(clientResponse.statusCode());
+                            } catch (IOException e) {
+                                throw new CustomWebClientException("Error from external microservice. No specific message provided.", clientResponse.statusCode());
+                            }
+                            throw new CustomWebClientException(javaApiError);
+                        });
+            } else {
+                return Mono.just(clientResponse);
+            }
+        });
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/ExportImportRestController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/ExportImportRestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..81adebf2efbc771b0aea8f777446a29cb7732cda
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/ExportImportRestController.java
@@ -0,0 +1,140 @@
+package cz.muni.ics.kypo.training.adaptive.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.bohnman.squiggly.Squiggly;
+import com.github.bohnman.squiggly.util.SquigglyUtils;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingInstanceArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.FileToReturnDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.training.TrainingDefinitionWithPhasesExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.ImportTrainingDefinitionDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.TrainingDefinitionByIdDTO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiError;
+import cz.muni.ics.kypo.training.adaptive.facade.ExportImportFacade;
+import cz.muni.ics.kypo.training.adaptive.utils.AbstractFileExtensions;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * The controller for export/import.
+ */
+@Api(value = "/", tags = "Export Imports",
+        consumes = MediaType.APPLICATION_JSON_VALUE,
+        authorizations = @Authorization(value = "bearerAuth"))
+@ApiResponses(value = {
+        @ApiResponse(code = 401, message = "Full authentication is required to access this resource.", response = ApiError.class),
+        @ApiResponse(code = 403, message = "The necessary permissions are required for a resource.", response = ApiError.class)
+})
+@RestController
+public class ExportImportRestController {
+
+    private ExportImportFacade exportImportFacade;
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Export import rest controller.
+     *
+     * @param exportImportFacade the export import facade
+     * @param objectMapper       the object mapper
+     */
+    @Autowired
+    public ExportImportRestController(ExportImportFacade exportImportFacade,
+                                      ObjectMapper objectMapper) {
+        this.exportImportFacade = exportImportFacade;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Exports training definition and phase.
+     *
+     * @param trainingDefinitionId the training definition id
+     * @return Exported training definition and phase.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get exported training definitions and phase.",
+            response = TrainingDefinitionWithPhasesExportDTO.class,
+            nickname = "getExportedTrainingDefinitionAndPhases",
+            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "Training definitions and phase found and exported.", response = TrainingDefinitionWithPhasesExportDTO.class),
+            @ApiResponse(code = 404, message = "Training definition not found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/exports/training-definitions/{definitionId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public ResponseEntity<byte[]> getExportedTrainingDefinitionAndPhases(
+            @ApiParam(value = "Id of training definition", required = true)
+            @PathVariable("definitionId") Long trainingDefinitionId) {
+        FileToReturnDTO file = exportImportFacade.dbExport(trainingDefinitionId);
+        HttpHeaders header = new HttpHeaders();
+        header.setContentType(new MediaType("application", "octet-stream"));
+        header.set("Content-Disposition", "inline; filename=" + file.getTitle() + AbstractFileExtensions.JSON_FILE_EXTENSION);
+        header.setContentLength(file.getContent().length);
+        return new ResponseEntity<>(file.getContent(), header, HttpStatus.OK);
+    }
+
+    /**
+     * Import training definition response entity.
+     *
+     * @param importTrainingDefinitionDTO the training definition to be imported
+     * @param fields                      attributes of the object to be returned as the result.
+     * @return the new imported definition
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Import training definition with phase.",
+            response = TrainingDefinitionByIdDTO.class,
+            nickname = "importTrainingDefinition",
+            produces = MediaType.APPLICATION_JSON_VALUE,
+            consumes = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "Training definition imported.", response = TrainingDefinitionByIdDTO.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PostMapping(path = "/imports/training-definitions", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> importTrainingDefinition(
+            @ApiParam(value = "Training definition to be imported", required = true)
+            @Valid @RequestBody ImportTrainingDefinitionDTO importTrainingDefinitionDTO,
+            @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+            @RequestParam(value = "fields", required = false) String fields) {
+        TrainingDefinitionByIdDTO trainingDefinitionResource = exportImportFacade.dbImport(importTrainingDefinitionDTO);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingDefinitionResource));
+    }
+
+    /**
+     * Archive training instance.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return file containing wanted training instance
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Archive training instance",
+            response = TrainingInstanceArchiveDTO.class,
+            nickname = "archiveTrainingInstance",
+            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "Training instance archived.", response = TrainingInstanceArchiveDTO.class),
+            @ApiResponse(code = 404, message = "Training instance not found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot archive instance that is not finished.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/exports/training-instances/{instanceId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
+    public ResponseEntity<byte[]> archiveTrainingInstance(
+            @ApiParam(value = "Id of training instance", required = true)
+            @PathVariable("instanceId") Long trainingInstanceId) {
+        FileToReturnDTO file = exportImportFacade.archiveTrainingInstance(trainingInstanceId);
+        HttpHeaders header = new HttpHeaders();
+        header.setContentType(new MediaType("application", "octet-stream"));
+        header.set("Content-Disposition", "inline; filename=" + file.getTitle() + AbstractFileExtensions.ZIP_FILE_EXTENSION);
+        header.setContentLength(file.getContent().length);
+        return new ResponseEntity<>(file.getContent(), header, HttpStatus.OK);
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/PhasesController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/PhasesController.java
index 1bfe0ccd2c71a5a1e58ce7e7e677f98eee32c0b9..ab9fe85604f607d55d751559e5e7644f8a55e857 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/PhasesController.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/PhasesController.java
@@ -8,27 +8,13 @@ import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDT
 import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.facade.TrainingPhaseFacade;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.Authorization;
+import cz.muni.ics.kypo.training.adaptive.facade.PhaseFacade;
+import io.swagger.annotations.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 import java.util.List;
@@ -37,17 +23,17 @@ import java.util.List;
 @RequestMapping(value = "/training-definitions/{definitionId}/phases", produces = MediaType.APPLICATION_JSON_VALUE)
 @CrossOrigin(origins = "*", allowCredentials = "true", allowedHeaders = "*",
         methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE, RequestMethod.PUT})
-@Api(value = "/training-definitions/{definitionId}/phases",
+@Api(value = "/training-definitions/{definitionId}/phase",
         tags = "Phases",
         consumes = MediaType.APPLICATION_JSON_VALUE,
         authorizations = @Authorization(value = "bearerAuth"))
 public class PhasesController {
 
-    private final TrainingPhaseFacade trainingPhaseFacade;
+    private final PhaseFacade phaseFacade;
 
     @Autowired
-    public PhasesController(TrainingPhaseFacade trainingPhaseFacade) {
-        this.trainingPhaseFacade = trainingPhaseFacade;
+    public PhasesController(PhaseFacade phaseFacade) {
+        this.phaseFacade = phaseFacade;
     }
 
     @ApiOperation(httpMethod = "POST",
@@ -67,13 +53,13 @@ public class PhasesController {
             @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase type", allowableValues = "questionnaire, info, game", required = true)
             @RequestBody @Valid PhaseCreateDTO phaseCreateDTO) {
-        AbstractPhaseDTO createdPhase = trainingPhaseFacade.createPhase(definitionId, phaseCreateDTO);
+        AbstractPhaseDTO createdPhase = phaseFacade.createPhase(definitionId, phaseCreateDTO);
         return new ResponseEntity<>(createdPhase, HttpStatus.CREATED);
     }
 
     @ApiOperation(httpMethod = "GET",
-            value = "Get all phases",
-            notes = "Get all phases associated with specified training definition",
+            value = "Get all phase",
+            notes = "Get all phase associated with specified training definition",
             response = Object.class,
             nickname = "getPhases",
             produces = MediaType.APPLICATION_JSON_VALUE
@@ -86,7 +72,7 @@ public class PhasesController {
     public ResponseEntity<List<AbstractPhaseDTO>> getPhases(
             @ApiParam(value = "Training definition ID", required = true)
             @PathVariable(name = "definitionId") Long definitionId) {
-        List<AbstractPhaseDTO> phases = trainingPhaseFacade.getPhases(definitionId);
+        List<AbstractPhaseDTO> phases = phaseFacade.getPhases(definitionId);
         return new ResponseEntity<>(phases, HttpStatus.OK);
     }
 
@@ -102,11 +88,9 @@ public class PhasesController {
     })
     @GetMapping(path = "/{phaseId}")
     public ResponseEntity<AbstractPhaseDTO> getPhase(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase ID", required = true)
             @PathVariable("phaseId") Long phaseId) {
-        AbstractPhaseDTO phase = trainingPhaseFacade.getPhase(definitionId, phaseId);
+        AbstractPhaseDTO phase = phaseFacade.getPhase(phaseId);
         return new ResponseEntity<>(phase, HttpStatus.OK);
     }
 
@@ -122,11 +106,9 @@ public class PhasesController {
     })
     @DeleteMapping(path = "/{phaseId}")
     public ResponseEntity<List<AbstractPhaseDTO>> removePhase(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase ID", required = true)
             @PathVariable("phaseId") Long phaseId) {
-        List<AbstractPhaseDTO> remainingPhases = trainingPhaseFacade.deletePhase(definitionId, phaseId);
+        List<AbstractPhaseDTO> remainingPhases = phaseFacade.deletePhase(phaseId);
         return new ResponseEntity<>(remainingPhases, HttpStatus.OK);
     }
 
@@ -142,13 +124,11 @@ public class PhasesController {
     })
     @PutMapping(path = "/{phaseId}/info")
     public ResponseEntity<InfoPhaseDTO> updateInfoPhase(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase ID", required = true)
             @PathVariable("phaseId") Long phaseId,
             @ApiParam(value = "Info phase to be updated")
             @RequestBody @Valid InfoPhaseUpdateDTO infoPhaseUpdateDto) {
-        InfoPhaseDTO updatedInfoPhase = trainingPhaseFacade.updateInfoPhase(definitionId, phaseId, infoPhaseUpdateDto);
+        InfoPhaseDTO updatedInfoPhase = phaseFacade.updateInfoPhase(phaseId, infoPhaseUpdateDto);
         return new ResponseEntity<>(updatedInfoPhase, HttpStatus.OK);
     }
 
@@ -164,13 +144,11 @@ public class PhasesController {
     })
     @PutMapping(path = "/{phaseId}/training")
     public ResponseEntity<TrainingPhaseDTO> updateTrainingPhase(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase ID", required = true)
             @PathVariable("phaseId") Long phaseId,
             @ApiParam(value = "Training phase to be updated")
             @RequestBody @Valid TrainingPhaseUpdateDTO trainingPhaseUpdateDto) {
-        TrainingPhaseDTO updatedTrainingPhase = trainingPhaseFacade.updateTrainingPhase(definitionId, phaseId, trainingPhaseUpdateDto);
+        TrainingPhaseDTO updatedTrainingPhase = phaseFacade.updateTrainingPhase(phaseId, trainingPhaseUpdateDto);
         return new ResponseEntity<>(updatedTrainingPhase, HttpStatus.OK);
     }
 
@@ -185,13 +163,11 @@ public class PhasesController {
     })
     @PutMapping(path = "/{phaseId}/questionnaire")
     public ResponseEntity<QuestionnairePhaseDTO> updateQuestionnairePhase(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Phase ID", required = true)
             @PathVariable("phaseId") Long phaseId,
             @ApiParam(value = "Questionnaire to be updated")
             @RequestBody @Valid QuestionnaireUpdateDTO questionnaireUpdateDto) {
-        QuestionnairePhaseDTO updatedQuestionnairePhase = trainingPhaseFacade.updateQuestionnairePhase(definitionId, phaseId, questionnaireUpdateDto);
+        QuestionnairePhaseDTO updatedQuestionnairePhase = phaseFacade.updateQuestionnairePhase(phaseId, questionnaireUpdateDto);
         return new ResponseEntity<>(updatedQuestionnairePhase, HttpStatus.OK);
     }
 
@@ -206,9 +182,13 @@ public class PhasesController {
     })
     @PutMapping(value = "/{phaseIdFrom}/move-to/{newPosition}", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<Void> movePhaseToSpecifiedOrder(
-            @ApiParam(value = "Phase ID - from", required = true) @PathVariable(name = "phaseIdFrom") Long phaseIdFrom,
-            @ApiParam(value = "Position (order) to which the phase should be moved", required = true) @PathVariable(name = "newPosition") int newPosition) {
-        trainingPhaseFacade.movePhaseToSpecifiedOrder(phaseIdFrom, newPosition);
+            @ApiParam(value = "Training definition ID", required = true)
+            @PathVariable(name = "definitionId") Long definitionId,
+            @ApiParam(value = "Phase ID - from", required = true)
+            @PathVariable(name = "phaseIdFrom") Long phaseIdFrom,
+            @ApiParam(value = "Position (order) to which the phase should be moved", required = true)
+            @PathVariable(name = "newPosition") int newPosition) {
+        phaseFacade.movePhaseToSpecifiedOrder(phaseIdFrom, newPosition);
         return ResponseEntity.ok().build();
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TasksController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TasksController.java
index 248854552e18f436e64205a1920b7f57711d1741..d3ad7e447b38fff389d841e1a5fc36a9006f9d17 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TasksController.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TasksController.java
@@ -3,27 +3,13 @@ package cz.muni.ics.kypo.training.adaptive.controller;
 import cz.muni.ics.kypo.training.adaptive.dto.training.TaskCopyDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.training.TaskUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.service.TaskService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.Authorization;
+import cz.muni.ics.kypo.training.adaptive.facade.TaskFacade;
+import io.swagger.annotations.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
 
@@ -37,11 +23,11 @@ import javax.validation.Valid;
         authorizations = @Authorization(value = "bearerAuth"))
 public class TasksController {
 
-    private final TaskService taskService;
+    private final TaskFacade taskFacade;
 
     @Autowired
-    public TasksController(TaskService taskService) {
-        this.taskService = taskService;
+    public TasksController(TaskFacade taskFacade) {
+        this.taskFacade = taskFacade;
     }
 
     @ApiOperation(httpMethod = "POST",
@@ -57,11 +43,9 @@ public class TasksController {
     })
     @PostMapping
     public ResponseEntity<TaskDTO> createTask(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
             @ApiParam(value = "Training phase ID", required = true)
             @PathVariable(name = "phaseId") Long phaseId) {
-        TaskDTO createdTask = taskService.createDefaultTask(definitionId, phaseId);
+        TaskDTO createdTask = taskFacade.createDefaultTask(phaseId);
         return new ResponseEntity<>(createdTask, HttpStatus.CREATED);
     }
 
@@ -78,14 +62,12 @@ public class TasksController {
     })
     @PostMapping(path = "/{taskId}")
     public ResponseEntity<TaskDTO> cloneTask(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
-            @ApiParam(value = "Training phase ID", required = true)
-            @PathVariable(name = "phaseId") Long phaseId,
             @ApiParam(value = "Task ID", required = true)
             @PathVariable(name = "taskId") Long taskId,
+            @ApiParam(value = "Training phase ID", required = true)
+            @PathVariable(name = "phaseId") Long phaseId,
             @RequestBody @Valid TaskCopyDTO taskCopyDTO) {
-        TaskDTO createdTask = taskService.cloneTask(definitionId, phaseId, taskId, taskCopyDTO);
+        TaskDTO createdTask = taskFacade.createTask(phaseId, taskCopyDTO);
         return new ResponseEntity<>(createdTask, HttpStatus.CREATED);
     }
 
@@ -102,13 +84,9 @@ public class TasksController {
     })
     @GetMapping(path = "/{taskId}")
     public ResponseEntity<TaskDTO> getTask(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
-            @ApiParam(value = "Training phase ID", required = true)
-            @PathVariable(name = "phaseId") Long phaseId,
             @ApiParam(value = "Task ID", required = true)
             @PathVariable(name = "taskId") Long taskId) {
-        TaskDTO createdTask = taskService.getTask(definitionId, phaseId, taskId);
+        TaskDTO createdTask = taskFacade.getTask(taskId);
         return new ResponseEntity<>(createdTask, HttpStatus.CREATED);
     }
 
@@ -124,15 +102,11 @@ public class TasksController {
     })
     @PutMapping(path = "/{taskId}")
     public ResponseEntity<TaskDTO> updateTask(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
-            @ApiParam(value = "Training phase ID", required = true)
-            @PathVariable(name = "phaseId") Long phaseId,
             @ApiParam(value = "Task ID", required = true)
             @PathVariable(name = "taskId") Long taskId,
             @ApiParam(value = "Task to be updated")
             @RequestBody @Valid TaskUpdateDTO taskUpdateDto) {
-        TaskDTO updatedTask = taskService.updateTask(definitionId, phaseId, taskId, taskUpdateDto);
+        TaskDTO updatedTask = taskFacade.updateTask(taskId, taskUpdateDto);
         return new ResponseEntity<>(updatedTask, HttpStatus.OK);
     }
 
@@ -149,13 +123,9 @@ public class TasksController {
     })
     @DeleteMapping(path = "/{taskId}")
     public ResponseEntity<Void> removeTask(
-            @ApiParam(value = "Training definition ID", required = true)
-            @PathVariable(name = "definitionId") Long definitionId,
-            @ApiParam(value = "Training phase ID", required = true)
-            @PathVariable(name = "phaseId") Long phaseId,
             @ApiParam(value = "Task ID", required = true)
             @PathVariable(name = "taskId") Long taskId) {
-        taskService.removeTask(definitionId, phaseId, taskId);
+        taskFacade.removeTask(taskId);
         return ResponseEntity.ok().build();
     }
 
@@ -172,7 +142,7 @@ public class TasksController {
     public ResponseEntity<Void> moveTaskToSpecifiedOrder(
             @ApiParam(value = "Task ID - from", required = true) @PathVariable(name = "taskIdFrom") Long taskIdFrom,
             @ApiParam(value = "Position (order) to which the task should be moved", required = true) @PathVariable(name = "newPosition") int newPosition) {
-        taskService.moveTaskToSpecifiedOrder(taskIdFrom, newPosition);
+        taskFacade.moveTaskToSpecifiedOrder(taskIdFrom, newPosition);
         return ResponseEntity.ok().build();
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingDefinitionsRestController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingDefinitionsRestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd08ef5db53cc35d7631d2f4b7fd6638a56700ea
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingDefinitionsRestController.java
@@ -0,0 +1,472 @@
+package cz.muni.ics.kypo.training.adaptive.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.bohnman.squiggly.Squiggly;
+import com.github.bohnman.squiggly.util.SquigglyUtils;
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.commons.security.mapping.UserInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.annotations.swagger.ApiPageableSwagger;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.*;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleType;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiError;
+import cz.muni.ics.kypo.training.adaptive.facade.TrainingDefinitionFacade;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.querydsl.binding.QuerydslPredicate;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The rest controller for Training definitions.
+ */
+@Api(value = "/training-definitions",
+        tags = "Training definitions",
+        consumes = MediaType.APPLICATION_JSON_VALUE,
+        authorizations = @Authorization(value = "bearerAuth"))
+@ApiResponses(value = {
+        @ApiResponse(code = 401, message = "Full authentication is required to access this resource.", response = ApiError.class),
+        @ApiResponse(code = 403, message = "The necessary permissions are required for a resource.", response = ApiError.class)
+})
+@RestController
+@RequestMapping(path = "/training-definitions", produces = MediaType.APPLICATION_JSON_VALUE)
+public class TrainingDefinitionsRestController {
+
+    private TrainingDefinitionFacade trainingDefinitionFacade;
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Training Definitions rest controller.
+     *
+     * @param trainingDefinitionFacade the training definition facade
+     * @param objectMapper             the object mapper
+     */
+    @Autowired
+    public TrainingDefinitionsRestController(TrainingDefinitionFacade trainingDefinitionFacade,
+                                             ObjectMapper objectMapper) {
+        this.trainingDefinitionFacade = trainingDefinitionFacade;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Get requested Training Definition by id.
+     *
+     * @param id     of Training Definition to return.
+     * @param fields attributes of the object to be returned as the result.
+     * @return Requested Training Definition by id.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get Training Definition by Id.",
+            response = TrainingDefinitionByIdDTO.class,
+            nickname = "findTrainingDefinitionById",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The Training definition has been found.", response = TrainingDefinitionByIdDTO.class),
+            @ApiResponse(code = 404, message = "The Training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+
+    })
+    @GetMapping(path = "/{definitionId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findTrainingDefinitionById(@ApiParam(value = "ID of training definition to be retrieved.", required = true)
+                                                             @PathVariable(value = "definitionId") Long id,
+                                                             @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                             @RequestParam(value = "fields", required = false) String fields) {
+        TrainingDefinitionByIdDTO trainingDefinitionResource = trainingDefinitionFacade.findById(id);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingDefinitionResource));
+    }
+
+    /**
+     * Get all Training Definitions.
+     *
+     * @param predicate specifies query to database.
+     * @param pageable  pageable parameter with information about pagination.
+     * @param fields    attributes of the object to be returned as the result.
+     * @return all Training Definitions.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all Training Definitions.",
+            response = TrainingDefinitionRestResource.class,
+            nickname = "findAllTrainingDefinitions",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The requested resources have been found.", response = TrainingDefinitionByIdDTO.class, responseContainer = "List"),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findAllTrainingDefinitions(
+            @QuerydslPredicate(root = TrainingDefinition.class) Predicate predicate,
+            Pageable pageable,
+            @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+            @RequestParam(value = "fields", required = false) String fields) {
+
+        PageResultResource<TrainingDefinitionDTO> trainingDefinitionResource = trainingDefinitionFacade.findAll(predicate, pageable);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingDefinitionResource));
+    }
+
+    /**
+     * Get all Training Definitions for organizers.
+     *
+     * @param state    training definition state (should be RELEASED or UNRELEASED)
+     * @param pageable pageable parameter with information about pagination.
+     * @param fields   attributes of the object to be returned as the result.
+     * @return all Training Definitions for organizers.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all Training Definitions for organizers.",
+            response = TrainingDefinitionRestResource.class,
+            nickname = "findAllTrainingDefinitionsForOrganizers",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The Training definitions have been found.", response = TrainingDefinitionInfoDTO.class, responseContainer = "List"),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/for-organizers", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findAllTrainingDefinitionsForOrganizers(
+            @ApiParam(value = "State of the training definition", required = true)
+            @RequestParam(value = "state") TDState state,
+            Pageable pageable,
+            @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+            @RequestParam(value = "fields", required = false) String fields) {
+
+        PageResultResource<TrainingDefinitionInfoDTO> trainingDefinitionResource = trainingDefinitionFacade.findAllForOrganizers(state, pageable);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingDefinitionResource));
+    }
+
+    /**
+     * Create Training Definition.
+     *
+     * @param trainingDefinitionCreateDTO the Training Definition to be create
+     * @param fields                      attributes of the object to be returned as the result.
+     * @return the new Training Definition
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Create Training Definition",
+            response = TrainingDefinitionByIdDTO.class,
+            nickname = "createTrainingDefinition",
+            produces = MediaType.APPLICATION_JSON_VALUE,
+            consumes = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The Training definition has been created.", response = TrainingDefinitionByIdDTO.class),
+            @ApiResponse(code = 400, message = "The provided training definition is not valid", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> createTrainingDefinition(@ApiParam(value = "Training Definition to be created")
+                                                           @RequestBody @Valid TrainingDefinitionCreateDTO trainingDefinitionCreateDTO,
+                                                           @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                           @RequestParam(value = "fields", required = false) String fields) {
+        TrainingDefinitionByIdDTO trainingDefinitionResource = trainingDefinitionFacade.create(trainingDefinitionCreateDTO);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingDefinitionResource));
+    }
+
+    /**
+     * Update Training Definition.
+     *
+     * @param trainingDefinitionUpdateDTO the training definition to be updated
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Update Training Definition",
+            notes = "Only unreleased training definition can be updated",
+            nickname = "updateTrainingDefinition",
+            consumes = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training definition has been updated."),
+            @ApiResponse(code = 400, message = "The provided training definition is not valid", response = ApiError.class),
+            @ApiResponse(code = 404, message = "The training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot edit released or archived training definition.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> updateTrainingDefinition(
+            @ApiParam(value = "Training definition to be updated")
+            @RequestBody @Valid TrainingDefinitionUpdateDTO trainingDefinitionUpdateDTO) {
+        trainingDefinitionFacade.update(trainingDefinitionUpdateDTO);
+        return ResponseEntity.noContent().build();
+    }
+
+    /**
+     * Delete Training Definition.
+     *
+     * @param id the id of definition to be deleted
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "DELETE",
+            value = "Delete training definition",
+            notes = "Released training definition cannot be deleted",
+            nickname = "deleteTrainingDefinition"
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The Training definition has been deleted."),
+            @ApiResponse(code = 404, message = "The Training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot delete released training definition.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @DeleteMapping(path = "/{definitionId}")
+    public ResponseEntity<Void> deleteTrainingDefinition(@ApiParam(value = "Id of training definition to be deleted", required = true)
+                                                         @PathVariable("definitionId") Long id) {
+        trainingDefinitionFacade.delete(id);
+        return ResponseEntity.ok().build();
+    }
+
+    /**
+     * Get requested designers.
+     *
+     * @param givenName  the given name
+     * @param familyName the family name
+     * @param pageable   pageable parameter with information about pagination.
+     * @return List of users login and full name with role designer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get designers.",
+            response = UserInfoRestResource.class,
+            nickname = "getDesigners",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The designers have been found.", response = UserInfoRestResource.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/designers", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getDesigners(@ApiParam(value = "Given name filter.", required = false)
+                                               @RequestParam(value = "givenName", required = false) String givenName,
+                                               @ApiParam(value = "Family name filter.", required = false)
+                                               @RequestParam(value = "familyName", required = false) String familyName,
+                                               Pageable pageable) {
+        PageResultResource<UserRefDTO> designers = trainingDefinitionFacade.getUsersWithGivenRole(RoleType.ROLE_TRAINING_DESIGNER, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, designers));
+    }
+
+    /**
+     * Get requested organizers.
+     *
+     * @param givenName  the given name
+     * @param familyName the family name
+     * @param pageable   pageable parameter with information about pagination.
+     * @return List of users login and full name with role designer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get organizers.",
+            response = UserInfoRestResource.class,
+            nickname = "getOrganizers",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The organizers have been found.", response = UserInfoRestResource.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/organizers", produces = MediaType.APPLICATION_JSON_VALUE)
+
+    public ResponseEntity<Object> getOrganizers(@ApiParam(value = "Given name filter.", required = false)
+                                                @RequestParam(value = "givenName", required = false) String givenName,
+                                                @ApiParam(value = "Family name filter.", required = false)
+                                                @RequestParam(value = "familyName", required = false) String familyName,
+                                                @ApiParam(value = "Pagination support.", required = false) Pageable pageable) {
+        PageResultResource<UserRefDTO> organizers = trainingDefinitionFacade.getUsersWithGivenRole(RoleType.ROLE_TRAINING_ORGANIZER, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, organizers));
+    }
+
+    /**
+     * Get requested designers not in given Training Definition.
+     *
+     * @param trainingDefinitionId id of the training definition
+     * @param givenName            the given name
+     * @param familyName           the family name
+     * @param pageable             pageable parameter with information about pagination.
+     * @return List of users login and full name with role designer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get designers not in given training definition.",
+            response = UserInfoRestResource.class,
+            nickname = "findDesignersNotInGivenTrainingDefinition",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The designers have been found.", response = UserInfoRestResource.class),
+            @ApiResponse(code = 404, message = "The training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "{definitionId}/designers-not-in-training-definition", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getDesignersNotInGivenTrainingDefinition(@ApiParam(value = "ID of the training definition which do not contains authors you want to retrieve.", required = true)
+                                                                           @PathVariable("definitionId") Long trainingDefinitionId,
+                                                                           @ApiParam(value = "Given name filter.", required = false)
+                                                                           @RequestParam(value = "givenName", required = false) String givenName,
+                                                                           @ApiParam(value = "Family name filter.", required = false)
+                                                                           @RequestParam(value = "familyName", required = false) String familyName,
+                                                                           Pageable pageable) {
+        PageResultResource<UserRefDTO> designers = trainingDefinitionFacade.getDesignersNotInGivenTrainingDefinition(trainingDefinitionId, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, designers));
+    }
+
+    /**
+     * Get requested authors for Training Definition.
+     *
+     * @param trainingDefinitionId id of training definition for which to retrieve authors
+     * @param givenName            the given name
+     * @param familyName           the family name
+     * @param pageable             pageable parameter with information about pagination.
+     * @return List of users login and full name with role designer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get authors.",
+            response = UserInfoRestResource.class,
+            nickname = "getAuthors",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The authors have been found.", response = UserInfoRestResource.class),
+            @ApiResponse(code = 404, message = "The training definition has not been found", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/{definitionId}/authors", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getAuthors(@ApiParam(value = "ID of the training definition which contains authors you want to retrieve.", required = true)
+                                             @PathVariable("definitionId") Long trainingDefinitionId,
+                                             @ApiParam(value = "Given name filter.", required = false)
+                                             @RequestParam(value = "givenName", required = false) String givenName,
+                                             @ApiParam(value = "Family name filter.", required = false)
+                                             @RequestParam(value = "familyName", required = false) String familyName,
+                                             @ApiParam(value = "Pagination support.", required = false) Pageable pageable) {
+        PageResultResource<UserRefDTO> designers = trainingDefinitionFacade.getAuthors(trainingDefinitionId, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, designers));
+    }
+
+    /**
+     * Concurrently add/remove authors with given ids to/from the Training Definition.
+     *
+     * @param trainingDefinitionId id of training definition for which to retrieve authors
+     * @param authorsAddition      ids of the authors to be added to the training definition.
+     * @param authorsRemoval       ids of the authors to be removed from the training definition.
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Edit authors.",
+            response = UserInfoRestResource.class,
+            nickname = "editAuthors",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The authors have been updated."),
+            @ApiResponse(code = 404, message = "The training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @PutMapping(path = "/{definitionId}/authors", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> editAuthors(@ApiParam(value = "ID of training definition to be updated.", required = true)
+                                            @PathVariable("definitionId") Long trainingDefinitionId,
+                                            @ApiParam(value = "Ids of the users to be added to the training definition.")
+                                            @RequestParam(value = "authorsAddition", required = false) Set<Long> authorsAddition,
+                                            @ApiParam(value = "Ids of the users to be removed from the training definition.")
+                                            @RequestParam(value = "authorsRemoval", required = false) Set<Long> authorsRemoval) {
+        trainingDefinitionFacade.editAuthors(trainingDefinitionId, authorsAddition, authorsRemoval);
+        return ResponseEntity.noContent().build();
+    }
+
+    /**
+     * Clone Training Definition response entity.
+     *
+     * @param id    the id of cloned Training Definition
+     * @param title the title of new Training Definition
+     * @return the new Training Definition
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Clone training definition",
+            notes = "Only released and archived training definitions can be cloned",
+            response = TrainingDefinitionByIdDTO.class,
+            nickname = "cloneTrainingDefinition",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The Training definition has been cloned.", response = TrainingDefinitionByIdDTO.class),
+            @ApiResponse(code = 404, message = "The Training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PostMapping(path = "/{definitionId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<TrainingDefinitionByIdDTO> cloneTrainingDefinition(@ApiParam(value = "Id of training definition to be cloned", required = true)
+                                                                             @PathVariable("definitionId") Long id,
+                                                                             @ApiParam(value = "Title of cloned definition", required = true)
+                                                                             @RequestParam(value = "title") String title) {
+        TrainingDefinitionByIdDTO trainingDefinitionByIdDTO = trainingDefinitionFacade.clone(id, title);
+        return ResponseEntity.ok(trainingDefinitionByIdDTO);
+    }
+
+    /**
+     * Switch development state of given definition.
+     *
+     * @param definitionId the definition id
+     * @param state        the new development state
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Switch state of training definition",
+            nickname = "switchDefinitionState")
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training definition has been updated."),
+            @ApiResponse(code = 404, message = "The training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot edit definition with created instances.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PutMapping(path = "/{definitionId}/states/{state}")
+    public ResponseEntity<Void> switchState(
+            @ApiParam(value = "Id of definition", required = true)
+            @PathVariable("definitionId") Long definitionId,
+            @ApiParam(value = "New state of definition", allowableValues = "RELEASED, UNRELEASED, ARCHIVED", required = true)
+            @PathVariable("state") TDState state) {
+        trainingDefinitionFacade.switchState(definitionId, state);
+        return ResponseEntity.noContent().build();
+    }
+
+    @ApiModel(description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+    private static class TrainingDefinitionRestResource extends PageResultResource<TrainingDefinitionByIdDTO> {
+
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Retrieved Training Definitions from databases.")
+        private List<TrainingDefinitionByIdDTO> content;
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+        private Pagination pagination;
+
+    }
+
+    /**
+     * The type User info rest resource.
+     */
+    @ApiModel(value = "UserInfoRestResource",
+            description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+    public static class UserInfoRestResource extends PageResultResource<UserInfoDTO> {
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Retrieved Training Instances from databases.")
+        private List<UserRefDTO> content;
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+        private Pagination pagination;
+    }
+
+}
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingInstancesRestController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingInstancesRestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..55d4cb064e60de94c884e1ceaa290b812f03ddc0
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingInstancesRestController.java
@@ -0,0 +1,395 @@
+package cz.muni.ics.kypo.training.adaptive.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.bohnman.squiggly.Squiggly;
+import com.github.bohnman.squiggly.util.SquigglyUtils;
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.swagger.ApiPageableSwagger;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.traininginstance.*;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiError;
+import cz.muni.ics.kypo.training.adaptive.facade.TrainingInstanceFacade;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.querydsl.binding.QuerydslPredicate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The rest controller for Training instances.
+ */
+@Api(value = "/training-instances",
+        tags = "Training instances",
+        consumes = MediaType.APPLICATION_JSON_VALUE,
+        authorizations = @Authorization(value = "bearerAuth"))
+@ApiResponses(value = {
+        @ApiResponse(code = 401, message = "Full authentication is required to access this resource.", response = ApiError.class),
+        @ApiResponse(code = 403, message = "The necessary permissions are required for a resource.", response = ApiError.class)
+})
+@RestController
+@RequestMapping(path = "/training-instances", produces = MediaType.APPLICATION_JSON_VALUE)
+public class TrainingInstancesRestController {
+
+    private TrainingInstanceFacade trainingInstanceFacade;
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Training instances rest controller.
+     *
+     * @param trainingInstanceFacade the training instance facade
+     * @param objectMapper           the object mapper
+     */
+    @Autowired
+    public TrainingInstancesRestController(TrainingInstanceFacade trainingInstanceFacade,
+                                           ObjectMapper objectMapper) {
+        this.trainingInstanceFacade = trainingInstanceFacade;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Get requested Training Instance by id.
+     *
+     * @param id     id of the Training Instance to return.
+     * @param fields attributes of the object to be returned as the result.
+     * @return Requested Training Instance by id.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get training instance by id.",
+            response = TrainingInstanceDTO.class,
+            nickname = "findTrainingInstanceById",
+            notes = "Returns training instance by id and also contains particular training definition in it.",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instance has been found", response = TrainingInstanceDTO.class),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{instanceId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findTrainingInstanceById(
+            @ApiParam(value = "Training instance ID", required = true)
+            @PathVariable("instanceId") Long id,
+            @ApiParam(value = "Fields which should be returned in REST API response")
+            @RequestParam(value = "fields", required = false) String fields) {
+        TrainingInstanceDTO trainingInstanceResource = trainingInstanceFacade.findById(id);
+        return ResponseEntity.ok(trainingInstanceResource);
+    }
+
+    /**
+     * Get all Training Instances.
+     *
+     * @param predicate specifies query to database.
+     * @param pageable  pageable parameter with information about pagination.
+     * @param fields    attributes of the object to be returned as the result.
+     * @return all Training Instances.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all training instances.",
+            response = TrainingInstanceRestResource.class,
+            nickname = "findAllTrainingInstances",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instances have been found.", response = TrainingInstanceRestResource.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findAllTrainingInstances(@QuerydslPredicate(root = TrainingInstance.class) Predicate predicate,
+                                                           @ApiParam(value = "Pagination support.", required = false)
+                                                                   Pageable pageable,
+                                                           @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                           @RequestParam(value = "fields", required = false) String fields) {
+        PageResultResource<TrainingInstanceFindAllResponseDTO> trainingInstanceResource = trainingInstanceFacade.findAll(predicate, pageable);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingInstanceResource));
+    }
+
+    /**
+     * Create new Training Instance.
+     *
+     * @param trainingInstanceCreateDTO the Training Instance to be created
+     * @param fields                    attributes of the object to be returned as the result.
+     * @return the newly created instance
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Create training instance",
+            notes = "This can only be done by the organizer or administrator",
+            response = TrainingInstanceDTO.class,
+            nickname = "createTrainingInstance",
+            produces = MediaType.APPLICATION_JSON_VALUE,
+            consumes = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instance has been created.", response = TrainingInstanceDTO.class),
+            @ApiResponse(code = 400, message = "The provided training instance is not valid.", response = ApiError.class),
+            @ApiResponse(code = 404, message = "The training definition has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "The training instance start time and end time are not valid.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> createTrainingInstance(@ApiParam(value = "Training instance to be created", required = true)
+                                                         @Valid @RequestBody TrainingInstanceCreateDTO trainingInstanceCreateDTO,
+                                                         @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                         @RequestParam(value = "fields", required = false) String fields) {
+        TrainingInstanceDTO trainingInstanceResource = trainingInstanceFacade.create(trainingInstanceCreateDTO);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingInstanceResource));
+    }
+
+    /**
+     * Update Training Instance.
+     *
+     * @param trainingInstanceUpdateDTO the Training Instance to be updated
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Update training instance",
+            notes = "This can only be done by organizer of training instance or administrator",
+            nickname = "updateTrainingInstance",
+            consumes = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instance has been updated."),
+            @ApiResponse(code = 404, message = "The training instance has not been found", response = ApiError.class),
+            @ApiResponse(code = 409, message = "The training instance start time and end time are not valid.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> updateTrainingInstance(@ApiParam(value = "Training instance to be updated")
+                                                         @RequestBody @Valid TrainingInstanceUpdateDTO trainingInstanceUpdateDTO) {
+        String newToken = trainingInstanceFacade.update(trainingInstanceUpdateDTO);
+        return new ResponseEntity<>(newToken, HttpStatus.OK);
+    }
+
+    /**
+     * Delete Training Instance.
+     *
+     * @param id          id of the Training Instance to be deleted
+     * @param forceDelete the force delete
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "DELETE",
+            value = "Delete training instance",
+            notes = "This can only be done by organizer of training instance or administrator",
+            nickname = "deleteTrainingInstance"
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instance has been updated."),
+            @ApiResponse(code = 400, message = "The provided training instance is not valid.", response = ApiError.class),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "The training instance cannot be deleted for the specific reason stated in the error message.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @DeleteMapping(path = "/{instanceId}")
+    public ResponseEntity<Void> deleteTrainingInstance(@ApiParam(value = "Id of training instance to be deleted", required = true)
+                                                       @PathVariable("instanceId") Long id,
+                                                       @ApiParam(value = "Indication if this training run must be deleted no matter of any check (force it)", required = false)
+                                                       @RequestParam(value = "forceDelete", required = false) boolean forceDelete) {
+        trainingInstanceFacade.delete(id, forceDelete);
+        return ResponseEntity.ok().build();
+    }
+
+    /**
+     * Assign pool response entity.
+     *
+     * @param id                              the id
+     * @param trainingInstanceAssignPoolIdDTO the training instance assign pool id dto
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PATCH",
+            value = "Assign pool to the training instance",
+            notes = "This can only be done by organizer of training instance or administrator",
+            nickname = "assignPool"
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training instance has been updated."),
+            @ApiResponse(code = 400, message = "The provided training instance is not valid.", response = ApiError.class),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "The training instance cannot be updated for the specific reason stated in the error message.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PatchMapping(path = "/{instanceId}/assign-pool")
+    public ResponseEntity<TrainingInstanceBasicInfoDTO> assignPool(@ApiParam(value = "Id of training instance to be updated", required = true)
+                                                                   @PathVariable("instanceId") Long id,
+                                                                   @ApiParam(value = "Id of pool to be assigned to training instance", required = true)
+                                                                   @Valid @RequestBody TrainingInstanceAssignPoolIdDTO trainingInstanceAssignPoolIdDTO) {
+        return ResponseEntity.ok(trainingInstanceFacade.assignPoolToTrainingInstance(id, trainingInstanceAssignPoolIdDTO));
+    }
+
+    /**
+     * Unassign pool response entity.
+     *
+     * @param id the id
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PATCH",
+            value = "Unassign pool of training instance",
+            notes = "This can only be done by organizer of training instance or administrator",
+            nickname = "unassignPool"
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The pool has been unassigned."),
+            @ApiResponse(code = 409, message = "The training instance has not assigned pool.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PatchMapping(path = "/{instanceId}/unassign-pool")
+    public ResponseEntity<TrainingInstanceBasicInfoDTO> unassignPool(@ApiParam(value = "Id of training instance to unassign pool.", required = true)
+                                                                     @PathVariable("instanceId") Long id) {
+        return ResponseEntity.ok(trainingInstanceFacade.unassignPoolInTrainingInstance(id));
+    }
+
+    /**
+     * Get all Training Runs by Training Instance id.
+     *
+     * @param instanceId the Training Instance id
+     * @param isActive   if true, only active Training Runs are returned
+     * @param pageable   Pageable parameter with information about pagination.
+     * @param fields     attributes of the object to be returned as the result.
+     * @return all Training Runs in given Training Instance.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all training runs of specific training instance",
+            response = TrainingRunsRestController.TrainingRunRestResource.class,
+            notes = "This can only be done by organizer of training instance or administrator",
+            nickname = "findAllTrainingRunsByTrainingInstanceId",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training runs have been found.", response = TrainingRunsRestController.TrainingRunRestResource.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{instanceId}/training-runs", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findAllTrainingRunsByTrainingInstanceId(@ApiParam(value = "Training Instance Id", required = true)
+                                                                          @PathVariable("instanceId") Long instanceId,
+                                                                          @ApiParam(value = "If only active or not active training runs should be returned.")
+                                                                          @RequestParam(value = "isActive", required = false) Boolean isActive,
+                                                                          @ApiParam(value = "Pagination support.")
+                                                                                  Pageable pageable,
+                                                                          @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                                          @RequestParam(value = "fields", required = false) String fields) {
+        PageResultResource<TrainingRunDTO> trainingRunResource =
+                trainingInstanceFacade.findTrainingRunsByTrainingInstance(instanceId, isActive, pageable);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, trainingRunResource));
+    }
+
+    /**
+     * Get requested organizers of training instance.
+     *
+     * @param trainingInstanceId id of training instance for which to get the organizers
+     * @param givenName          the given name
+     * @param familyName         the family name
+     * @param pageable           pageable parameter with information about pagination.
+     * @return List of users login and full name with role designer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get organizers of training instance.",
+            response = TrainingDefinitionsRestController.UserInfoRestResource.class,
+            nickname = "getOrganizersOfTrainingInstance",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The organizers have been found.", response = TrainingDefinitionsRestController.UserInfoRestResource.class),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/{instanceId}/organizers", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getOrganizersOfTrainingInstance(@ApiParam(value = "ID of training instance for which to retrieve the organizers.", required = true)
+                                                                  @PathVariable("instanceId") Long trainingInstanceId,
+                                                                  @ApiParam(value = "Given name filter.", required = true)
+                                                                  @RequestParam(value = "givenName", required = false) String givenName,
+                                                                  @ApiParam(value = "Family name filter.", required = true)
+                                                                  @RequestParam(value = "familyName", required = false) String familyName,
+                                                                  @ApiParam(value = "Pagination support.")
+                                                                          Pageable pageable) {
+        PageResultResource<UserRefDTO> designers = trainingInstanceFacade.getOrganizersOfTrainingInstance(trainingInstanceId, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, designers));
+    }
+
+    /**
+     * Get requested organizers not in given training instance.
+     *
+     * @param trainingInstanceId id ot the training instance
+     * @param givenName          the given name
+     * @param familyName         the family name
+     * @param pageable           pageable parameter with information about pagination.
+     * @return List of users login and full name with role organizer.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get organizers not in given training instance.",
+            response = TrainingDefinitionsRestController.UserInfoRestResource.class,
+            nickname = "findOrganizersNotInGivenTrainingInstance",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The organizers have been found.", response = TrainingDefinitionsRestController.UserInfoRestResource.class),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "{instanceId}/organizers-not-in-training-instance", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getOrganizersNotInGivenTrainingInstance(@ApiParam(value = "ID of the training instance which do not contains organizers you want to retrieve.", required = true)
+                                                                          @PathVariable("instanceId") Long trainingInstanceId,
+                                                                          @ApiParam(value = "Given name filter.", required = false)
+                                                                          @RequestParam(value = "givenName", required = false) String givenName,
+                                                                          @ApiParam(value = "Family name filter.", required = false)
+                                                                          @RequestParam(value = "familyName", required = false) String familyName,
+                                                                          @ApiParam(value = "Pagination support.")
+                                                                                  Pageable pageable) {
+        PageResultResource<UserRefDTO> designers = trainingInstanceFacade.getOrganizersNotInGivenTrainingInstance(trainingInstanceId, pageable, givenName, familyName);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, designers));
+    }
+
+    /**
+     * Concurrently add/remove organizers with given ids to/from the training instance.
+     *
+     * @param trainingInstanceId id of training instance for which to retrieve organizers
+     * @param organizersAddition ids of the organizers to be added to the training instance.
+     * @param organizersRemoval  ids of the organizers to be removed from the training instance.
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Edit organizers.",
+            nickname = "editOrganizers"
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 204, message = "The organizers of training instance have been edited."),
+            @ApiResponse(code = 404, message = "The training instance has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered. Probably error during calling other microservice.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @PutMapping(path = "/{instanceId}/organizers", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> editOrganizers(@ApiParam(value = "ID of training instance to be updated.", required = true)
+                                               @PathVariable("instanceId") Long trainingInstanceId,
+                                               @ApiParam(value = "Ids of the organizers to be added to the training instance.")
+                                               @RequestParam(value = "organizersAddition", required = false) Set<Long> organizersAddition,
+                                               @ApiParam(value = "Ids of the organizers to be removed from the training instance.")
+                                               @RequestParam(value = "organizersRemoval", required = false) Set<Long> organizersRemoval) {
+        trainingInstanceFacade.editOrganizers(trainingInstanceId, organizersAddition, organizersRemoval);
+        return ResponseEntity.noContent().build();
+    }
+
+    @ApiModel(value = "TrainingInstanceRestResource",
+            description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+    private static class TrainingInstanceRestResource extends PageResultResource<TrainingInstanceFindAllResponseDTO> {
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Retrieved Training Instances from databases.")
+        private List<TrainingInstanceFindAllResponseDTO> content;
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+        private Pagination pagination;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingRunsRestController.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingRunsRestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..83fe2059e8f757b1f1071abee2a51bdc16042f47
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/controller/TrainingRunsRestController.java
@@ -0,0 +1,462 @@
+package cz.muni.ics.kypo.training.adaptive.controller;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.bohnman.squiggly.Squiggly;
+import com.github.bohnman.squiggly.util.SquigglyUtils;
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.swagger.ApiPageableSwagger;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.IsCorrectAnswerDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.training.ValidateAnswerDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.AccessTrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.AccessedTrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunByIdDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiError;
+import cz.muni.ics.kypo.training.adaptive.facade.TrainingRunFacade;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.querydsl.binding.QuerydslPredicate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * The rest controller for Training runs.
+ */
+@Api(value = "/training-runs",
+        tags = "Training runs",
+        consumes = MediaType.APPLICATION_JSON_VALUE,
+        authorizations = @Authorization(value = "bearerAuth"))
+@ApiResponses(value = {
+        @ApiResponse(code = 401, message = "Full authentication is required to access this resource.", response = ApiError.class),
+        @ApiResponse(code = 403, message = "The necessary permissions are required for a resource.", response = ApiError.class)
+})
+@RestController
+@RequestMapping(value = "/training-runs", produces = MediaType.APPLICATION_JSON_VALUE)
+@Validated
+public class TrainingRunsRestController {
+
+    private TrainingRunFacade trainingRunFacade;
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Training runs rest controller.
+     *
+     * @param trainingRunFacade the training run facade
+     * @param objectMapper      the object mapper
+     */
+    @Autowired
+    public TrainingRunsRestController(TrainingRunFacade trainingRunFacade,
+                                      ObjectMapper objectMapper) {
+        this.trainingRunFacade = trainingRunFacade;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Delete training runs.
+     *
+     * @param trainingRunIds the training run ids
+     * @param forceDelete    the force delete
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "DELETE",
+            value = "Delete training runs",
+            nickname = "deleteTrainingRuns")
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training runs have been deleted."),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered", response = ApiError.class)
+    })
+    @DeleteMapping
+    public ResponseEntity<Void> deleteTrainingRuns(
+            @ApiParam(value = "Ids of training runs that will be deleted", required = true)
+            @RequestParam(value = "trainingRunIds", required = true) List<Long> trainingRunIds,
+            @ApiParam(value = "Indication if this training run must be deleted no matter of any check (force it)", required = false)
+            @RequestParam(value = "forceDelete", required = false) boolean forceDelete) {
+        trainingRunFacade.deleteTrainingRuns(trainingRunIds, forceDelete);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    /**
+     * Delete a given training run.
+     *
+     * @param runId       the training run id
+     * @param forceDelete the force delete
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "DELETE",
+            value = "Delete training run",
+            nickname = "deleteTrainingRun")
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been deleted."),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "The training run is still running.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered", response = ApiError.class)
+    })
+    @DeleteMapping(path = "/{runId}")
+    public ResponseEntity<Void> deleteTrainingRun(
+            @ApiParam(value = "Id of training run that will be deleted", required = true)
+            @PathVariable("runId") Long runId,
+            @ApiParam(value = "Indication if this training run must be deleted no matter of any check (force it)", required = false)
+            @RequestParam(value = "forceDelete", required = false) boolean forceDelete) {
+        trainingRunFacade.deleteTrainingRun(runId, forceDelete);
+        return new ResponseEntity<>(HttpStatus.OK);
+    }
+
+    /**
+     * Get requested Training Run by id.
+     *
+     * @param runId  of Training Run to return.
+     * @param fields attributes of the object to be returned as the result.
+     * @return Requested Training Run by id.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get training run by ID.",
+            response = TrainingRunByIdDTO.class,
+            nickname = "findTrainingRunById",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been found.", response = TrainingRunDTO.class),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{runId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findTrainingRunById(@ApiParam(value = "Id of training run", required = true)
+                                                      @PathVariable("runId") Long runId,
+                                                      @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                      @RequestParam(value = "fields", required = false) String fields) {
+        TrainingRunByIdDTO trainingRunResource = trainingRunFacade.findById(runId);
+        Squiggly.init(objectMapper, fields);
+        return new ResponseEntity<>(SquigglyUtils.stringify(objectMapper, trainingRunResource), HttpStatus.OK);
+    }
+
+    /**
+     * Get all Training Runs.
+     *
+     * @param predicate specifies query to database.
+     * @param pageable  pageable parameter with information about pagination.
+     * @param fields    attributes of the object to be returned as the result.
+     * @return all Training Runs.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all training runs.",
+            response = TrainingRunRestResource.class,
+            nickname = "findAllTrainingRuns",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training runs have been found.", response = TrainingRunRestResource.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> findAllTrainingRuns(@QuerydslPredicate(root = TrainingRun.class) Predicate predicate,
+                                                      @ApiParam(value = "Pagination support.", required = false) Pageable pageable,
+                                                      @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                      @RequestParam(value = "fields", required = false) String fields) {
+        PageResultResource<TrainingRunDTO> trainingRunResource = trainingRunFacade.findAll(predicate, pageable);
+        Squiggly.init(objectMapper, fields);
+        return new ResponseEntity<>(SquigglyUtils.stringify(objectMapper, trainingRunResource), HttpStatus.OK);
+    }
+
+    /**
+     * Access training run.
+     *
+     * @param accessToken the access token
+     * @return first phase of training run.
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Access training run.",
+            response = AccessTrainingRunDTO.class,
+            nickname = "createTrainingRun",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been accessed.", response = AccessTrainingRunDTO.class),
+            @ApiResponse(code = 404, message = "There is no training instance with given accessToken or first phase not found in database.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "No assigned pool to the training instance.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Some error occurred during getting info about sandboxes.", response = ApiError.class),
+    })
+    @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<AccessTrainingRunDTO> accessTrainingRun(@ApiParam(value = "accessToken", required = true)
+                                                                  @RequestParam(value = "accessToken", required = true) String accessToken) {
+        AccessTrainingRunDTO accessTrainingRunDTO = trainingRunFacade.accessTrainingRun(accessToken);
+        return ResponseEntity.ok(accessTrainingRunDTO);
+    }
+
+    /**
+     * Get all accessed Training Runs.
+     *
+     * @param pageable    pageable parameter with information about pagination.
+     * @param fields      attributes of the object to be returned as the result.
+     * @param sortByTitle "asc" for ascending alphabetical sort by title, "desc" for descending
+     * @return all accessed Training Runs.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get all accessed training runs.",
+            notes = "Returns training run which was accessed by logged in user",
+            response = AccessedTrainingRunRestResource.class,
+            nickname = "getAllAccessedTrainingRuns",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The accessed training runs have been found.", response = AccessedTrainingRunDTO.class, responseContainer = "List"),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @ApiPageableSwagger
+    @GetMapping(path = "/accessible", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getAllAccessedTrainingRuns(@ApiParam(value = "Pagination support.", required = false) Pageable pageable,
+                                                             @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                                             @RequestParam(value = "fields", required = false) String fields,
+                                                             @ApiParam(value = "Sort by title attribute. As values us asc|desc", required = false, example = "asc")
+                                                             @RequestParam(value = "sortByTitle", required = false) String sortByTitle) {
+        PageResultResource<AccessedTrainingRunDTO> accessedTrainingRunDTOS = trainingRunFacade.findAllAccessedTrainingRuns(pageable, sortByTitle);
+        Squiggly.init(objectMapper, fields);
+        return new ResponseEntity<>(SquigglyUtils.stringify(objectMapper, accessedTrainingRunDTOS), HttpStatus.OK);
+    }
+
+    /**
+     * Get next phase of given Training Run.
+     *
+     * @param runId  of Training Run for which to get next phase.
+     * @param fields attributes of the object to be returned as the result.
+     * @return Requested next phase.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get phase of given training run.",
+            notes = "Returns (questionnaire, training, info) phase if any next phase exists and training run as well",
+            response = AbstractPhaseDTO.class,
+            nickname = "getNextPhase",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The next phase has been found.", response = AbstractPhaseDTO.class),
+            @ApiResponse(code = 404, message = "The next phase has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{runId}/next-phases", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getNextPhase(@ApiParam(value = "Training run ID", required = true)
+                                               @PathVariable("runId") Long runId,
+                                               @ApiParam(value = "Fields which should be returned in REST API response", required = false)
+                                               @RequestParam(value = "fields", required = false) String fields) {
+        AbstractPhaseDTO phaseDTO = trainingRunFacade.getNextPhase(runId);
+        Squiggly.init(objectMapper, fields);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, phaseDTO));
+    }
+
+    /**
+     * Get solution of current training phase.
+     *
+     * @param runId of Training Run for which to get solution.
+     * @return Requested solution of training phase.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get solution of training phase.",
+            notes = "Returns solution if given training runs exists and current phase is training phase",
+            response = String.class,
+            nickname = "getSolution",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The solution has been found.", response = String.class),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 400, message = "Current phase is not training phase and does not have solution.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{runId}/solutions", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<String> getSolution(@ApiParam(value = "Training run ID", required = true)
+                                              @PathVariable("runId") Long runId) {
+        return ResponseEntity.ok(trainingRunFacade.getSolution(runId));
+    }
+
+    /**
+     * Check if submitted answer is correct.
+     *
+     * @param runId             the run id
+     * @param validateAnswerDTO submitted string.
+     * @return True if answer is correct, false if answer is wrong.
+     */
+    @ApiOperation(httpMethod = "POST",
+            value = "Check answer of training phase",
+            notes = "Current phase of given training run must be training phase",
+            response = Boolean.class,
+            nickname = "isCorrectAnswer",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The answer has been checked.", response = IsCorrectAnswerDTO.class),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 400, message = "Current phase is not training phase and does not have answer.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PostMapping(path = "/{runId}/is-correct-answer", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<IsCorrectAnswerDTO> isCorrectAnswer(@ApiParam(value = "Training run ID", required = true)
+                                                              @PathVariable("runId") Long runId,
+                                                              @ApiParam(value = "Submitted answer", required = true)
+                                                              @RequestBody @Valid ValidateAnswerDTO validateAnswerDTO) {
+        return ResponseEntity.ok(trainingRunFacade.isCorrectAnswer(runId, validateAnswerDTO.getAnswer()));
+    }
+
+    /**
+     * Resume paused training run.
+     *
+     * @param runId id of training run.
+     * @return current phase of training run.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get current phase of resumed training run",
+            response = AccessTrainingRunDTO.class,
+            nickname = "resumeTrainingRun",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been resumed.", response = AccessTrainingRunDTO.class),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot resume finished training run.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{runId}/resumption", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<AccessTrainingRunDTO> resumeTrainingRun(@ApiParam(value = "Training run ID", required = true)
+                                                                  @PathVariable("runId") Long runId) {
+        AccessTrainingRunDTO resumedTrainingRunDTO = trainingRunFacade.resumeTrainingRun(runId);
+        return ResponseEntity.ok(resumedTrainingRunDTO);
+    }
+
+    /**
+     * Finish training run.
+     *
+     * @param runId id of training run.
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Finish training run",
+            nickname = "finishTrainingRun",
+            notes = "Training run will be finished if the current phase is the last phase and it is answered.",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been finished."),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Cannot finish training run because of the current state.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PutMapping(path = "/{runId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> finishTrainingRun(@ApiParam(value = "Training run ID", required = true)
+                                                  @PathVariable("runId") Long runId) {
+        trainingRunFacade.finishTrainingRun(runId);
+        return ResponseEntity.ok().build();
+    }
+
+    /**
+     * Evaluate responses to questionnaire.
+     *
+     * @param runId     id of training run.
+     * @param responses to questionnaire
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PUT",
+            value = "Evaluate responses to questionnaire",
+            nickname = "evaluateResponsesToQuestionnaire",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 204, message = "The responses to questionnaire has been evaluated and stored."),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 409, message = "Current phase of training is not questionnaire phase or phase has been already answered.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PutMapping(value = "/{runId}/questionnaire-evaluations", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> evaluateResponsesToQuestionnaire(@ApiParam(value = "Training run ID", required = true)
+                                                                 @PathVariable("runId") Long runId,
+                                                                 @ApiParam(value = "Responses to questionnaire", required = true)
+                                                                 @RequestBody String responses) {
+        trainingRunFacade.evaluateResponsesToQuestionnaire(runId, responses);
+        return ResponseEntity.noContent().build();
+    }
+
+    /**
+     * Get requested participant of the given training run.
+     *
+     * @param trainingRunId id of training run for which to get participant
+     * @return Participant of specific training run.
+     */
+    @ApiOperation(httpMethod = "GET",
+            value = "Get participant.",
+            response = UserRefDTO.class,
+            nickname = "getParticipant",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The participant has been found.", response = UserRefDTO.class),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @GetMapping(path = "/{runId}/participant", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Object> getParticipant(
+            @ApiParam(value = "Get participant for the given runId.")
+            @PathVariable("runId") Long trainingRunId) {
+        UserRefDTO participant = trainingRunFacade.getParticipant(trainingRunId);
+        return ResponseEntity.ok(SquigglyUtils.stringify(objectMapper, participant));
+    }
+
+    /**
+     * Archive training run.
+     *
+     * @param runId id of training run.
+     * @return the response entity
+     */
+    @ApiOperation(httpMethod = "PATCH",
+            value = "Archive training run",
+            nickname = "archiveTrainingRun",
+            notes = "The state of the Training run will be change to archived.",
+            produces = MediaType.APPLICATION_JSON_VALUE
+    )
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "The training run has been archived."),
+            @ApiResponse(code = 404, message = "The training run has not been found.", response = ApiError.class),
+            @ApiResponse(code = 500, message = "Unexpected condition was encountered.", response = ApiError.class)
+    })
+    @PatchMapping(path = "/{runId}/archive", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<Void> archiveTrainingRun(@ApiParam(value = "Training run ID", required = true)
+                                                   @PathVariable("runId") Long runId) {
+        trainingRunFacade.archiveTrainingRun(runId);
+        return ResponseEntity.ok().build();
+    }
+
+    /**
+     * The type Training run rest resource.
+     */
+    @ApiModel(value = "TrainingRunRestResource",
+            description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+    public static class TrainingRunRestResource extends PageResultResource<TrainingRunDTO> {
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Retrieved Training Runs from databases.")
+        private List<TrainingRunDTO> content;
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+        private Pagination pagination;
+    }
+
+    @ApiModel(description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+    private static class AccessedTrainingRunRestResource extends PageResultResource<AccessedTrainingRunDTO> {
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Retrieved Accessed Training Runs from databases.")
+        private List<AccessedTrainingRunDTO> content;
+        @JsonProperty(required = true)
+        @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+        private Pagination pagination;
+    }
+
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCDeserializer.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..e31e9c6501f2990d30b142877062dd8267701ca5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCDeserializer.java
@@ -0,0 +1,28 @@
+package cz.muni.ics.kypo.training.adaptive.converter;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+
+/**
+ * Deserializes UTC time with 'Z' suffix from Angular typescript Date, e.g., the date: '2018-11-30T10:26:02.727Z'
+ */
+public class LocalDateTimeUTCDeserializer extends StdDeserializer<LocalDateTime> {
+
+    public LocalDateTimeUTCDeserializer() {
+        super(LocalDateTime.class);
+    }
+
+    @Override
+    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+        Instant instant = Instant.parse(jp.readValueAs(String.class));
+        return LocalDateTime.ofInstant(instant, ZoneId.of(ZoneOffset.UTC.getId()));
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCSerializer.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfc9c3b29959f1310f5fb25b3e47a3928fbbe6de
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/converter/LocalDateTimeUTCSerializer.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.converter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+/**
+ * This class serialize LocalDateTime to UTC time.
+ */
+public class LocalDateTimeUTCSerializer extends StdSerializer<LocalDateTime> {
+
+    public LocalDateTimeUTCSerializer() {
+        super(LocalDateTime.class);
+    }
+
+    @Override
+    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+        gen.writeString(value.toInstant(ZoneOffset.UTC).toString());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractEntity.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..5892f365aaf89aefacd021f9195ca68300dae7d8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractEntity.java
@@ -0,0 +1,50 @@
+package cz.muni.ics.kypo.training.adaptive.domain;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * The type Abstract entity.
+ *
+ * @param <PK> Primary key for a given entity.
+ */
+@MappedSuperclass
+public abstract class AbstractEntity<PK extends Serializable> implements Serializable {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id", unique = true, nullable = false, insertable = false)
+    private PK id;
+
+    /**
+     * Instantiates a new Abstract entity.
+     */
+    public AbstractEntity() {
+    }
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public PK getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(PK id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractEntity{" +
+                "id=" + id +
+                '}';
+    }
+}
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AccessToken.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AccessToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc97d18f3f7a36318fede45d6d313c7905ed38a2
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AccessToken.java
@@ -0,0 +1,98 @@
+package cz.muni.ics.kypo.training.adaptive.domain;
+
+import javax.persistence.*;
+import java.util.Objects;
+
+/**
+ * Class representing access token needed by trainee to start a Training run.
+ * Access tokens are associated with Training instances.
+ */
+@Entity
+@Table(name = "access_token")
+@NamedQueries({
+        @NamedQuery(
+                name = "AccessToken.findOneByAccessToken",
+                query = "SELECT at FROM AccessToken at WHERE at.accessToken = :accessToken"
+        ),
+})
+public class AccessToken extends AbstractEntity<Long> {
+
+    @Column(name = "access_token", nullable = false, unique = true)
+    private String accessToken;
+
+    /**
+     * Instantiates a new Access token
+     */
+    public AccessToken() {
+    }
+
+    /**
+     * Instantiates a new Access token
+     *
+     * @param id          unique identification number of access token
+     * @param accessToken string representing token that trainee needs to know to access Training run
+     */
+    public AccessToken(Long id, String accessToken) {
+        this.accessToken = accessToken;
+        super.setId(id);
+    }
+
+    /**
+     * Gets unique identification number of access token
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return super.getId();
+    }
+
+    /**
+     * Sets unique identification number of access token
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets string representing token that trainee needs to know to access Training run
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets string representing token that trainee needs to know to access Training run
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof AccessToken))
+            return false;
+        AccessToken accessToken = (AccessToken) o;
+        return Objects.equals(super.getId(), accessToken.getId()) && Objects.equals(this.accessToken, accessToken.getAccessToken());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.getId(), accessToken);
+    }
+
+    @Override
+    public String toString() {
+        return "AccessToken{" +
+                "id=" + super.getId() +
+                ", accessToken='" + accessToken + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TRAcquisitionLock.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TRAcquisitionLock.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e5e7b7b165160a792925dbe7eb4d9dccfebe497
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TRAcquisitionLock.java
@@ -0,0 +1,133 @@
+package cz.muni.ics.kypo.training.adaptive.domain;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * The entity which prevents multiple training runs to be created in parallel threads. Basically it determines active training runs.
+ */
+@Entity
+@Table(name = "training_run_acquisition_lock",
+        uniqueConstraints = @UniqueConstraint(columnNames = {"participant_ref_id", "training_instance_id"}))
+@NamedQueries({
+        @NamedQuery(
+                name = "TRAcquisitionLock.deleteByParticipantRefIdAndTrainingInstanceId",
+                query = "DELETE FROM TRAcquisitionLock tral WHERE tral.participantRefId = :participantRefId AND tral.trainingInstanceId = :trainingInstanceId"
+        )
+})
+public class TRAcquisitionLock extends AbstractEntity<Long> {
+
+    @Column(name = "participant_ref_id")
+    private Long participantRefId;
+    @Column(name = "training_instance_id")
+    private Long trainingInstanceId;
+    @Column(name = "creation_time")
+    private LocalDateTime creationTime;
+
+    /**
+     * Instantiates a new Tr acquisition lock.
+     */
+    public TRAcquisitionLock() {
+    }
+
+    /**
+     * Instantiates a new Tr acquisition lock.
+     *
+     * @param participantRefId   the participant ref id
+     * @param trainingInstanceId the training instance id
+     * @param creationTime       the creation time
+     */
+    public TRAcquisitionLock(Long participantRefId, Long trainingInstanceId, LocalDateTime creationTime) {
+        this.participantRefId = participantRefId;
+        this.trainingInstanceId = trainingInstanceId;
+        this.creationTime = creationTime;
+    }
+
+    public Long getId() {
+        return super.getId();
+    }
+
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets participant ref id.
+     *
+     * @return the participant ref id
+     */
+    public Long getParticipantRefId() {
+        return participantRefId;
+    }
+
+    /**
+     * Sets participant ref id.
+     *
+     * @param participantRefId the participant ref id
+     */
+    public void setParticipantRefId(Long participantRefId) {
+        this.participantRefId = participantRefId;
+    }
+
+    /**
+     * Gets training instance id.
+     *
+     * @return the training instance id
+     */
+    public Long getTrainingInstanceId() {
+        return trainingInstanceId;
+    }
+
+    /**
+     * Sets training instance id.
+     *
+     * @param trainingInstanceId the training instance id
+     */
+    public void setTrainingInstanceId(Long trainingInstanceId) {
+        this.trainingInstanceId = trainingInstanceId;
+    }
+
+    /**
+     * Gets creation time.
+     *
+     * @return the creation time
+     */
+    public LocalDateTime getCreationTime() {
+        return creationTime;
+    }
+
+    /**
+     * Sets creation time.
+     *
+     * @param creationTime the creation time
+     */
+    public void setCreationTime(LocalDateTime creationTime) {
+        this.creationTime = creationTime;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TRAcquisitionLock))
+            return false;
+        TRAcquisitionLock that = (TRAcquisitionLock) o;
+        return Objects.equals(getParticipantRefId(), that.getParticipantRefId()) &&
+                Objects.equals(getTrainingInstanceId(), that.getTrainingInstanceId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getParticipantRefId(), getTrainingInstanceId());
+    }
+
+    @Override
+    public String toString() {
+        return "TRAcquisitionLock{" +
+                "id=" + super.getId() +
+                ", participantRefId=" + participantRefId +
+                ", trainingInstanceId=" + trainingInstanceId +
+                ", creationTime=" + creationTime +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/UserRef.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/UserRef.java
new file mode 100644
index 0000000000000000000000000000000000000000..e312c32f34a0291fedc34a49c7b55453879e7a1d
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/UserRef.java
@@ -0,0 +1,185 @@
+package cz.muni.ics.kypo.training.adaptive.domain;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+
+import javax.persistence.*;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class representing DB reference for user and training instances and definition they can access
+ */
+@Entity
+@Table(name = "user_ref", uniqueConstraints = @UniqueConstraint(columnNames = {"user_ref_id"}))
+@NamedQueries({
+        @NamedQuery(
+                name = "UserRef.findUsers",
+                query = "SELECT ur FROM UserRef ur WHERE ur.userRefId IN :userRefId"
+        ),
+        @NamedQuery(
+                name = "UserRef.findUserByUserRefId",
+                query = "SELECT ur FROM UserRef ur WHERE ur.userRefId = :userRefId"
+        ),
+        @NamedQuery(
+                name = "UserRef.findParticipantsRefByTrainingInstanceId",
+                query = "SELECT pr.userRefId FROM TrainingRun tr " +
+                        "INNER JOIN tr.participantRef pr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "WHERE ti.id = :trainingInstanceId"
+        )
+})
+public class UserRef extends AbstractEntity<Long> {
+
+    @Column(name = "user_ref_id", nullable = false)
+    private Long userRefId;
+    @ManyToMany(mappedBy = "organizers", fetch = FetchType.LAZY)
+    private Set<TrainingInstance> trainingInstances = new HashSet<>();
+    @ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY)
+    private Set<TrainingDefinition> trainingDefinitions = new HashSet<>();
+
+    /**
+     * Instantiates a new user reference
+     */
+    public UserRef() {
+    }
+
+    /**
+     * Instantiates a new user reference
+     * @param userRefId id of the user stored in user management service
+     */
+    public UserRef(Long userRefId) {
+        this.userRefId = userRefId;
+    }
+
+    /**
+     * Gets unique identification number of user reference
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return super.getId();
+    }
+
+    /**
+     * Sets unique identification number of user reference
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets user ref id.
+     *
+     * @return the user ref id
+     */
+    public Long getUserRefId() {
+        return userRefId;
+    }
+
+    /**
+     * Sets user ref id.
+     *
+     * @param userRefId the user ref id
+     */
+    public void setUserRefId(Long userRefId) {
+        this.userRefId = userRefId;
+    }
+
+    /**
+     * Gets set of training instances user can access
+     *
+     * @return the training instances
+     */
+    public Set<TrainingInstance> getTrainingInstances() {
+        return Collections.unmodifiableSet(trainingInstances);
+    }
+
+    /**
+     * Sets set of training instances user can access
+     *
+     * @param trainingInstances the training instances
+     */
+    public void setTrainingInstances(Set<TrainingInstance> trainingInstances) {
+        this.trainingInstances = trainingInstances;
+    }
+
+    /**
+     * Gets set of training definitions user can access
+     *
+     * @return the training definitions
+     */
+    public Set<TrainingDefinition> getTrainingDefinitions() {
+        return Collections.unmodifiableSet(trainingDefinitions);
+    }
+
+    /**
+     * Sets set of training definitions user can access
+     *
+     * @param trainingDefinitions the training definitions
+     */
+    public void setTrainingDefinitions(Set<TrainingDefinition> trainingDefinitions) {
+        this.trainingDefinitions = trainingDefinitions;
+    }
+
+    /**
+     * Adds definition to the set of training definitions user can access
+     *
+     * @param trainingDefinition the training definition
+     */
+    public void addTrainingDefinition(TrainingDefinition trainingDefinition) {
+        this.trainingDefinitions.add(trainingDefinition);
+    }
+
+    /**
+     * Removes definition from the set of training definitions user can access
+     *
+     * @param trainingDefinition the training definition
+     */
+    public void removeTrainingDefinition(TrainingDefinition trainingDefinition) {
+        this.trainingDefinitions.remove(trainingDefinition);
+    }
+
+    /**
+     * Adds instance to the set of training instances user can access
+     *
+     * @param trainingInstance the training instance
+     */
+    public void addTrainingInstance(TrainingInstance trainingInstance) {
+        this.trainingInstances.add(trainingInstance);
+    }
+
+    /**
+     * Removes instance from the set of training instances user can access
+     *
+     * @param trainingInstance the training instance
+     */
+    public void removeTrainingInstance(TrainingInstance trainingInstance) {
+        this.trainingInstances.remove(trainingInstance);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UserRef)) return false;
+        UserRef userRef = (UserRef) o;
+        return Objects.equals(getUserRefId(), userRef.getUserRefId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUserRefId());
+    }
+
+    @Override
+    public String toString() {
+        return "UserRef{" +
+                "id=" + super.getId() +
+                ", userRefId=" + userRefId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractPhase.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/AbstractPhase.java
similarity index 62%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractPhase.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/AbstractPhase.java
index c2a77de28ea3639d482df5294f912f191db42173..33a0fa465c223a814289b6b3f1a4eb8b33b9904c 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/AbstractPhase.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/AbstractPhase.java
@@ -1,15 +1,9 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
+
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+
+import javax.persistence.*;
 import java.io.Serializable;
 
 
@@ -17,7 +11,7 @@ import java.io.Serializable;
 
 @Entity
 @Table(name = "abstract_phase")
-@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@Inheritance(strategy = InheritanceType.JOINED)
 public abstract class AbstractPhase implements Serializable {
 
     @Id
@@ -31,7 +25,8 @@ public abstract class AbstractPhase implements Serializable {
     @Column(name = "order_in_training_definition", nullable = false)
     private Integer order;
 
-    private Long trainingDefinitionId;
+    @ManyToOne(fetch = FetchType.LAZY)
+    private TrainingDefinition trainingDefinition;
 
     public String getTitle() {
         return title;
@@ -57,12 +52,12 @@ public abstract class AbstractPhase implements Serializable {
         this.id = id;
     }
 
-    public Long getTrainingDefinitionId() {
-        return trainingDefinitionId;
+    public TrainingDefinition getTrainingDefinition() {
+        return trainingDefinition;
     }
 
-    public void setTrainingDefinitionId(Long trainingDefinition) {
-        this.trainingDefinitionId = trainingDefinition;
+    public void setTrainingDefinition(TrainingDefinition trainingDefinition) {
+        this.trainingDefinition = trainingDefinition;
     }
 
     @Override
@@ -71,7 +66,7 @@ public abstract class AbstractPhase implements Serializable {
                 "id=" + id +
                 ", title='" + title + '\'' +
                 ", order=" + order +
-                ", trainingDefinitionId=" + trainingDefinitionId +
+                ", trainingDefinitionId=" + trainingDefinition +
                 '}';
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/DecisionMatrixRow.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/DecisionMatrixRow.java
similarity index 87%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/DecisionMatrixRow.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/DecisionMatrixRow.java
index 964d9aa41fe0c9e4d824afa92dc6a915c2f93770..ba11c914876153515a1e14194f5b1e5a7f4c8e30 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/DecisionMatrixRow.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/DecisionMatrixRow.java
@@ -1,14 +1,7 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.ManyToOne;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
+
+
+import javax.persistence.*;
 import java.io.Serializable;
 
 @Entity
@@ -30,6 +23,7 @@ public class DecisionMatrixRow implements Serializable {
     private double wrongAnswers;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "training_phase_id")
     private TrainingPhase trainingPhase;
 
     public Long getId() {
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/InfoPhase.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/InfoPhase.java
similarity index 77%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/InfoPhase.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/InfoPhase.java
index ccc0f87ffcebc6b32f543519b8f80e18d929f349..ce0d2be77dc2356a6f48e2b12e432aae5198f0b6 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/InfoPhase.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/InfoPhase.java
@@ -1,10 +1,12 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
 
 import javax.persistence.Entity;
+import javax.persistence.PrimaryKeyJoinColumn;
 import javax.persistence.Table;
 
 @Entity
 @Table(name = "info_phase")
+@PrimaryKeyJoinColumn(name = "phase_id")
 public class InfoPhase extends AbstractPhase {
 
     private String content;
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionnairePhase.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/QuestionnairePhase.java
similarity index 73%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionnairePhase.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/QuestionnairePhase.java
index 85e53f08e5e2e5677c4fcca3a6375176b78ec124..3b3ac5a1b467c2b59f0587068bfe23f1bcb18588 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionnairePhase.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/QuestionnairePhase.java
@@ -1,15 +1,10 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
 
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
 import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
 
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
-import javax.persistence.OneToMany;
-import javax.persistence.OrderBy;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -51,4 +46,11 @@ public class QuestionnairePhase extends AbstractPhase {
     public void setQuestionPhaseRelations(List<QuestionPhaseRelation> questionPhaseRelations) {
         this.questionPhaseRelations = questionPhaseRelations;
     }
+
+    public void addQuestionPhaseRelation(QuestionPhaseRelation questionPhaseRelation) {
+        if (this.questionPhaseRelations == null) {
+            this.questionPhaseRelations = new ArrayList<>();
+        }
+        this.questionPhaseRelations.add(questionPhaseRelation);
+    }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Task.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/Task.java
similarity index 87%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Task.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/Task.java
index aee9e4666f8297e9f643c01ab64f2dd7c4cbe859..73dc8241167c8aa3895eff40e933cba3b401934e 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Task.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/Task.java
@@ -1,14 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.ManyToOne;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
+
+import javax.persistence.*;
 import java.io.Serializable;
 
 
@@ -34,6 +26,7 @@ public class Task implements Serializable {
     private Integer order;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "training_phase_id")
     private TrainingPhase trainingPhase;
 
     public Long getId() {
@@ -128,7 +121,7 @@ public class Task implements Serializable {
                 ", modifySandbox=" + modifySandbox +
                 ", sandboxChangeExpectedDuration=" + sandboxChangeExpectedDuration +
                 ", order=" + order +
-                ", trainingPhase=" + trainingPhase.getId() +
+                ", trainingPhase=" + id +
                 '}';
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TrainingPhase.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/TrainingPhase.java
similarity index 84%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TrainingPhase.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/TrainingPhase.java
index be1db20ab43f314eef6258b2f61bff641484e8a9..98d3b372e1c13593a03ff46ed589316f776a68b6 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/TrainingPhase.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/TrainingPhase.java
@@ -1,11 +1,8 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.OneToMany;
-import javax.persistence.OrderBy;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+
+import javax.persistence.*;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -18,7 +15,7 @@ public class TrainingPhase extends AbstractPhase {
     private int allowedWrongAnswers;
 
     @OrderBy
-    @OneToMany(mappedBy = "trainingPhase", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
+    @OneToMany(mappedBy = "trainingPhase", cascade = {CascadeType.REMOVE, CascadeType.PERSIST}, orphanRemoval = true, fetch = FetchType.LAZY)
     private List<Task> tasks = new ArrayList<>();
 
     @OrderBy
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Question.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/Question.java
similarity index 77%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Question.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/Question.java
index 4f02b68c4f239a213d47f7069199a2d85b94b9a5..9edf793159a89284be53f814665d69bef62de17e 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/Question.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/Question.java
@@ -1,28 +1,11 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
+package cz.muni.ics.kypo.training.adaptive.domain.phase.questions;
 
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
 import cz.muni.ics.kypo.training.adaptive.enums.QuestionType;
 
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
-import javax.persistence.OneToMany;
-import javax.persistence.OrderBy;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+import javax.persistence.*;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
 @Entity
 @Table(name = "question")
@@ -43,6 +26,7 @@ public class Question implements Serializable {
     private int order;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "questionnaire_phase_id")
     private QuestionnairePhase questionnairePhase;
 
     @OrderBy
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionChoice.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionChoice.java
similarity index 77%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionChoice.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionChoice.java
index 1aa21438b9876ffb2dbcc258a51affaa3e32b5f0..ecdc67fc0ddc042ef4956f0305fd00850de5ec58 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionChoice.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionChoice.java
@@ -1,14 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.ManyToOne;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase.questions;
+
+import javax.persistence.*;
 import java.io.Serializable;
 
 @Entity
@@ -28,6 +20,7 @@ public class QuestionChoice implements Serializable {
     private Integer order;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "question_id")
     private Question question;
 
     public Long getId() {
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionPhaseRelation.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionPhaseRelation.java
similarity index 80%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionPhaseRelation.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionPhaseRelation.java
index 87fd9068daffc46e90cd74ce0001ab6f0edb3a63..e2e3cf753f92eb32e8bfbbbfb14a2ecc9fdc8eb3 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/QuestionPhaseRelation.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/phase/questions/QuestionPhaseRelation.java
@@ -1,18 +1,9 @@
-package cz.muni.ics.kypo.training.adaptive.domain;
-
-import javax.persistence.CascadeType;
-import javax.persistence.Column;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
-import javax.persistence.ManyToOne;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.Table;
+package cz.muni.ics.kypo.training.adaptive.domain.phase.questions;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+
+import javax.persistence.*;
 import java.io.Serializable;
 import java.util.Set;
 
@@ -30,9 +21,11 @@ public class QuestionPhaseRelation implements Serializable {
     private Integer order;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "questionnaire_phase_id")
     private QuestionnairePhase questionnairePhase;
 
     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "related_training_phase_id")
     private TrainingPhase relatedTrainingPhase;
 
     @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@@ -82,6 +75,9 @@ public class QuestionPhaseRelation implements Serializable {
 
     public void setQuestions(Set<Question> questions) {
         this.questions = questions;
+        for (Question question : questions) {
+            question.addQuestionPhaseRelation(this);
+        }
     }
 
     public int getSuccessRate() {
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingDefinition.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingDefinition.java
new file mode 100644
index 0000000000000000000000000000000000000000..69753683b9a5a64ac0cafeb7bd01a65466905fd5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingDefinition.java
@@ -0,0 +1,293 @@
+package cz.muni.ics.kypo.training.adaptive.domain.training;
+
+import cz.muni.ics.kypo.training.adaptive.domain.AbstractEntity;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * Class represents Training definition.
+ * Training instances can be created based on definitions.
+ */
+@Entity
+@Table(name = "training_definition")
+@NamedQueries({
+        @NamedQuery(
+                name = "TrainingDefinition.findAllForOrganizers",
+                query = "SELECT DISTINCT td FROM TrainingDefinition td WHERE td.state = :state"
+        ),
+        @NamedQuery(
+                name = "TrainingDefinition.findAllForDesigner",
+                query = "SELECT DISTINCT td FROM TrainingDefinition td " +
+                        "LEFT JOIN td.authors aut " +
+                        "WHERE aut.userRefId = :userRefId  AND td.state = 'UNRELEASED'"
+        )
+})
+public class TrainingDefinition extends AbstractEntity<Long> {
+
+    @Column(name = "title", nullable = false)
+    private String title;
+    @Column(name = "description", nullable = true)
+    private String description;
+    @Column(name = "prerequisites", nullable = true)
+    private String[] prerequisites;
+    @Column(name = "outcomes", nullable = true)
+    private String[] outcomes;
+    @Column(name = "state", length = 128, nullable = false)
+    @Enumerated(EnumType.STRING)
+    private TDState state;
+    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
+    @JoinTable(name = "training_definition_user_ref",
+            joinColumns = @JoinColumn(name = "training_definition_id"),
+            inverseJoinColumns = @JoinColumn(name = "user_ref_id")
+    )
+    private Set<UserRef> authors = new HashSet<>();
+    @Column(name = "show_stepper_bar", nullable = false)
+    private boolean showStepperBar;
+    @Column(name = "estimated_duration", nullable = true)
+    private long estimatedDuration;
+    @Column(name = "last_edited", nullable = false)
+    private LocalDateTime lastEdited;
+
+    /**
+     * Gets unique identification number of Training definition
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return super.getId();
+    }
+
+    /**
+     * Sets unique identification number of Training definition
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets title of Training definition
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title of Training definition
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets text description specifying info about Training definition
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets text description specifying info about Training definition
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Gets skill prerequisites that trainee should have to be able to complete training runs created
+     * from this Training definition
+     *
+     * @return the string [ ]
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets skill prerequisites that trainee should have to be able to complete training runs created
+     * from this Training definition
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Gets knowledge that trainee can learn from training runs created from this Training definition
+     *
+     * @return the string [ ]
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets knowledge that trainee can learn from training runs created from this Training definition
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets development state in which is Training definition
+     * States are PRIVATED, RELEASED, ARCHIVED and UNRELEASED
+     *
+     * @return the state
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets development state in which is Training definition
+     * States are PRIVATED, RELEASED, ARCHIVED and UNRELEASED
+     *
+     * @param state the state
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets set of users that can make changes to the Training definition
+     *
+     * @return the authors
+     */
+    public Set<UserRef> getAuthors() {
+        return Collections.unmodifiableSet(authors);
+    }
+
+    /**
+     * Sets set of users that can make changes to the Training definition
+     *
+     * @param authors the authors
+     */
+    public void setAuthors(Set<UserRef> authors) {
+        this.authors = authors;
+    }
+
+    /**
+     * Adds user to the set of authors that can make changes to the Training definition
+     *
+     * @param authorRef the author ref
+     */
+    public void addAuthor(UserRef authorRef) {
+        this.authors.add(authorRef);
+        authorRef.addTrainingDefinition(this);
+    }
+
+    /**
+     * Remove authors with given ids from the set of authors.
+     *
+     * @param userRefIds ids of the authors to be removed.
+     */
+    public void removeAuthorsByUserRefIds(Set<Long> userRefIds) {
+        this.authors.removeIf(userRef -> userRefIds.contains(userRef.getUserRefId()));
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Gets estimated duration in minutes that it should take to complete run based on given Training definition
+     *
+     * @return the estimated duration
+     */
+    public long getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration in minutes that it should take to complete run based on given Training definition
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(long estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    /**
+     * Gets time of last edit done to Training Definition
+     *
+     * @return the last edited
+     */
+    public LocalDateTime getLastEdited() {
+        return lastEdited;
+    }
+
+    /**
+     * Sets time of last edit done to Training Definition
+     *
+     * @param lastEdited the last edited
+     */
+    public void setLastEdited(LocalDateTime lastEdited) {
+        this.lastEdited = lastEdited;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(description, outcomes, prerequisites, state, title);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof TrainingDefinition))
+            return false;
+        TrainingDefinition other = (TrainingDefinition) obj;
+        return Objects.equals(description, other.getDescription())
+                && Arrays.equals(outcomes, other.getOutcomes())
+                && Arrays.equals(prerequisites, other.getPrerequisites())
+                && Objects.equals(state, other.getState())
+                && Objects.equals(title, other.getTitle());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinition{" +
+                "id=" + super.getId() +
+                ", title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", prerequisites=" + Arrays.toString(prerequisites) +
+                ", outcomes=" + Arrays.toString(outcomes) +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                ", estimatedDuration=" + estimatedDuration +
+                ", lastEdited=" + lastEdited +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingInstance.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingInstance.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e80535224f2e5f8bcf9774f75c3341a50a5b8a8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingInstance.java
@@ -0,0 +1,288 @@
+package cz.muni.ics.kypo.training.adaptive.domain.training;
+
+import cz.muni.ics.kypo.training.adaptive.domain.AbstractEntity;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Class represents Training instance.
+ * Training instances can be created based on definitions.
+ * Training runs can be created based on instances.
+ */
+@Entity
+@Table(name = "training_instance")
+@NamedEntityGraphs({
+        @NamedEntityGraph(
+                name = "TrainingInstance.findAllAuthorsOrganizers",
+                attributeNodes = {
+                        @NamedAttributeNode(value = "organizers"),
+                        @NamedAttributeNode(value = "trainingDefinition", subgraph = "trainingDefinition"),
+                },
+                subgraphs = {
+                        @NamedSubgraph(name = "trainingDefinition", attributeNodes = {
+                                @NamedAttributeNode(value = "authors"),
+                        }),
+                }
+        ),
+        @NamedEntityGraph(
+                name = "TrainingInstance.findByIdAuthorsOrganizers",
+                attributeNodes = {
+                        @NamedAttributeNode(value = "organizers"),
+                        @NamedAttributeNode(value = "trainingDefinition", subgraph = "trainingDefinition.authors")
+                },
+                subgraphs = {
+                        @NamedSubgraph(name = "trainingDefinition.authors", attributeNodes = @NamedAttributeNode(value = "authors"))
+                }
+        )
+})
+@NamedQueries({
+        @NamedQuery(
+                name = "TrainingInstance.findByStartTimeAfterAndEndTimeBeforeAndAccessToken",
+                query = "SELECT ti FROM TrainingInstance ti " +
+                        "JOIN FETCH ti.trainingDefinition td " +
+                        "WHERE ti.startTime < :datetime AND ti.endTime > :datetime AND ti.accessToken = :accessToken"
+        ),
+        @NamedQuery(
+                name = "TrainingInstance.findByIdIncludingDefinition",
+                query = "SELECT ti FROM TrainingInstance ti " +
+                        "LEFT OUTER JOIN FETCH ti.organizers " +
+                        "JOIN FETCH ti.trainingDefinition td " +
+                        "LEFT OUTER JOIN FETCH td.authors " +
+                        "WHERE ti.id = :instanceId"
+        ),
+        @NamedQuery(
+                name = "TrainingInstance.findAllByTrainingDefinitionId",
+                query = "SELECT ti FROM TrainingInstance ti JOIN FETCH ti.trainingDefinition td WHERE td.id = :trainingDefId"
+        ),
+        @NamedQuery(
+                name = "TrainingInstance.existsAnyForTrainingDefinition",
+                query = "SELECT (COUNT(ti) > 0) FROM TrainingInstance ti " +
+                        "INNER JOIN ti.trainingDefinition td WHERE td.id = :trainingDefinitionId"
+        ),
+        @NamedQuery(
+                name = "TrainingInstance.isFinished",
+                query = "SELECT (COUNT(ti) > 0) FROM TrainingInstance ti WHERE ti.id = :instanceId AND ti.endTime < :currentTime"
+        )
+})
+public class TrainingInstance extends AbstractEntity<Long> {
+
+    @Column(name = "start_time", nullable = false)
+    private LocalDateTime startTime;
+    @Column(name = "end_time", nullable = false)
+    private LocalDateTime endTime;
+    @Column(name = "title", nullable = false)
+    private String title;
+    @Column(name = "pool_id")
+    private Long poolId;
+    @Column(name = "access_token", nullable = false, unique = true)
+    private String accessToken;
+    @ManyToOne(fetch = FetchType.LAZY)
+    private TrainingDefinition trainingDefinition;
+    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
+    @JoinTable(name = "training_instance_user_ref",
+            joinColumns = @JoinColumn(name = "training_instance_id"),
+            inverseJoinColumns = @JoinColumn(name = "user_ref_id")
+    )
+    private Set<UserRef> organizers = new HashSet<>();
+
+    /**
+     * Gets unique identification number of Training instance
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return super.getId();
+    }
+
+    /**
+     * Sets unique identification number of Training instance
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets initiation time of Training instance
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets initiation time of Training instance
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets finish time of Training instance
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets finish time of Training instance
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets access token needed to start Training runs associated with given Training Instance
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token needed to start Training runs associated with given Training Instance
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets title of Training Instance
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title of Training Instance
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets unique identification number of sandbox pool associated with given Training instance
+     *
+     * @return the pool id
+     */
+    public Long getPoolId() {
+        return poolId;
+    }
+
+    /**
+     * Sets unique identification number of sandbox pool associated with given Training instance
+     *
+     * @param poolId the pool id
+     */
+    public void setPoolId(Long poolId) {
+        this.poolId = poolId;
+    }
+
+    /**
+     * Gets Training definition associated with given Training instance
+     *
+     * @return the training definition
+     */
+    public TrainingDefinition getTrainingDefinition() {
+        return trainingDefinition;
+    }
+
+    /**
+     * Sets Training definition associated with given Training instance
+     *
+     * @param trainingDefinition the training definition
+     */
+    public void setTrainingDefinition(TrainingDefinition trainingDefinition) {
+        this.trainingDefinition = trainingDefinition;
+    }
+
+    /**
+     * Gets set of users that can make changes to the Training instance
+     *
+     * @return the organizers
+     */
+    public Set<UserRef> getOrganizers() {
+        return Collections.unmodifiableSet(organizers);
+    }
+
+    /**
+     * Sets set of users that can make changes to the Training instance
+     *
+     * @param organizers the organizers
+     */
+    public void setOrganizers(Set<UserRef> organizers) {
+        this.organizers = organizers;
+    }
+
+    /**
+     * Adds user to the set of users that can make changes to the Training instance
+     *
+     * @param userRef the user ref
+     */
+    public void addOrganizer(UserRef userRef) {
+        this.organizers.add(userRef);
+        userRef.addTrainingInstance(this);
+    }
+
+    /**
+     * Remove organizers with given ids from the set of organizers.
+     *
+     * @param userRefIds ids of the organizers to be removed.
+     */
+    public void removeOrganizersByUserRefIds(Set<Long> userRefIds) {
+        this.organizers.removeIf(userRef -> userRefIds.contains(userRef.getUserRefId()));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(accessToken, startTime, endTime, title, trainingDefinition);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof TrainingInstance))
+            return false;
+        TrainingInstance other = (TrainingInstance) obj;
+        return Objects.equals(accessToken, other.getAccessToken())
+                && Objects.equals(startTime, other.getStartTime())
+                && Objects.equals(endTime, other.getEndTime())
+                && Objects.equals(title, other.getTitle())
+                && Objects.equals(trainingDefinition, other.getTrainingDefinition());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstance{" +
+                "id=" + super.getId() +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", accessToken='" + accessToken + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingRun.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingRun.java
new file mode 100644
index 0000000000000000000000000000000000000000..95acdfb77bc62b175e916b242aea200528e3d078
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/domain/training/TrainingRun.java
@@ -0,0 +1,417 @@
+package cz.muni.ics.kypo.training.adaptive.domain.training;
+
+import cz.muni.ics.kypo.training.adaptive.domain.AbstractEntity;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.Task;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import org.hibernate.annotations.Type;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * Class represents Training run.
+ * Training runs can be created based on instances.
+ * Training runs are accessed by trainees
+ */
+@Entity
+@Table(name = "training_run")
+@NamedEntityGraphs({
+        @NamedEntityGraph(
+                name = "TrainingRun.findAllParticipantRef",
+                attributeNodes = @NamedAttributeNode(value = "participantRef")
+        ),
+        @NamedEntityGraph(
+                name = "TrainingRun.findByIdParticipantRefTrainingInstance",
+                attributeNodes = {
+                        @NamedAttributeNode(value = "participantRef"),
+                        @NamedAttributeNode(value = "trainingInstance")
+                }
+        )
+})
+@NamedQueries({
+        @NamedQuery(
+                name = "TrainingRun.findRunningTrainingRunOfUser",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "JOIN FETCH tr.trainingInstance ti " +
+                        "JOIN FETCH tr.participantRef pr " +
+                        "JOIN FETCH tr.currentPhase " +
+                        "WHERE ti.accessToken = :accessToken AND pr.userRefId = :userRefId AND tr.sandboxInstanceRefId IS NOT NULL AND tr.state NOT LIKE 'FINISHED'"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findByIdWithPhase",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "JOIN FETCH tr.currentPhase " +
+                        "JOIN FETCH tr.trainingInstance ti " +
+                        "JOIN FETCH ti.trainingDefinition " +
+                        "WHERE tr.id= :trainingRunId",
+                lockMode = LockModeType.PESSIMISTIC_WRITE
+        ),
+        @NamedQuery(
+                name = "TrainingRun.deleteTrainingRunsByTrainingInstance",
+                query = "DELETE FROM TrainingRun tr WHERE tr.trainingInstance.id = :trainingInstanceId"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.existsAnyForTrainingInstance",
+                query = "SELECT (COUNT(tr) > 0) FROM TrainingRun tr INNER JOIN tr.trainingInstance ti WHERE ti.id = :trainingInstanceId"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findAllByParticipantRefId",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "INNER JOIN tr.participantRef pr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "INNER JOIN ti.trainingDefinition " +
+                        "WHERE pr.userRefId = :userRefId"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findAllByTrainingDefinitionIdAndParticipantUserRefId",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "INNER JOIN tr.participantRef pr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "INNER JOIN ti.trainingDefinition td " +
+                        "WHERE td.id = :trainingDefinitionId AND pr.userRefId = :userRefId"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findAllActiveByTrainingInstanceId",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "WHERE ti.id = :trainingInstanceId AND tr.state <> 'ARCHIVED'"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findAllInactiveByTrainingInstanceId",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "WHERE ti.id = :trainingInstanceId AND tr.state = 'ARCHIVED'"
+        ),
+        @NamedQuery(
+                name = "TrainingRun.findAllByTrainingDefinitionId",
+                query = "SELECT tr FROM TrainingRun tr " +
+                        "INNER JOIN tr.trainingInstance ti " +
+                        "INNER JOIN ti.trainingDefinition td " +
+                        "WHERE td.id = :trainingDefinitionId"
+        )
+})
+public class TrainingRun extends AbstractEntity<Long> {
+
+    @Column(name = "start_time", nullable = false)
+    private LocalDateTime startTime;
+    @Column(name = "end_time", nullable = false)
+    private LocalDateTime endTime;
+    @Column(name = "state", length = 128, nullable = false)
+    @Enumerated(EnumType.STRING)
+    private TRState state;
+    @Column(name = "incorrect_answer_count", nullable = false)
+    private int incorrectAnswerCount;
+    @Column(name = "solution_taken", nullable = false)
+    private boolean solutionTaken;
+    @ManyToOne(fetch = FetchType.LAZY, optional = false)
+    @JoinColumn(name = "current_phase_id")
+    private AbstractPhase currentPhase;
+    @ManyToOne(fetch = FetchType.LAZY, optional = true)
+    @JoinColumn(name = "current_task_id")
+    private Task currentTask;
+    @ManyToOne(fetch = FetchType.LAZY, optional = false)
+    private TrainingInstance trainingInstance;
+    @Column(name = "sandbox_instance_ref_id")
+    private Long sandboxInstanceRefId;
+    @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_ref_id", nullable = false)
+    private UserRef participantRef;
+    @Lob
+    @Type(type = "org.hibernate.type.TextType")
+    @Column(name = "questionnaire_responses", nullable = true)
+    private String questionnaireResponses;
+    @Column(name = "phase_answered")
+    private boolean phaseAnswered;
+    @Column(name = "previous_sandbox_instance_ref_id")
+    private Long previousSandboxInstanceRefId;
+
+    /**
+     * Gets unique identification number of Training run
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return super.getId();
+    }
+
+    /**
+     * Sets unique identification number of Training run
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        super.setId(id);
+    }
+
+    /**
+     * Gets initiation time of Training run
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets initiation time of Training run
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets finish time of Training run
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets finish time of Training run
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets completion state of Training run
+     * States are RUNNING, FINISHED, ARCHIVED
+     *
+     * @return the state
+     */
+    public TRState getState() {
+        return state;
+    }
+
+    /**
+     * Sets completion state of Training run
+     * States are RUNNING, FINISHED, ARCHIVED
+     *
+     * @param state the state
+     */
+    public void setState(TRState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets phase that is currently being displayed to the trainee
+     *
+     * @return the current phase
+     */
+    public AbstractPhase getCurrentPhase() {
+        return currentPhase;
+    }
+
+    /**
+     * Sets phase that is currently being displayed to the trainee
+     * Sets default data about phase to training run
+     *
+     * @param currentPhase the current phase
+     */
+    public void setCurrentPhase(AbstractPhase currentPhase) {
+        this.phaseAnswered = currentPhase instanceof InfoPhase;
+        this.solutionTaken = false;
+        this.currentPhase = currentPhase;
+    }
+
+    public Task getCurrentTask() {
+        return currentTask;
+    }
+
+    public void setCurrentTask(Task currentTask) {
+        this.currentTask = currentTask;
+    }
+
+    /**
+     * Gets Training instance associated to Training run
+     *
+     * @return the training instance
+     */
+    public TrainingInstance getTrainingInstance() {
+        return trainingInstance;
+    }
+
+    /**
+     * Sets Training instance associated to Training run
+     *
+     * @param trainingInstance the training instance
+     */
+    public void setTrainingInstance(TrainingInstance trainingInstance) {
+        this.trainingInstance = trainingInstance;
+    }
+
+    /**
+     * Gets id of sandbox instance associated with Training run
+     *
+     * @return the sandbox instance ref id
+     */
+    public Long getSandboxInstanceRefId() {
+        return sandboxInstanceRefId;
+    }
+
+    /**
+     * Sets id of sandbox instance associated with Training run
+     *
+     * @param sandboxInstanceRefId the sandbox instance ref id
+     */
+    public void setSandboxInstanceRefId(Long sandboxInstanceRefId) {
+        this.sandboxInstanceRefId = sandboxInstanceRefId;
+    }
+
+    /**
+     * Gets number of failed attempts by trainee to submit correct answer on current phase
+     *
+     * @return the incorrect answer count
+     */
+    public int getIncorrectAnswerCount() {
+        return incorrectAnswerCount;
+    }
+
+    /**
+     * Sets number of failed attempts trainee can submit on current phase
+     *
+     * @param incorrectAnswerCount the incorrect answer count
+     */
+    public void setIncorrectAnswerCount(int incorrectAnswerCount) {
+        this.incorrectAnswerCount = incorrectAnswerCount;
+    }
+
+    /**
+     * Gets solution was taken on current phase
+     *
+     * @return the boolean
+     */
+    public boolean isSolutionTaken() {
+        return solutionTaken;
+    }
+
+    /**
+     * Sets solution was taken on current phase
+     *
+     * @param solutionTaken the solution taken
+     */
+    public void setSolutionTaken(boolean solutionTaken) {
+        this.solutionTaken = solutionTaken;
+    }
+
+    /**
+     * Gets responses of current assessment phase
+     *
+     * @return the assessment responses
+     */
+    public String getQuestionnaireResponses() {
+        return questionnaireResponses;
+    }
+
+    /**
+     * Sets responses of current assessment phase
+     *
+     * @param assessmentResponses the assessment responses
+     */
+    public void setQuestionnaireResponses(String assessmentResponses) {
+        this.questionnaireResponses = assessmentResponses;
+    }
+
+    /**
+     * Gets DB reference of trainee
+     *
+     * @return the participant ref
+     */
+    public UserRef getParticipantRef() {
+        return participantRef;
+    }
+
+    /**
+     * Sets DB reference of trainee
+     *
+     * @param participantRef the participant ref
+     */
+    public void setParticipantRef(UserRef participantRef) {
+        this.participantRef = participantRef;
+    }
+
+    /**
+     * Gets if phase was answered
+     *
+     * @return the boolean
+     */
+    public boolean isPhaseAnswered() {
+        return phaseAnswered;
+    }
+
+    /**
+     * Sets if phase was answered
+     *
+     * @param phaseAnswered the phase answered
+     */
+    public void setPhaseAnswered(boolean phaseAnswered) {
+        this.phaseAnswered = phaseAnswered;
+    }
+
+    /**
+     * Gets id of previous sandbox instance ref assigned by training run.
+     *
+     * @return the id of previous sandbox instance ref
+     */
+    public Long getPreviousSandboxInstanceRefId() {
+        return previousSandboxInstanceRefId;
+    }
+
+    /**
+     * Sets previous sandbox instance ref ID
+     *
+     * @param previousSandboxInstanceRefId the id of previous sandbox instance ref
+     */
+    public void setPreviousSandboxInstanceRefId(Long previousSandboxInstanceRefId) {
+        this.previousSandboxInstanceRefId = previousSandboxInstanceRefId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(currentPhase, startTime, endTime, state, trainingInstance, incorrectAnswerCount);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof TrainingRun))
+            return false;
+        TrainingRun other = (TrainingRun) obj;
+        return Objects.equals(currentPhase, other.getCurrentPhase())
+                && Objects.equals(startTime, other.getStartTime())
+                && Objects.equals(endTime, other.getEndTime())
+                && Objects.equals(state, other.getState())
+                && Objects.equals(incorrectAnswerCount, other.getIncorrectAnswerCount())
+                && Objects.equals(trainingInstance, other.getTrainingInstance())
+                && Objects.equals(participantRef, other.getParticipantRef())
+                && Objects.equals(solutionTaken, other.isSolutionTaken());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingRun{" +
+                "id=" + super.getId() +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", state=" + state +
+                ", incorrectAnswerCount=" + incorrectAnswerCount +
+                ", solutionTaken=" + solutionTaken +
+                ", currentPhase=" + currentPhase +
+                ", sandboxInstanceRefId=" + sandboxInstanceRefId +
+                ", phaseAnswered=" + phaseAnswered +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/AuditInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/AuditInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..2502bf89801c52a787882ca27923ee481a3b39ea
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/AuditInfoDTO.java
@@ -0,0 +1,180 @@
+package cz.muni.ics.kypo.training.adaptive.dto;
+
+/**
+ * Encapsulates information used for auditing.
+ */
+public class AuditInfoDTO {
+
+    private long userRefId;
+    private long sandboxId;
+    private long poolId;
+    private long trainingRunId;
+    private long trainingDefinitionId;
+    private long trainingInstanceId;
+    private long trainingTime;
+    private long phase;
+
+    /**
+     * Instantiates a new Audit info dto.
+     */
+    public AuditInfoDTO() {
+    }
+
+    /**
+     * Gets user ref id.
+     *
+     * @return the user ref id
+     */
+    public long getUserRefId() {
+        return userRefId;
+    }
+
+    /**
+     * Sets user ref id.
+     *
+     * @param userRefId the user ref id
+     */
+    public void setUserRefId(long userRefId) {
+        this.userRefId = userRefId;
+    }
+
+    /**
+     * Gets sandbox id.
+     *
+     * @return the sandbox id
+     */
+    public long getSandboxId() {
+        return sandboxId;
+    }
+
+    /**
+     * Sets sandbox id.
+     *
+     * @param sandboxId the sandbox id
+     */
+    public void setSandboxId(long sandboxId) {
+        this.sandboxId = sandboxId;
+    }
+
+    /**
+     * Gets pool id.
+     *
+     * @return the pool id
+     */
+    public long getPoolId() {
+        return poolId;
+    }
+
+    /**
+     * Sets pool id.
+     *
+     * @param poolId the pool id
+     */
+    public void setPoolId(long poolId) {
+        this.poolId = poolId;
+    }
+
+    /**
+     * Gets training run id.
+     *
+     * @return the training run id
+     */
+    public long getTrainingRunId() {
+        return trainingRunId;
+    }
+
+    /**
+     * Sets training run id.
+     *
+     * @param trainingRunId the training run id
+     */
+    public void setTrainingRunId(long trainingRunId) {
+        this.trainingRunId = trainingRunId;
+    }
+
+    /**
+     * Gets training definition id.
+     *
+     * @return the training definition id
+     */
+    public long getTrainingDefinitionId() {
+        return trainingDefinitionId;
+    }
+
+    /**
+     * Sets training definition id.
+     *
+     * @param trainingDefinitionId the training definition id
+     */
+    public void setTrainingDefinitionId(long trainingDefinitionId) {
+        this.trainingDefinitionId = trainingDefinitionId;
+    }
+
+    /**
+     * Gets training instance id.
+     *
+     * @return the training instance id
+     */
+    public long getTrainingInstanceId() {
+        return trainingInstanceId;
+    }
+
+    /**
+     * Sets training instance id.
+     *
+     * @param trainingInstanceId the training instance id
+     */
+    public void setTrainingInstanceId(long trainingInstanceId) {
+        this.trainingInstanceId = trainingInstanceId;
+    }
+
+    /**
+     * Gets training time.
+     *
+     * @return the training time
+     */
+    public long getTrainingTime() {
+        return trainingTime;
+    }
+
+    /**
+     * Sets training time.
+     *
+     * @param trainingTime the training time
+     */
+    public void setTrainingTime(long trainingTime) {
+        this.trainingTime = trainingTime;
+    }
+
+    /**
+     * Gets phase.
+     *
+     * @return the phase
+     */
+    public long getPhase() {
+        return phase;
+    }
+
+    /**
+     * Sets phase.
+     *
+     * @param phase the phase
+     */
+    public void setPhase(long phase) {
+        this.phase = phase;
+    }
+
+    @Override
+    public String toString() {
+        return "AuditInfoDTO{" +
+                "userRefId=" + userRefId +
+                ", sandboxId=" + sandboxId +
+                ", poolId=" + poolId +
+                ", trainingRunId=" + trainingRunId +
+                ", trainingDefinitionId=" + trainingDefinitionId +
+                ", trainingInstanceId=" + trainingInstanceId +
+                ", trainingTime=" + trainingTime +
+                ", phase=" + phase +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/BasicPhaseInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/BasicPhaseInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..266b2bb303d0184d8d96ba2bc32d649cfe22b3ba
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/BasicPhaseInfoDTO.java
@@ -0,0 +1,145 @@
+package cz.muni.ics.kypo.training.adaptive.dto;
+
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates basic information about phase.
+ */
+@ApiModel(value = "BasicPhaseInfoDTO", description = "Basic information about the phase and its type.")
+public class BasicPhaseInfoDTO {
+
+    @ApiModelProperty(value = "Main identifier of phase.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Short textual description of the phase.", example = "Training phase")
+    private String title;
+    @ApiModelProperty(value = "Order of phase among phase in training definition.", example = "1")
+    private int order;
+    @ApiModelProperty(value = "Type of the phase.", example = "TRAINING")
+    private PhaseType phaseType;
+
+    /**
+     * Instantiates a new Basic phase info dto.
+     */
+    public BasicPhaseInfoDTO() {
+    }
+
+    /**
+     * Instantiates a new Basic phase info dto.
+     *
+     * @param id        the id
+     * @param title     the title
+     * @param phaseType the phase type
+     * @param order     the order
+     */
+    public BasicPhaseInfoDTO(Long id, String title, PhaseType phaseType, int order) {
+        this.id = id;
+        this.title = title;
+        this.phaseType = phaseType;
+        this.order = order;
+    }
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets order number of phase that is compared with order numbers of other phase associated with same definition.
+     * First phase from definition has order of 0
+     *
+     * @return the order
+     */
+    public int getOrder() {
+        return order;
+    }
+
+    /**
+     * Sets order number of phase that is compared with order numbers of other phase associated with same definition.
+     * First phase from definition has order of 0
+     *
+     * @param order the order
+     */
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    /**
+     * Gets phase type.
+     *
+     * @return the {@link PhaseType}
+     */
+    public PhaseType getPhaseType() {
+        return phaseType;
+    }
+
+    /**
+     * Sets phase type.
+     *
+     * @param phaseType the {@link PhaseType}
+     */
+    public void setPhaseType(PhaseType phaseType) {
+        this.phaseType = phaseType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        BasicPhaseInfoDTO that = (BasicPhaseInfoDTO) o;
+        return order == that.order &&
+                Objects.equals(id, that.id) &&
+                Objects.equals(title, that.title) &&
+                phaseType == that.phaseType;
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(id, title, order, phaseType);
+    }
+
+    @Override
+    public String toString() {
+        return "BasicPhaseInfoDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", order=" + order +
+                ", phaseType=" + phaseType +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/IsCorrectAnswerDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/IsCorrectAnswerDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..65210a7d5011d031ac62521eee1c39af93ab3214
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/IsCorrectAnswerDTO.java
@@ -0,0 +1,97 @@
+package cz.muni.ics.kypo.training.adaptive.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Response to attempt of answer input
+ */
+@ApiModel(value = "IsCorrectAnswerDTO", description = "A response for the request about the validation of the task answer. May also " +
+        "include solution if remaining attempts reach 0.")
+public class IsCorrectAnswerDTO {
+
+    @ApiModelProperty(value = "True/false if answer has been correct/incorrect.", example = "false")
+    private boolean isCorrect;
+    @ApiModelProperty(value = "Number of attempts to submit a bad answer.", example = "3")
+    private int remainingAttempts;
+    @ApiModelProperty(value = "Instruction how to get answer in training.", example = "This is how you do it")
+    private String solution;
+
+    /**
+     * Is correct boolean.
+     *
+     * @return True if answer is correct
+     */
+    public boolean isCorrect() {
+        return isCorrect;
+    }
+
+    /**
+     * Sets correct.
+     *
+     * @param correct True if answer is correct
+     */
+    public void setCorrect(boolean correct) {
+        isCorrect = correct;
+    }
+
+    /**
+     * Gets remaining attempts.
+     *
+     * @return the remaining attempts
+     */
+    public int getRemainingAttempts() {
+        return remainingAttempts;
+    }
+
+    /**
+     * Sets remaining attempts.
+     *
+     * @param remainingAttempts the remaining attempts
+     */
+    public void setRemainingAttempts(int remainingAttempts) {
+        this.remainingAttempts = remainingAttempts;
+    }
+
+    /**
+     * Gets solution.
+     *
+     * @return the solution
+     */
+    public String getSolution() {
+        return solution;
+    }
+
+    /**
+     * Sets solution.
+     *
+     * @param solution the solution
+     */
+    public void setSolution(String solution) {
+        this.solution = solution;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof IsCorrectAnswerDTO)) return false;
+        IsCorrectAnswerDTO that = (IsCorrectAnswerDTO) object;
+        return isCorrect() == that.isCorrect() &&
+                getRemainingAttempts() == that.getRemainingAttempts() &&
+                Objects.equals(getSolution(), that.getSolution());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(isCorrect(), getRemainingAttempts(), getSolution());
+    }
+
+    @Override
+    public String toString() {
+        return "IsCorrectAnswerDTO{" +
+                "isCorrect=" + isCorrect +
+                ", remainingAttempts=" + remainingAttempts +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/PhaseCreateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/PhaseCreateDTO.java
index f994fa3bd63d279a0120887362082827ea66dac4..db7025ce5e622f8ddf47f5951a12516b0aae9156 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/PhaseCreateDTO.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/PhaseCreateDTO.java
@@ -1,25 +1,39 @@
 package cz.muni.ics.kypo.training.adaptive.dto;
 
-import cz.muni.ics.kypo.training.adaptive.enums.PhaseTypeCreate;
+import cz.muni.ics.kypo.training.adaptive.annotations.validation.NotNullQuestionnaireType;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
 import io.swagger.annotations.ApiModelProperty;
 
 import javax.validation.constraints.NotNull;
 import java.util.Objects;
 
+@NotNullQuestionnaireType
 public class PhaseCreateDTO {
 
-    @ApiModelProperty(value = "Type of phase.", required = true, allowableValues = "QUESTIONNAIRE_ADAPTIVE, QUESTIONNAIRE_GENERAL, INFO, GAME", example = "TRAINING")
+    @ApiModelProperty(value = "Type of phase.", required = true, allowableValues = "QUESTIONNAIRE, INFO, TRAINING", example = "TRAINING")
     @NotNull(message = "Phase type must be specified")
-    private PhaseTypeCreate phaseType;
+    private PhaseType phaseType;
 
-    public PhaseTypeCreate getPhaseType() {
+    @ApiModelProperty(value = "Type of questionnaire.", allowableValues = "ADAPTIVE, GENERAL", example = "ADAPTIVE")
+    private QuestionnaireType questionnaireType;
+
+    public PhaseType getPhaseType() {
         return phaseType;
     }
 
-    public void setPhaseType(PhaseTypeCreate phaseType) {
+    public void setPhaseType(PhaseType phaseType) {
         this.phaseType = phaseType;
     }
 
+    public QuestionnaireType getQuestionnaireType() {
+        return questionnaireType;
+    }
+
+    public void setQuestionnaireType(QuestionnaireType questionnaireType) {
+        this.questionnaireType = questionnaireType;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -37,6 +51,7 @@ public class PhaseCreateDTO {
     public String toString() {
         return "PhaseCreateDTO{" +
                 "phaseType=" + phaseType +
+                ", questionnaireType=" + questionnaireType +
                 '}';
     }
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/UserRefDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/UserRefDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9fccf01725884887126e335168b2a73eb4a2080
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/UserRefDTO.java
@@ -0,0 +1,194 @@
+package cz.muni.ics.kypo.training.adaptive.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates information about user reference.
+ */
+@ApiModel(value = "UserRefDTO", description = "User information from user-and-group microservice is mapped to this class " +
+        "and is also used to provide information about authors, participants, and organizers.")
+public class UserRefDTO {
+
+    @ApiModelProperty(value = "Reference to user in another microservice and get his id", example = "1")
+    private Long userRefId;
+    @ApiModelProperty(value = "Reference to user in another microservice.", example = "441048@mail.muni.cz")
+    @JsonProperty("sub")
+    private String userRefSub;
+    @ApiModelProperty(value = "Reference to user in another microservice and get his full name", example = "Mgr. Ing. Pavel Ĺ eda")
+    @JsonProperty("full_name")
+    private String userRefFullName;
+    @ApiModelProperty(value = "User given name", example = "Pavel")
+    @JsonProperty("given_name")
+    private String userRefGivenName;
+    @ApiModelProperty(value = "User family name", example = "Seda")
+    @JsonProperty("family_name")
+    private String userRefFamilyName;
+    @ApiModelProperty(value = "Reference to user in another microservice and get his iss", example = "https://oidc.muni.cz")
+    private String iss;
+    @ApiModelProperty(value = "Identicon of a user.", example = "iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAYAAAA4TnrqAAACIUlEQVR4Xu3YsY0dSQxAQQUlpXT5Z3CS/YgxSrQa4gLlEOBb9pj/x6//fv7/t/78/XhN3yBWyz3kBX2DWC33kBf0DWK13ENe0DeI1XIPeUHfIFbLPeQFfYNYLfeQF/QNYrXcQ17QN4jVcg95Qd8gVss95AV9g1gt95AX9A1itdxDXtA3iNVyD3lB3yBWyz3kBX2DWC33kBf0DWLERGOiLdGWaEuMgeghoi3RlmhLjIHoIaIt0ZZoS4yB6CGiLdGWaEuMgeghoi3RlmhLjIHoIaIt0ZZoS4yB6CGiLdGWaEuMgeghoi3RlmhLjIHoIaIt0ZZoS4yB6CGiLdGWaEuMgeghoi3RlmhLjIHoIaIt0ZZoS4yB6CGiLdGWaEuMgeghoi3RlmhLjIHoIaIt0ZZoS6z+8b/mPha4jwXuY4H7WOA+FriPBe5jgftY4D4WuI8F7mOB+1jgPha4jwXGbzbn2xicb2Nwvo3B+TYG59sYnG9jcL6Nwfk2BufbGJxvY3C+jcH5Ngbn2xicb2Nwvq1+z2pMtCXaEm2J1XIPEW2JtkRbYrXcQ0Rboi3Rllgt9xDRlmhLtCVWyz1EtCXaEm2J1XIPEW2JtkRbYrXcQ0Rboi3Rllgt9xDRlmhLtCVWyz1EtCXaEm2J1XIPEW2JtkRbYrXcQ0Rboi3Rllgt9xDRlmhLtCVWyz1EtCXaEm2J1XIPEW2JtkRbYrXcQ0Rboi3RlvgNt34wfeJElG8AAAAASUVORK5CYII=")
+    private byte[] picture;
+
+    /**
+     * Gets user ref sub.
+     *
+     * @return the user ref sub
+     */
+    public String getUserRefSub() {
+        return userRefSub;
+    }
+
+    /**
+     * Sets user ref sub.
+     *
+     * @param userRefSub the user ref sub
+     */
+    public void setUserRefSub(String userRefSub) {
+        this.userRefSub = userRefSub;
+    }
+
+    /**
+     * Gets iss.
+     *
+     * @return the iss
+     */
+    public String getIss() {
+        return iss;
+    }
+
+    /**
+     * Sets iss.
+     *
+     * @param iss the iss
+     */
+    public void setIss(String iss) {
+        this.iss = iss;
+    }
+
+    /**
+     * Gets user ref id.
+     *
+     * @return the user ref id
+     */
+    @JsonProperty("user_ref_id")
+    public Long getUserRefId() {
+        return userRefId;
+    }
+
+    /**
+     * Sets user ref id.
+     *
+     * @param userRefId the user ref id
+     */
+    @JsonAlias({"id", "user_ref_id"})
+    public void setUserRefId(Long userRefId) {
+        this.userRefId = userRefId;
+    }
+
+    /**
+     * Gets user ref full name.
+     *
+     * @return the user ref full name
+     */
+    public String getUserRefFullName() {
+        return userRefFullName;
+    }
+
+    /**
+     * Sets user ref full name.
+     *
+     * @param userRefFullName the user ref full name
+     */
+    public void setUserRefFullName(String userRefFullName) {
+        this.userRefFullName = userRefFullName;
+    }
+
+    /**
+     * Gets user ref given name.
+     *
+     * @return the user ref given name
+     */
+    public String getUserRefGivenName() {
+        return userRefGivenName;
+    }
+
+    /**
+     * Sets user ref given name.
+     *
+     * @param userRefGivenName the user ref given name
+     */
+    public void setUserRefGivenName(String userRefGivenName) {
+        this.userRefGivenName = userRefGivenName;
+    }
+
+    /**
+     * Gets user ref family name.
+     *
+     * @return the user ref family name
+     */
+    public String getUserRefFamilyName() {
+        return userRefFamilyName;
+    }
+
+    /**
+     * Sets user ref family name.
+     *
+     * @param userRefFamilyName the user ref family name
+     */
+    public void setUserRefFamilyName(String userRefFamilyName) {
+        this.userRefFamilyName = userRefFamilyName;
+    }
+
+    /**
+     * Gets the identicon of the user encoded in base64.
+     *
+     * @return identicon of the user.
+     */
+    public byte[] getPicture() {
+        return picture;
+    }
+
+    /**
+     * Sets the identicon of the user encoded in base64.
+     *
+     * @param picture encoded identicon of the user.
+     */
+    public void setPicture(byte[] picture) {
+        this.picture = picture;
+    }
+
+
+    @Override
+    public String toString() {
+        return "UserRefDTO{" +
+                ", userRefSub='" + userRefSub + '\'' +
+                ", userRefFullName='" + userRefFullName + '\'' +
+                ", userRefGivenName='" + userRefGivenName + '\'' +
+                ", userRefFamilyName='" + userRefFamilyName + '\'' +
+                ", iss='" + iss + '\'' +
+                ", userRefId=" + userRefId +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof UserRefDTO)) return false;
+        UserRefDTO that = (UserRefDTO) o;
+        return Objects.equals(getUserRefId(), that.getUserRefId()) &&
+                Objects.equals(getUserRefSub(), that.getUserRefSub()) &&
+                Objects.equals(getUserRefFullName(), that.getUserRefFullName()) &&
+                Objects.equals(getUserRefGivenName(), that.getUserRefGivenName()) &&
+                Objects.equals(getUserRefFamilyName(), that.getUserRefFamilyName()) &&
+                Objects.equals(getIss(), that.getIss());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUserRefId(), getUserRefSub(), getUserRefFullName(), getUserRefGivenName(), getUserRefFamilyName(), getIss());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/AbstractPhaseArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/AbstractPhaseArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..a89740dec9f37d78f5da846c9072f4e724d0a508
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/AbstractPhaseArchiveDTO.java
@@ -0,0 +1,114 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.info.InfoPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire.QuestionnairePhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training.TrainingPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Encapsulates information about abstract phase.
+ * Used for archiving.
+ * Extended by {@link QuestionnairePhaseArchiveDTO}, {@link TrainingPhaseArchiveDTO} and {@link InfoPhaseArchiveDTO}.
+ */
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = TrainingPhaseArchiveDTO.class, name = "TrainingPhaseArchiveDTO"),
+        @JsonSubTypes.Type(value = QuestionnairePhaseArchiveDTO.class, name = "QuestionnairePhaseArchiveDTO"),
+        @JsonSubTypes.Type(value = InfoPhaseArchiveDTO.class, name = "InfoPhaseArchiveDTO")})
+@ApiModel(value = "AbstractPhaseArchiveDTO", subTypes = {TrainingPhaseArchiveDTO.class, InfoPhaseArchiveDTO.class, QuestionnairePhaseArchiveDTO.class},
+        description = "Superclass for classes TrainingPhaseArchiveDTO, InfoPhaseArchiveDTO and QuestionnairePhaseArchiveDTO")
+public abstract class AbstractPhaseArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of phase.", example = "1")
+    protected Long id;
+    @ApiModelProperty(value = "Short textual description of the phase.", example = "Training phase")
+    protected String title;
+    @ApiModelProperty(value = "Type of the phase.", example = "TRAINING")
+    protected PhaseType phaseType;
+    @ApiModelProperty(value = "Order of phase, starts with 0", example = "2")
+    protected Integer order;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets phase type.
+     *
+     * @return the phase type
+     */
+    public PhaseType getPhaseType() {
+        return phaseType;
+    }
+
+    /**
+     * Sets phase type.
+     *
+     * @param phaseType the phase type
+     */
+    public void setPhaseType(PhaseType phaseType) {
+        this.phaseType = phaseType;
+    }
+
+    /**
+     * Gets order.
+     *
+     * @return the order
+     */
+    public int getOrder() {
+        return order;
+    }
+
+    /**
+     * Sets order.
+     *
+     * @param order the order
+     */
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractPhaseArchiveDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/info/InfoPhaseArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/info/InfoPhaseArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..544e73938dd83392c07613bcfbaead0e0768c91a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/info/InfoPhaseArchiveDTO.java
@@ -0,0 +1,56 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.info;
+
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates information about info phase. Inherits from {@link AbstractPhaseArchiveDTO}
+ * Used for archiving.
+ */
+@ApiModel(value = "InfoPhaseArchiveDTO", description = "Archived info phase.", parent = AbstractPhaseArchiveDTO.class)
+public class InfoPhaseArchiveDTO extends AbstractPhaseArchiveDTO {
+
+    @ApiModelProperty(value = "The information and experiences that are directed towards a participant.", example = "Informational stuff")
+    private String content;
+
+    /**
+     * Gets content.
+     *
+     * @return the content
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * Sets content.
+     *
+     * @param content the content
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof InfoPhaseArchiveDTO)) return false;
+        InfoPhaseArchiveDTO that = (InfoPhaseArchiveDTO) o;
+        return Objects.equals(getContent(), that.getContent());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getContent());
+    }
+
+    @Override
+    public String toString() {
+        return "InfoPhaseArchiveDTO{" +
+                "content='" + content + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e41021fd2a5210f58e2c661a2acb24c87d5f416
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionArchiveDTO.java
@@ -0,0 +1,77 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionType;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+import java.util.Objects;
+
+public class QuestionArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of question.", example = "1")
+    protected Long id;
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    private int order;
+    @ApiModelProperty(value = "The question that will be displayed to a player", required = true, example = "What's the capital of Canada?")
+    private String text;
+    @ApiModelProperty(value = "It defines the type of the question", allowableValues = "FFQ, MCQ, RFQ", required = true, example = "MCQ")
+    private QuestionType questionType;
+    @ApiModelProperty(value = "Choices that are distributed with the question", required = true)
+    private List<QuestionChoiceArchiveDTO> choices;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public QuestionType getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(QuestionType type) {
+        this.questionType = type;
+    }
+
+    public List<QuestionChoiceArchiveDTO> getChoices() {
+        return choices;
+    }
+
+    public void setChoices(List<QuestionChoiceArchiveDTO> choices) {
+        this.choices = choices;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionArchiveDTO)) return false;
+        QuestionArchiveDTO that = (QuestionArchiveDTO) o;
+        return getOrder() == that.getOrder() &&
+                Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getText(), that.getText()) &&
+                getQuestionType() == that.getQuestionType();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getOrder(), getText(), getQuestionType());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionChoiceArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionChoiceArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d81161304e183cac3dad0a411f10e8179d531a86
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionChoiceArchiveDTO.java
@@ -0,0 +1,75 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+public class QuestionChoiceArchiveDTO {
+
+    @ApiModelProperty(value = "Question choice ID. Leave blank if new choice is added", required = true, example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Short description of question choice", required = true, example = "An answer")
+    private String text;
+    @ApiModelProperty(value = "It defines whether this answer is correct or not", required = true, example = "true")
+    private Boolean correct;
+    @ApiModelProperty(value = "Order of question choice", required = true, example = "0")
+    private Integer order;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public Boolean isCorrect() {
+        return correct;
+    }
+
+    public void setCorrect(Boolean correct) {
+        this.correct = correct;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionChoiceArchiveDTO)) return false;
+        QuestionChoiceArchiveDTO that = (QuestionChoiceArchiveDTO) o;
+        return Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getText(), that.getText()) &&
+                Objects.equals(correct, that.correct) &&
+                Objects.equals(getOrder(), that.getOrder());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getText(), correct, getOrder());
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionChoiceArchiveDTO{" +
+                "id=" + id +
+                ", text='" + text + '\'' +
+                ", correct=" + correct +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionPhaseRelationArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionPhaseRelationArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..22481f69f580e8f0d0f328b920db6ed7a4f79610
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionPhaseRelationArchiveDTO.java
@@ -0,0 +1,59 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Set;
+
+public class QuestionPhaseRelationArchiveDTO {
+
+    @ApiModelProperty(value = "Question-Phase relation ID. Leave blank if a new one is added", required = true, example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    private Integer order;
+    @ApiModelProperty(value = "Set of IDs of questions related to the specified questionnaire")
+    private Set<Long> questionIds;
+    @ApiModelProperty(value = "ID of training phase to which the questions are related of question", required = true, example = "1")
+    private Long phaseId;
+    @ApiModelProperty(value = "Percentage that defines whether a player was successful or not ", required = true, example = "50")
+    private int successRate;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public Set<Long> getQuestionIds() {
+        return questionIds;
+    }
+
+    public void setQuestionIds(Set<Long> questionIds) {
+        this.questionIds = questionIds;
+    }
+
+    public Long getPhaseId() {
+        return phaseId;
+    }
+
+    public void setPhaseId(Long phaseId) {
+        this.phaseId = phaseId;
+    }
+
+    public int getSuccessRate() {
+        return successRate;
+    }
+
+    public void setSuccessRate(int successRate) {
+        this.successRate = successRate;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionnairePhaseArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionnairePhaseArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9dbfb68595793aaa0a32ab0f3bdfa169f18b3d97
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/questionnaire/QuestionnairePhaseArchiveDTO.java
@@ -0,0 +1,72 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionPhaseRelationDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
+import io.swagger.annotations.ApiModel;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about c phase. Inherits from {@link AbstractPhaseArchiveDTO}
+ * Used for archiving.
+ */
+@ApiModel(value = "QuestionnairePhaseArchiveDTO", description = "Archived questionnaire phase.", parent = AbstractPhaseArchiveDTO.class)
+public class QuestionnairePhaseArchiveDTO extends AbstractPhaseArchiveDTO {
+
+    private List<QuestionDTO> questions;
+    private QuestionnaireType questionnaireType;
+    private List<QuestionPhaseRelationDTO> phaseRelations;
+
+    public List<QuestionDTO> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionDTO> questions) {
+        this.questions = questions;
+    }
+
+    public QuestionnaireType getQuestionnaireType() {
+        return questionnaireType;
+    }
+
+    public void setQuestionnaireType(QuestionnaireType questionnaireType) {
+        this.questionnaireType = questionnaireType;
+    }
+
+    public List<QuestionPhaseRelationDTO> getPhaseRelations() {
+        return phaseRelations;
+    }
+
+    public void setPhaseRelations(List<QuestionPhaseRelationDTO> phaseRelations) {
+        this.phaseRelations = phaseRelations;
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionnairePhaseArchiveDTO{" +
+                "questionnaireType=" + questionnaireType +
+                ", id=" + id +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionnairePhaseArchiveDTO)) return false;
+        QuestionnairePhaseArchiveDTO that = (QuestionnairePhaseArchiveDTO) o;
+        return Objects.equals(getQuestions(), that.getQuestions()) &&
+                getQuestionnaireType() == that.getQuestionnaireType() &&
+                Objects.equals(getPhaseRelations(), that.getPhaseRelations());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getQuestions(), getQuestionnaireType(), getPhaseRelations());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/DecisionMatrixRowArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/DecisionMatrixRowArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5609945243d7bd56607683c9897fc4e6d831c93c
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/DecisionMatrixRowArchiveDTO.java
@@ -0,0 +1,80 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training;
+
+public class DecisionMatrixRowArchiveDTO {
+    private long id;
+    private int order;
+    private double questionnaireAnswered;
+    private double keywordUsed;
+    private double completedInTime;
+    private double solutionDisplayed;
+    private double wrongAnswers;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public double getQuestionnaireAnswered() {
+        return questionnaireAnswered;
+    }
+
+    public void setQuestionnaireAnswered(double questionnaireAnswered) {
+        this.questionnaireAnswered = questionnaireAnswered;
+    }
+
+    public double getKeywordUsed() {
+        return keywordUsed;
+    }
+
+    public void setKeywordUsed(double keywordUsed) {
+        this.keywordUsed = keywordUsed;
+    }
+
+    public double getCompletedInTime() {
+        return completedInTime;
+    }
+
+    public void setCompletedInTime(double completedInTime) {
+        this.completedInTime = completedInTime;
+    }
+
+    public double getSolutionDisplayed() {
+        return solutionDisplayed;
+    }
+
+    public void setSolutionDisplayed(double solutionDisplayed) {
+        this.solutionDisplayed = solutionDisplayed;
+    }
+
+    public double getWrongAnswers() {
+        return wrongAnswers;
+    }
+
+    public void setWrongAnswers(double wrongAnswers) {
+        this.wrongAnswers = wrongAnswers;
+    }
+
+    @Override
+    public String toString() {
+        return "DecisionMatrixRow{" +
+                "id=" + id +
+                ", order=" + order +
+                ", questionnaireAnswered=" + questionnaireAnswered +
+                ", keywordUsed=" + keywordUsed +
+                ", completedInTime=" + completedInTime +
+                ", solutionDisplayed=" + solutionDisplayed +
+                ", wrongAnswers=" + wrongAnswers +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TaskArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TaskArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..df6aad099e7604c24d2f3aea7c04a2afb270beac
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TaskArchiveDTO.java
@@ -0,0 +1,111 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class TaskArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of task.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Short description of task", required = true, example = "Task title")
+    private String title;
+    private Integer order;
+    @ApiModelProperty(value = "The information that are displayed to a player", required = true, example = "Capture the flag")
+    private String content;
+    @ApiModelProperty(value = "Keyword that must be found in the task. Necessary in order to get to the next phase", required = true, example = "secretFlag")
+    private String answer;
+    @ApiModelProperty(value = "Description how to get the answer", required = true, example = "Open secret.txt")
+    private String solution;
+    @ApiModelProperty(value = "It defines the allowed number of incorrect answers submitted by the player", required = true, example = "5")
+    private int incorrectAnswerLimit;
+    @ApiModelProperty(value = "It defines whether the sandbox can be modified", example = "true")
+    private boolean modifySandbox;
+    @ApiModelProperty(value = "It defines the expected duration of sandbox change defined in seconds", example = "15")
+    private int sandboxChangeExpectedDuration;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public String getSolution() {
+        return solution;
+    }
+
+    public void setSolution(String solution) {
+        this.solution = solution;
+    }
+
+    public int getIncorrectAnswerLimit() {
+        return incorrectAnswerLimit;
+    }
+
+    public void setIncorrectAnswerLimit(int incorrectAnswerLimit) {
+        this.incorrectAnswerLimit = incorrectAnswerLimit;
+    }
+
+    public boolean isModifySandbox() {
+        return modifySandbox;
+    }
+
+    public void setModifySandbox(boolean modifySandbox) {
+        this.modifySandbox = modifySandbox;
+    }
+
+    public int getSandboxChangeExpectedDuration() {
+        return sandboxChangeExpectedDuration;
+    }
+
+    public void setSandboxChangeExpectedDuration(int sandboxChangeExpectedDuration) {
+        this.sandboxChangeExpectedDuration = sandboxChangeExpectedDuration;
+    }
+
+    @Override
+    public String toString() {
+        return "TaskDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", order=" + order +
+                ", content='" + content + '\'' +
+                ", answer='" + answer + '\'' +
+                ", solution='" + solution + '\'' +
+                ", incorrectAnswerLimit=" + incorrectAnswerLimit +
+                ", isSandboxModified=" + modifySandbox +
+                ", sandboxChangeExpectedDuration=" + sandboxChangeExpectedDuration +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TrainingPhaseArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TrainingPhaseArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..b390ada0fb9bb10701428f253f26260f35caf0e5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/phases/training/TrainingPhaseArchiveDTO.java
@@ -0,0 +1,78 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training;
+
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates information about training phase. Inherits from {@link AbstractPhaseArchiveDTO}
+ * Used for archiving.
+ */
+@ApiModel(value = "TrainingPhaseArchiveDTO", description = "Archived training phase.", parent = AbstractPhaseArchiveDTO.class)
+public class TrainingPhaseArchiveDTO extends AbstractPhaseArchiveDTO {
+
+    @ApiModelProperty(value = "Estimated time (minutes) taken by the player to solve the training phase", example = "20")
+    private int estimatedDuration;
+    @ApiModelProperty(value = "Maximal number of allowed commands provided by played", required = true, example = "10")
+    private int allowedCommands;
+    @ApiModelProperty(value = "Maximal number of allowed wrong answers provided by played", required = true, example = "10")
+    private int allowedWrongAnswers;
+    private List<TaskDTO> tasks = new ArrayList<>();
+
+    private List<DecisionMatrixRowArchiveDTO> decisionMatrix;
+
+    public int getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    public void setEstimatedDuration(int estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    public int getAllowedCommands() {
+        return allowedCommands;
+    }
+
+    public void setAllowedCommands(int allowedCommands) {
+        this.allowedCommands = allowedCommands;
+    }
+
+    public int getAllowedWrongAnswers() {
+        return allowedWrongAnswers;
+    }
+
+    public void setAllowedWrongAnswers(int allowedWrongAnswers) {
+        this.allowedWrongAnswers = allowedWrongAnswers;
+    }
+
+    public List<TaskDTO> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(List<TaskDTO> tasks) {
+        this.tasks = tasks;
+    }
+
+    public List<DecisionMatrixRowArchiveDTO> getDecisionMatrix() {
+        return decisionMatrix;
+    }
+
+    public void setDecisionMatrix(List<DecisionMatrixRowArchiveDTO> decisionMatrix) {
+        this.decisionMatrix = decisionMatrix;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingPhaseArchiveDTO{" +
+                "estimatedDuration=" + estimatedDuration +
+                ", allowedCommands=" + allowedCommands +
+                ", allowedWrongAnswers=" + allowedWrongAnswers +
+                ", tasks=" + tasks +
+                ", decisionMatrix=" + decisionMatrix +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingDefinitionArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingDefinitionArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..34a1ad38db8a422703c32e0ba235e23ddb2183e9
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingDefinitionArchiveDTO.java
@@ -0,0 +1,199 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.training;
+
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates information about Training Definition.
+ * Used for archiving
+ */
+@ApiModel(value = "TrainingDefinitionArchiveDTO", description = "Archived detailed information about training definition which also include individual phase.")
+public class TrainingDefinitionArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of training definition.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    private boolean showStepperBar;
+    @ApiModelProperty(value = "Information about all phase in training definition.")
+    private List<AbstractPhaseArchiveDTO> phases = new ArrayList<>();
+    @ApiModelProperty(value = "Estimated time it takes to finish runs created from this definition.", example = "5")
+    private int estimatedDuration;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites string [ ].
+     *
+     * @return the string [ ]
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes string [ ].
+     *
+     * @return the string [ ]
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the state
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the state
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Is show stepper bar boolean.
+     *
+     * @return the boolean
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets show stepper bar.
+     *
+     * @param showStepperBar the show stepper bar
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Gets phase.
+     *
+     * @return the phase
+     */
+    public List<AbstractPhaseArchiveDTO> getPhases() {
+        return phases;
+    }
+
+    /**
+     * Sets phase.
+     *
+     * @param phases the phase
+     */
+    public void setPhases(List<AbstractPhaseArchiveDTO> phases) {
+        this.phases = phases;
+    }
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public int getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(int estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingInstanceArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingInstanceArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..22a90c819f6005716451aba6f6ccf7a2b416f8d7
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingInstanceArchiveDTO.java
@@ -0,0 +1,153 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.training;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.Set;
+
+/**
+ * Encapsulates information about Training instance.
+ * Used for archiving
+ */
+@ApiModel(value = "TrainingInstanceArchiveDTO", description = "The finished and archived instance of training definition which includes individual finished training runs of participants.")
+public class TrainingInstanceArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of training instance.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Main identifier of training definition associated with this instance.", example = "1")
+    private Long definitionId;
+    @ApiModelProperty(value = "Date when training instance starts.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", example = "Concluded Instance")
+    private String title;
+    @ApiModelProperty(value = "Reference to organizersRefIds which organize training instance.")
+    private Set<Long> organizersRefIds;
+    @ApiModelProperty(value = "Token needed to access runs created from this definition", example = "pass-1234")
+    private String accessToken;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getDefinitionId() {
+        return definitionId;
+    }
+
+    public void setDefinitionId(Long definitionId) {
+        this.definitionId = definitionId;
+    }
+
+    /**
+     * Gets start time of training instance.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time of training instance.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time of training instance.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time of training instance.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets title of training instance.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title of training instance.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets organizersRefIds of training instance.
+     *
+     * @return the organizersRefIds
+     */
+    public Set<Long> getOrganizersRefIds() {
+        return organizersRefIds;
+    }
+
+    /**
+     * Sets organizersRefIds of training instance.
+     *
+     * @param organizersRefIds the organizersRefIds
+     */
+    public void setOrganizersRefIds(Set<Long> organizersRefIds) {
+        this.organizersRefIds = organizersRefIds;
+    }
+
+    /**
+     * Gets access token of training instance.
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token of training instance.
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceArchiveDTO{" +
+                "id=" + id +
+                ", definitionId=" + definitionId +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", organizersRefIds=" + organizersRefIds +
+                ", accessToken='" + accessToken + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingRunArchiveDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingRunArchiveDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa9edfe6310c35ae74e5b2d0377f07367a53d83e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/archive/training/TrainingRunArchiveDTO.java
@@ -0,0 +1,152 @@
+package cz.muni.ics.kypo.training.adaptive.dto.archive.training;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training run.
+ * Used for archiving
+ */
+@ApiModel(value = "TrainingRunArchiveDTO", description = "An archived run of training instance of a particular participant.")
+public class TrainingRunArchiveDTO {
+
+    @ApiModelProperty(value = "Main identifier of training run.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Main identifier of training instance associated with this run.", example = "1")
+    private Long instanceId;
+    @ApiModelProperty(value = "Date when training run started.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training run ends.", example = "2022-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Current state of training run.", example = "ALLOCATED")
+    private TRState state;
+    @ApiModelProperty(value = "Reference to participant of training run.", example = "5")
+    private Long participantRefId;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets instance id.
+     *
+     * @return the instance id
+     */
+    public Long getInstanceId() {
+        return instanceId;
+    }
+
+    /**
+     * Sets instance id.
+     *
+     * @param instanceId the instance id
+     */
+    public void setInstanceId(Long instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the state
+     */
+    public TRState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the state
+     */
+    public void setState(TRState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets participant ref id.
+     *
+     * @return the participant ref id
+     */
+    public Long getParticipantRefId() {
+        return participantRefId;
+    }
+
+    /**
+     * Sets participant ref id.
+     *
+     * @param participantRefId the participant ref id
+     */
+    public void setParticipantRefId(Long participantRefId) {
+        this.participantRefId = participantRefId;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingRunArchiveDTO{" +
+                "id=" + id +
+                ", instanceId=" + instanceId +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", state=" + state +
+                ", participantRefId=" + participantRefId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/FileToReturnDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/FileToReturnDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..a879e3f8dd068a1b5d8598fae0cef144f705ee87
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/FileToReturnDTO.java
@@ -0,0 +1,74 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Class encapsulating entity into file.
+ */
+@ApiModel(value = "FileToReturnDTO", description = "Wrapping model which contains the content and title of the file.")
+public class FileToReturnDTO {
+
+    @ApiModelProperty(value = "Content of the file.", example = "[string]")
+    private byte[] content;
+    @ApiModelProperty(value = "Title of the file.", example = "TrainingInstance-NetworkDemolition")
+    private String title;
+
+    /**
+     * Instantiates a new File to return dto.
+     */
+    public FileToReturnDTO() {
+    }
+
+    /**
+     * Instantiates a new File to return dto.
+     *
+     * @param content the content in byte array
+     * @param title   the title of the file
+     */
+    public FileToReturnDTO(byte[] content, String title) {
+        this.content = content;
+        this.title = title;
+    }
+
+    /**
+     * Get content in byte array
+     *
+     * @return the in byte array
+     */
+    public byte[] getContent() {
+        return content;
+    }
+
+    /**
+     * Sets content.
+     *
+     * @param content the content
+     */
+    public void setContent(byte[] content) {
+        this.content = content;
+    }
+
+    /**
+     * Gets title of the file.
+     *
+     * @return the title of the file.
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title of the file.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public String toString() {
+        return "FileToReturnDTO{" + "content=" + content + ", title='" + title + '\'' + '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/UserRefExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/UserRefExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..24b726a2f90ae3e9f450e2fbd01ee602b0dd3cd4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/UserRefExportDTO.java
@@ -0,0 +1,145 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Encapsulates information about user reference.
+ */
+@ApiModel(value = "UserRefExportDTO", description = "An exported information about user reference.")
+public class UserRefExportDTO {
+
+    @ApiModelProperty(value = "Reference to user in another microservice.", example = "441048@mail.muni.cz")
+    private String userRefLogin;
+    @ApiModelProperty(value = "Reference to user in another microservice and get his full name", example = "Mgr. Ing. Pavel Ĺ eda")
+    private String userRefFullName;
+    @ApiModelProperty(value = "User given name", example = "Pavel")
+    private String userRefGivenName;
+    @ApiModelProperty(value = "User family name", example = "Seda")
+    private String userRefFamilyName;
+    @ApiModelProperty(value = "Reference to user in another microservice and get his iss", example = "https://oidc.muni.cz")
+    private String iss;
+    @ApiModelProperty(value = "Reference to user in another microservice and get his id", example = "1")
+    private Long userRefId;
+
+    /**
+     * Gets user reference login.
+     *
+     * @return the user reference login
+     */
+    public String getUserRefLogin() {
+        return userRefLogin;
+    }
+
+    /**
+     * Sets user reference login.
+     *
+     * @param userRefLogin the user reference login
+     */
+    public void setUserRefLogin(String userRefLogin) {
+        this.userRefLogin = userRefLogin;
+    }
+
+    /**
+     * Gets user reference full name.
+     *
+     * @return the user reference full name
+     */
+    public String getUserRefFullName() {
+        return userRefFullName;
+    }
+
+    /**
+     * Sets user reference full name.
+     *
+     * @param userRefFullName the user reference full name
+     */
+    public void setUserRefFullName(String userRefFullName) {
+        this.userRefFullName = userRefFullName;
+    }
+
+    /**
+     * Gets iss.
+     *
+     * @return the iss
+     */
+    public String getIss() {
+        return iss;
+    }
+
+    /**
+     * Sets iss.
+     *
+     * @param iss the iss
+     */
+    public void setIss(String iss) {
+        this.iss = iss;
+    }
+
+    /**
+     * Gets user ref id.
+     *
+     * @return the user ref id
+     */
+    public Long getUserRefId() {
+        return userRefId;
+    }
+
+    /**
+     * Sets user ref id.
+     *
+     * @param userRefId the user ref id
+     */
+    public void setUserRefId(Long userRefId) {
+        this.userRefId = userRefId;
+    }
+
+    /**
+     * Gets user ref given name.
+     *
+     * @return the user ref given name
+     */
+    public String getUserRefGivenName() {
+        return userRefGivenName;
+    }
+
+    /**
+     * Sets user ref given name.
+     *
+     * @param userRefGivenName the user ref given name
+     */
+    public void setUserRefGivenName(String userRefGivenName) {
+        this.userRefGivenName = userRefGivenName;
+    }
+
+    /**
+     * Gets user ref family name.
+     *
+     * @return the user ref family name
+     */
+    public String getUserRefFamilyName() {
+        return userRefFamilyName;
+    }
+
+    /**
+     * Sets user ref family name.
+     *
+     * @param userRefFamilyName the user ref family name
+     */
+    public void setUserRefFamilyName(String userRefFamilyName) {
+        this.userRefFamilyName = userRefFamilyName;
+    }
+
+
+    @Override
+    public String toString() {
+        return "UserRefExportDTO{" +
+                "userRefLogin='" + userRefLogin + '\'' +
+                ", userRefFullName='" + userRefFullName + '\'' +
+                ", userRefGivenName='" + userRefGivenName + '\'' +
+                ", userRefFamilyName='" + userRefFamilyName + '\'' +
+                ", iss='" + iss + '\'' +
+                ", userRefId=" + userRefId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/AbstractPhaseExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/AbstractPhaseExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d28ba8604564f1920db2e9bb89047350424ede4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/AbstractPhaseExportDTO.java
@@ -0,0 +1,110 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases;
+
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.info.InfoPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire.QuestionnairePhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.training.TrainingPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates information about abstract phase.
+ * Extended by {@link QuestionnairePhaseExportDTO}, {@link TrainingPhaseExportDTO} and {@link InfoPhaseExportDTO}
+ */
+@ApiModel(value = "AbstractPhaseExportDTO", subTypes = {TrainingPhaseExportDTO.class, InfoPhaseExportDTO.class, QuestionnairePhaseExportDTO.class},
+        description = "Superclass for classes TrainingPhaseExportDTO, InfoPhaseExportDTO and QuestionnairePhaseExportDTO")
+public abstract class AbstractPhaseExportDTO {
+
+    @ApiModelProperty(value = "Short textual description of the phase.", example = "Training Phase")
+    protected String title;
+    @ApiModelProperty(value = "Type of the phase.", example = "TRAINING")
+    protected PhaseType phaseType;
+    @ApiModelProperty(value = "Order of phase, starts with 0", example = "2")
+    protected int order;
+
+    /**
+     * Instantiates a new Abstract phase export dto.
+     */
+    public AbstractPhaseExportDTO() {
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets phase type.
+     *
+     * @return the {@link PhaseType}
+     */
+    public PhaseType getPhaseType() {
+        return phaseType;
+    }
+
+    /**
+     * Sets phase type.
+     *
+     * @param phaseType the {@link PhaseType}
+     */
+    public void setPhaseType(PhaseType phaseType) {
+        this.phaseType = phaseType;
+    }
+
+    /**
+     * Gets order.
+     *
+     * @return the order
+     */
+    public int getOrder() {
+        return order;
+    }
+
+    /**
+     * Sets order.
+     *
+     * @param order the order
+     */
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AbstractPhaseExportDTO)) return false;
+        AbstractPhaseExportDTO that = (AbstractPhaseExportDTO) o;
+        return getOrder() == that.getOrder() &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                getPhaseType() == that.getPhaseType();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getTitle(), getPhaseType(), getOrder());
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractPhaseExportDTO{" +
+                "title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/info/InfoPhaseExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/info/InfoPhaseExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..87f5a59134572683a88d125666a187db99a33c2c
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/info/InfoPhaseExportDTO.java
@@ -0,0 +1,47 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.info;
+
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Encapsulates information about info phase. Inherits from {@link AbstractPhaseExportDTO}
+ */
+@ApiModel(value = "InfoPhaseExportDTO", description = "Exported info phase.", parent = AbstractPhaseExportDTO.class)
+public class InfoPhaseExportDTO extends AbstractPhaseExportDTO {
+
+    @ApiModelProperty(value = "The information and experiences that are directed towards a participant.", example = "Informational stuff")
+    private String content;
+
+    /**
+     * Instantiates a new Info phase export dto.
+     */
+    public InfoPhaseExportDTO() {
+        this.content = "";
+    }
+
+    /**
+     * Gets content.
+     *
+     * @return the content
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * Sets content.
+     *
+     * @param content the content
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        return "InfoPhaseExportDTO{" +
+                "content='" + content + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionChoiceExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionChoiceExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..717ad3113005bf769af6c211fd09f8a6990578b9
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionChoiceExportDTO.java
@@ -0,0 +1,37 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+public class QuestionChoiceExportDTO {
+
+    @ApiModelProperty(value = "Short description of question choice", required = true, example = "An answer")
+    private String text;
+    @ApiModelProperty(value = "It defines whether this answer is correct or not", required = true, example = "true")
+    private Boolean correct;
+    @ApiModelProperty(value = "Order of question choice", required = true, example = "0")
+    private Integer order;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public Boolean isCorrect() {
+        return correct;
+    }
+
+    public void setCorrect(Boolean correct) {
+        this.correct = correct;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..b54f857d211bc116463c76f4700a4f645ce3ad3e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionExportDTO.java
@@ -0,0 +1,50 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionType;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+public class QuestionExportDTO {
+
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    private int order;
+    @ApiModelProperty(value = "The question that will be displayed to a player", required = true, example = "What's the capital of Canada?")
+    private String text;
+    @ApiModelProperty(value = "It defines the type of the question", allowableValues = "FFQ, MCQ, RFQ", required = true, example = "MCQ")
+    private QuestionType questionType;
+    @ApiModelProperty(value = "Choices that are distributed with the question", required = true)
+    private List<QuestionChoiceExportDTO> choices;
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public QuestionType getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(QuestionType type) {
+        this.questionType = type;
+    }
+
+    public List<QuestionChoiceExportDTO> getChoices() {
+        return choices;
+    }
+
+    public void setChoices(List<QuestionChoiceExportDTO> choices) {
+        this.choices = choices;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionPhaseRelationExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionPhaseRelationExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bb8c0354fe948d034e1fd71fe0032cccf81f60f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionPhaseRelationExportDTO.java
@@ -0,0 +1,76 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+import java.util.Set;
+
+public class QuestionPhaseRelationExportDTO {
+
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    private Integer order;
+    @ApiModelProperty(value = "Set of IDs of questions related to the specified questionnaire")
+    private Set<Long> questionIds;
+    @ApiModelProperty(value = "ID of training phase to which the questions are related of question", required = true, example = "1")
+    private Long phaseId;
+    @ApiModelProperty(value = "Percentage that defines whether a player was successful or not ", required = true, example = "50")
+    private int successRate;
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public Set<Long> getQuestionIds() {
+        return questionIds;
+    }
+
+    public void setQuestionIds(Set<Long> questionIds) {
+        this.questionIds = questionIds;
+    }
+
+    public Long getPhaseId() {
+        return phaseId;
+    }
+
+    public void setPhaseId(Long phaseId) {
+        this.phaseId = phaseId;
+    }
+
+    public int getSuccessRate() {
+        return successRate;
+    }
+
+    public void setSuccessRate(int successRate) {
+        this.successRate = successRate;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionPhaseRelationExportDTO)) return false;
+        QuestionPhaseRelationExportDTO that = (QuestionPhaseRelationExportDTO) o;
+        return getSuccessRate() == that.getSuccessRate() &&
+                Objects.equals(getOrder(), that.getOrder()) &&
+                Objects.equals(getQuestionIds(), that.getQuestionIds()) &&
+                Objects.equals(getPhaseId(), that.getPhaseId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getOrder(), getQuestionIds(), getPhaseId(), getSuccessRate());
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionPhaseRelationExportDTO{" +
+                "order=" + order +
+                ", questionIds=" + questionIds +
+                ", phaseId=" + phaseId +
+                ", successRate=" + successRate +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionnairePhaseExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionnairePhaseExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..8281dabb8f906e87c6b3148b5249970501893be6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/questionnaire/QuestionnairePhaseExportDTO.java
@@ -0,0 +1,57 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
+import io.swagger.annotations.ApiModel;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about questionnaire phase. Inherits from {@link AbstractPhaseExportDTO}
+ */
+@ApiModel(value = "QuestionnairePhaseExportDTO", description = "Exported questionnaire phase.", parent = AbstractPhaseExportDTO.class)
+public class QuestionnairePhaseExportDTO extends AbstractPhaseExportDTO {
+
+    private List<QuestionExportDTO> questions;
+    private QuestionnaireType questionnaireType;
+    private List<QuestionPhaseRelationExportDTO> phaseRelations;
+
+    public List<QuestionExportDTO> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionExportDTO> questions) {
+        this.questions = questions;
+    }
+
+    public QuestionnaireType getQuestionnaireType() {
+        return questionnaireType;
+    }
+
+    public void setQuestionnaireType(QuestionnaireType questionnaireType) {
+        this.questionnaireType = questionnaireType;
+    }
+
+    public List<QuestionPhaseRelationExportDTO> getPhaseRelations() {
+        return phaseRelations;
+    }
+
+    public void setPhaseRelations(List<QuestionPhaseRelationExportDTO> phaseRelations) {
+        this.phaseRelations = phaseRelations;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionnairePhaseExportDTO)) return false;
+        if (!super.equals(o)) return false;
+        QuestionnairePhaseExportDTO that = (QuestionnairePhaseExportDTO) o;
+        return getQuestionnaireType() == that.getQuestionnaireType();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), getQuestionnaireType());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/DecisionMatrixRowExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/DecisionMatrixRowExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a4070be7b58eafb718e61a376aaec9f855cae3a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/DecisionMatrixRowExportDTO.java
@@ -0,0 +1,70 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.training;
+
+public class DecisionMatrixRowExportDTO {
+    private int order;
+    private double questionnaireAnswered;
+    private double keywordUsed;
+    private double completedInTime;
+    private double solutionDisplayed;
+    private double wrongAnswers;
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public double getQuestionnaireAnswered() {
+        return questionnaireAnswered;
+    }
+
+    public void setQuestionnaireAnswered(double questionnaireAnswered) {
+        this.questionnaireAnswered = questionnaireAnswered;
+    }
+
+    public double getKeywordUsed() {
+        return keywordUsed;
+    }
+
+    public void setKeywordUsed(double keywordUsed) {
+        this.keywordUsed = keywordUsed;
+    }
+
+    public double getCompletedInTime() {
+        return completedInTime;
+    }
+
+    public void setCompletedInTime(double completedInTime) {
+        this.completedInTime = completedInTime;
+    }
+
+    public double getSolutionDisplayed() {
+        return solutionDisplayed;
+    }
+
+    public void setSolutionDisplayed(double solutionDisplayed) {
+        this.solutionDisplayed = solutionDisplayed;
+    }
+
+    public double getWrongAnswers() {
+        return wrongAnswers;
+    }
+
+    public void setWrongAnswers(double wrongAnswers) {
+        this.wrongAnswers = wrongAnswers;
+    }
+
+    @Override
+    public String toString() {
+        return "DecisionMatrixRow{" +
+                "order=" + order +
+                ", questionnaireAnswered=" + questionnaireAnswered +
+                ", keywordUsed=" + keywordUsed +
+                ", completedInTime=" + completedInTime +
+                ", solutionDisplayed=" + solutionDisplayed +
+                ", wrongAnswers=" + wrongAnswers +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TaskExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TaskExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..30c95ded5ff2aaef8ca6ff1626be128b8af672fb
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TaskExportDTO.java
@@ -0,0 +1,91 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.training;
+
+public class TaskExportDTO {
+
+    private String title;
+    private Integer order;
+    private String content;
+    private String answer;
+    private String solution;
+    private int incorrectAnswerLimit;
+    private boolean modifySandbox;
+    private int sandboxChangeExpectedDuration;
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public String getSolution() {
+        return solution;
+    }
+
+    public void setSolution(String solution) {
+        this.solution = solution;
+    }
+
+    public int getIncorrectAnswerLimit() {
+        return incorrectAnswerLimit;
+    }
+
+    public void setIncorrectAnswerLimit(int incorrectAnswerLimit) {
+        this.incorrectAnswerLimit = incorrectAnswerLimit;
+    }
+
+    public boolean isModifySandbox() {
+        return modifySandbox;
+    }
+
+    public void setModifySandbox(boolean modifySandbox) {
+        this.modifySandbox = modifySandbox;
+    }
+
+    public int getSandboxChangeExpectedDuration() {
+        return sandboxChangeExpectedDuration;
+    }
+
+    public void setSandboxChangeExpectedDuration(int sandboxChangeExpectedDuration) {
+        this.sandboxChangeExpectedDuration = sandboxChangeExpectedDuration;
+    }
+
+    @Override
+    public String toString() {
+        return "TaskDTO{" +
+                "title='" + title + '\'' +
+                ", order=" + order +
+                ", content='" + content + '\'' +
+                ", answer='" + answer + '\'' +
+                ", solution='" + solution + '\'' +
+                ", incorrectAnswerLimit=" + incorrectAnswerLimit +
+                ", modifySandbox=" + modifySandbox +
+                ", sandboxChangeExpectedDuration=" + sandboxChangeExpectedDuration +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TrainingPhaseExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TrainingPhaseExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fb796028da2e777b37ebc5e7323e80d0411cd29
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/phases/training/TrainingPhaseExportDTO.java
@@ -0,0 +1,89 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.phases.training;
+
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import io.swagger.annotations.ApiModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about training phase. Inherits from {@link AbstractPhaseExportDTO}
+ */
+@ApiModel(value = "TrainingPhaseExportDTO", description = "Exported training phase.", parent = AbstractPhaseExportDTO.class)
+public class TrainingPhaseExportDTO extends AbstractPhaseExportDTO {
+
+    private int estimatedDuration;
+    private int allowedCommands;
+    private int allowedWrongAnswers;
+    private List<TaskExportDTO> tasks = new ArrayList<>();
+    private List<DecisionMatrixRowExportDTO> decisionMatrix;
+
+    public int getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    public void setEstimatedDuration(int estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    public int getAllowedCommands() {
+        return allowedCommands;
+    }
+
+    public void setAllowedCommands(int allowedCommands) {
+        this.allowedCommands = allowedCommands;
+    }
+
+    public int getAllowedWrongAnswers() {
+        return allowedWrongAnswers;
+    }
+
+    public void setAllowedWrongAnswers(int allowedWrongAnswers) {
+        this.allowedWrongAnswers = allowedWrongAnswers;
+    }
+
+    public List<TaskExportDTO> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(List<TaskExportDTO> tasks) {
+        this.tasks = tasks;
+    }
+
+    public List<DecisionMatrixRowExportDTO> getDecisionMatrix() {
+        return decisionMatrix;
+    }
+
+    public void setDecisionMatrix(List<DecisionMatrixRowExportDTO> decisionMatrix) {
+        this.decisionMatrix = decisionMatrix;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TrainingPhaseExportDTO)) return false;
+        if (!super.equals(o)) return false;
+        TrainingPhaseExportDTO that = (TrainingPhaseExportDTO) o;
+        return getEstimatedDuration() == that.getEstimatedDuration() &&
+                getAllowedCommands() == that.getAllowedCommands() &&
+                getAllowedWrongAnswers() == that.getAllowedWrongAnswers();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), getEstimatedDuration(), getAllowedCommands(), getAllowedWrongAnswers());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingPhaseExportDTO{" +
+                "estimatedDuration=" + estimatedDuration +
+                ", allowedCommands=" + allowedCommands +
+                ", allowedWrongAnswers=" + allowedWrongAnswers +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingDefinitionWithPhasesExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingDefinitionWithPhasesExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1cf5f5ece1910249ea4f2ba2c7614d3c2572bf4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingDefinitionWithPhasesExportDTO.java
@@ -0,0 +1,188 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.training;
+
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates information about training definition and its phase.
+ */
+@ApiModel(value = "TrainingDefinitionWithPhasesExportDTO", description = "An exported detailed information about training definition which also include individual phase.")
+public class TrainingDefinitionWithPhasesExportDTO {
+
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    private boolean showStepperBar;
+    @ApiModelProperty(value = "Information about all phase in training definition.")
+    private List<AbstractPhaseExportDTO> phases = new ArrayList<>();
+    @ApiModelProperty(value = "Estimated time (minutes) taken by the player to finish run created from this definition.", example = "5")
+    private int estimatedDuration;
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets development state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets development state.
+     *
+     * @param state {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Is show stepper bar boolean.
+     *
+     * @return the boolean
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets show stepper bar.
+     *
+     * @param showStepperBar the show stepper bar
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Gets phase.
+     *
+     * @return the list of {@link AbstractPhaseExportDTO}
+     */
+    public List<AbstractPhaseExportDTO> getPhases() {
+        return phases;
+    }
+
+    /**
+     * Sets phase.
+     *
+     * @param phases the list of {@link AbstractPhaseExportDTO}
+     */
+    public void setPhases(List<AbstractPhaseExportDTO> phases) {
+        this.phases = phases;
+    }
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public int getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(int estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionWithPhasesExportDTO{" +
+                "title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                ", estimatedDuration=" + estimatedDuration +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingRunExportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingRunExportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb96e3849e76e2674ccc63d325e001c35eae534f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/export/training/TrainingRunExportDTO.java
@@ -0,0 +1,112 @@
+package cz.muni.ics.kypo.training.adaptive.dto.export.training;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.export.UserRefExportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training Run.
+ */
+@ApiModel(value = "TrainingRunExportDTO", description = "An exported run of training instance of a particular participant.")
+public class TrainingRunExportDTO {
+
+    @ApiModelProperty(value = "Date when training run started.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training run ends.", example = "2022-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Current state of training run.", example = "ALLOCATED")
+    private TRState state;
+    @ApiModelProperty(value = "Reference to participant of training run.")
+    private UserRefExportDTO participantRef;
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TRState}
+     */
+    public TRState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state {@link TRState}
+     */
+    public void setState(TRState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets participant ref.
+     *
+     * @return the {@link UserRefExportDTO}
+     */
+    public UserRefExportDTO getParticipantRef() {
+        return participantRef;
+    }
+
+    /**
+     * Sets participant ref.
+     *
+     * @param participantRef the {@link UserRefExportDTO}
+     */
+    public void setParticipantRef(UserRefExportDTO participantRef) {
+        this.participantRef = participantRef;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingRunExportDTO{" +
+                "startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", state=" + state +
+                ", participantRef=" + participantRef +
+                '}';
+    }
+}
+
+
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/ImportTrainingDefinitionDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/ImportTrainingDefinitionDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e86e0206f6862e1fa46c3d544a9995ed929eb6a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/ImportTrainingDefinitionDTO.java
@@ -0,0 +1,203 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports;
+
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.AbstractPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates information about training definition and its phase.
+ */
+@ApiModel(value = "ImportTrainingDefinitionDTO", description = "A basic information about hint.")
+public class ImportTrainingDefinitionDTO {
+
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    @NotEmpty(message = "{trainingDefinition.title.NotEmpty.message}")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    @NotNull(message = "{trainingDefinition.state.NotNull.message}")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    @NotNull(message = "{trainingDefinition.showStepperBar.NotNull.message}")
+    private boolean showStepperBar;
+    @Valid
+    @ApiModelProperty(value = "Information about all phase in training definition.")
+    private List<AbstractPhaseImportDTO> phases = new ArrayList<>();
+    @ApiModelProperty(value = "Estimated time it takes to finish runs created from this definition.", example = "5")
+    private Integer estimatedDuration;
+
+    /**
+     * Instantiates a new Import training definition dto.
+     */
+    public ImportTrainingDefinitionDTO() {
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Gets phase.
+     *
+     * @return the list of {@link AbstractPhaseImportDTO}
+     */
+    public List<AbstractPhaseImportDTO> getPhases() {
+        return phases;
+    }
+
+    /**
+     * Sets phase.
+     *
+     * @param phases the list of {@link AbstractPhaseImportDTO}
+     */
+    public void setPhases(List<AbstractPhaseImportDTO> phases) {
+        this.phases = new ArrayList<>(phases);
+    }
+
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public Integer getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(Integer estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+
+    @Override
+    public String toString() {
+        return "ImportTrainingDefinitionDTO{" +
+                "title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                ", estimatedDuration=" + estimatedDuration +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/AbstractPhaseImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/AbstractPhaseImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e60638198556a3af192238fb60a195ff13d0528
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/AbstractPhaseImportDTO.java
@@ -0,0 +1,128 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.info.InfoPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionnairePhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.TaskImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.TrainingPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about abstract phase.
+ * Extended by {@link QuestionnairePhaseImportDTO}, {@link TaskImportDTO} and {@link InfoPhaseImportDTO}
+ */
+@ApiModel(value = "AbstractPhaseImportDTO", subTypes = {TrainingPhaseImportDTO.class, InfoPhaseImportDTO.class, QuestionnairePhaseImportDTO.class},
+        description = "Superclass for classes TrainingPhaseImportDTO, QuestionnairePhaseImportDTO and InfoPhaseImportDTO")
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "phase_type", visible = true)
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = TrainingPhaseImportDTO.class, name = "TRAINING"),
+        @JsonSubTypes.Type(value = QuestionnairePhaseImportDTO.class, name = "QUESTIONNAIRE"),
+        @JsonSubTypes.Type(value = InfoPhaseImportDTO.class, name = "INFO")})
+public abstract class AbstractPhaseImportDTO {
+
+    @ApiModelProperty(value = "Short textual description of the phase.", example = "Training phase description")
+    @NotEmpty(message = "{phase.title.NotEmpty.message}")
+    protected String title;
+    @ApiModelProperty(value = "Type of the phase.", example = "TRAINING")
+    @NotNull(message = "{phase.phaseType.NotNull.message}")
+    protected PhaseType phaseType;
+    @ApiModelProperty(value = "Order of phase, starts with 0", example = "2")
+    @NotNull(message = "{phase.order.NotNull.message}")
+    @Min(value = 0, message = "{phase.order.Min.message}")
+    protected Integer order;
+
+    /**
+     * Instantiates a new Abstract phase import dto.
+     */
+    public AbstractPhaseImportDTO() {
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+
+    /**
+     * Gets phase type.
+     *
+     * @return the {@link PhaseType}
+     */
+    public PhaseType getPhaseType() {
+        return phaseType;
+    }
+
+    /**
+     * Sets phase type.
+     *
+     * @param phaseType the {@link PhaseType}
+     */
+    public void setPhaseType(PhaseType phaseType) {
+        this.phaseType = phaseType;
+    }
+
+    /**
+     * Gets order number of phase that is compared with order numbers of other phase associated with same definition.
+     * First phase from definition has order of 0
+     *
+     * @return the order
+     */
+    public Integer getOrder() {
+        return order;
+    }
+
+    /**
+     * Sets order number of phase that is compared with order numbers of other phase associated with same definition.
+     * First phase from definition has order of 0
+     *
+     * @param order the order
+     */
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    @Override
+    public String toString() {
+        return "AbstractPhaseImportDTO{" +
+                "title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AbstractPhaseImportDTO)) return false;
+        AbstractPhaseImportDTO that = (AbstractPhaseImportDTO) o;
+        return Objects.equals(getTitle(), that.getTitle()) &&
+                getPhaseType() == that.getPhaseType() &&
+                Objects.equals(getOrder(), that.getOrder());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getTitle(), getPhaseType(), getOrder());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/info/InfoPhaseImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/info/InfoPhaseImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a49ea16ce2614634850a63357471ae2e3769fc5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/info/InfoPhaseImportDTO.java
@@ -0,0 +1,61 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.info;
+
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.AbstractPhaseImportDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.Objects;
+
+/**
+ * The type Info phase import dto. * Encapsulates information about info phase. Inherits from {@link AbstractPhaseImportDTO}
+ */
+@ApiModel(value = "InfoPhaseImportDTO", description = "An imported info phase.", parent = AbstractPhaseImportDTO.class)
+public class InfoPhaseImportDTO extends AbstractPhaseImportDTO {
+
+    @ApiModelProperty(value = "The information and experiences that are directed towards a participant.", example = "Informational stuff")
+    @NotEmpty(message = "{info.content.NotEmpty.message}")
+    private String content;
+
+    /**
+     * Gets content.
+     *
+     * @return the content
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * Sets content.
+     *
+     * @param content the content
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        return "InfoPhaseImportDTO{" +
+                "content='" + content + '\'' +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof InfoPhaseImportDTO)) return false;
+        if (!super.equals(o)) return false;
+        InfoPhaseImportDTO that = (InfoPhaseImportDTO) o;
+        return Objects.equals(getContent(), that.getContent());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), getContent());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionChoiceImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionChoiceImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9793477acc4b223d62d75466a4305b1d9bf0562b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionChoiceImportDTO.java
@@ -0,0 +1,61 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+public class QuestionChoiceImportDTO {
+
+    @ApiModelProperty(value = "Short description of question choice", required = true, example = "An answer")
+    @NotEmpty(message = "{questionChoices.text.NotEmpty.message}")
+    private String text;
+    @ApiModelProperty(value = "It defines whether this answer is correct or not", required = true, example = "true")
+    @NotNull(message = "{questionChoices.correct.NotNull.message}")
+    private Boolean correct;
+    @ApiModelProperty(value = "Order of question choice", required = true, example = "0")
+    @NotNull(message = "{questionChoices.order.NotNull.message}")
+    @Min(value = 0, message = "{questionChoices.order.Min.message}")
+    private Integer order;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public Boolean isCorrect() {
+        return correct;
+    }
+
+    public void setCorrect(Boolean correct) {
+        this.correct = correct;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionChoiceImportDTO)) return false;
+        QuestionChoiceImportDTO that = (QuestionChoiceImportDTO) o;
+        return Objects.equals(getText(), that.getText()) &&
+                Objects.equals(correct, that.correct) &&
+                Objects.equals(getOrder(), that.getOrder());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getText(), correct, getOrder());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..58ad7bf6adf4c2d8af6ba561c549f47174b17ffd
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionImportDTO.java
@@ -0,0 +1,86 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionType;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Objects;
+
+public class QuestionImportDTO {
+
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    @NotNull(message = "{question.order.NotNull.message}")
+    @Min(value = 0, message = "{question.order.Min.message}")
+    private Integer order;
+    @ApiModelProperty(value = "The question that will be displayed to a player", required = true, example = "What's the capital of Canada?")
+    @NotEmpty(message = "{question.text.NotEmpty.message}")
+    private String text;
+    @ApiModelProperty(value = "It defines the type of the question", allowableValues = "FFQ, MCQ, RFQ", required = true, example = "MCQ")
+    @NotNull(message = "{question.questionType.NotNull.message}")
+    private QuestionType questionType;
+    @Valid
+    @ApiModelProperty(value = "Choices that are distributed with the question", required = true)
+    private List<QuestionChoiceImportDTO> choices;
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public QuestionType getQuestionType() {
+        return questionType;
+    }
+
+    public void setQuestionType(QuestionType type) {
+        this.questionType = type;
+    }
+
+    public List<QuestionChoiceImportDTO> getChoices() {
+        return choices;
+    }
+
+    public void setChoices(List<QuestionChoiceImportDTO> choices) {
+        this.choices = choices;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionImportDTO)) return false;
+        QuestionImportDTO that = (QuestionImportDTO) o;
+        return getOrder() == that.getOrder() &&
+                Objects.equals(getText(), that.getText()) &&
+                getQuestionType() == that.getQuestionType() &&
+                Objects.equals(getChoices(), that.getChoices());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getOrder(), getText(), getQuestionType(), getChoices());
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionImportDTO{" +
+                "order=" + order +
+                ", text='" + text + '\'' +
+                ", questionType=" + questionType +
+                ", choices=" + choices +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionPhaseRelationImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionPhaseRelationImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ffeb585b113774e749e7175ddc29e659928d16e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionPhaseRelationImportDTO.java
@@ -0,0 +1,78 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.util.Objects;
+import java.util.Set;
+
+public class QuestionPhaseRelationImportDTO {
+
+    @ApiModelProperty(value = "Order of question", required = true, example = "0")
+    @NotNull(message = "{questionnairePhaseRelation.order.NotNull.message}")
+    @Min(value = 0, message = "{questionnairePhaseRelation.order.Min.message}")
+    private Integer order;
+    @ApiModelProperty(value = "Set of IDs of questions related to the specified questionnaire")
+    @NotNull(message = "{questionnairePhaseRelation.questionIds.NotNull.message}")
+    @Size(min = 1, message = "{questionnairePhaseRelation.questionIds.Size.message}")
+    private Set<Long> questionIds;
+    @ApiModelProperty(value = "ID of training phase to which the questions are related of question", required = true, example = "1")
+    @NotNull(message = "{questionnairePhaseRelation.phaseId.NotNull.message}")
+    @Min(value = 0, message = "{questionnairePhaseRelation.phaseId.Min.message}")
+    private Long phaseId;
+    @ApiModelProperty(value = "Percentage that defines whether a player was successful or not ", required = true, example = "50")
+    @Min(value = 0, message = "{questionnairePhaseRelation.successRate.Min.message}")
+    @Max(value = 100, message = "{questionnairePhaseRelation.successRate.Max.message}")
+    private int successRate;
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public Set<Long> getQuestionIds() {
+        return questionIds;
+    }
+
+    public void setQuestionIds(Set<Long> questionIds) {
+        this.questionIds = questionIds;
+    }
+
+    public Long getPhaseId() {
+        return phaseId;
+    }
+
+    public void setPhaseId(Long phaseId) {
+        this.phaseId = phaseId;
+    }
+
+    public int getSuccessRate() {
+        return successRate;
+    }
+
+    public void setSuccessRate(int successRate) {
+        this.successRate = successRate;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof QuestionPhaseRelationImportDTO)) return false;
+        QuestionPhaseRelationImportDTO that = (QuestionPhaseRelationImportDTO) o;
+        return getSuccessRate() == that.getSuccessRate() &&
+                getOrder().equals(that.getOrder()) &&
+                getQuestionIds().equals(that.getQuestionIds()) &&
+                getPhaseId().equals(that.getPhaseId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getOrder(), getQuestionIds(), getPhaseId(), getSuccessRate());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionnairePhaseImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionnairePhaseImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..6621c9946259d6109b24e1180476a3aaea10d45b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/questionnaire/QuestionnairePhaseImportDTO.java
@@ -0,0 +1,82 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire;
+
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.AbstractPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * Encapsulates information about questionnaire phase. Inherits from {@link QuestionnairePhaseImportDTO}
+ */
+@ApiModel(value = "QuestionnairePhaseImportDTO", description = "Imported questionnaire phase.", parent = QuestionnairePhaseImportDTO.class)
+public class QuestionnairePhaseImportDTO extends AbstractPhaseImportDTO {
+
+    @Valid
+    @ApiModelProperty(value = "Questions in the questionnaire", required = true)
+    private List<QuestionImportDTO> questions;
+    @ApiModelProperty(value = "The type of the questionnaire", required = true, example = "ADAPTIVE")
+    @NotNull(message = "{questionnairePhase.questionnaireType.NotNull.message}")
+    private QuestionnaireType questionnaireType;
+    @Valid
+    @ApiModelProperty(value = "The relation between questions in the questionnaire and phase in the training definition", required = true)
+    private List<QuestionPhaseRelationImportDTO> phaseRelations;
+
+    /**
+     * Gets questions.
+     *
+     * @return the questions
+     */
+    public List<QuestionImportDTO> getQuestions() {
+        return questions;
+    }
+
+    /**
+     * Sets questions.
+     *
+     * @param questions the questions
+     */
+    public void setQuestions(List<QuestionImportDTO> questions) {
+        this.questions = questions;
+    }
+
+    /**
+     * Gets phase relations.
+     *
+     * @return the relations
+     */
+    public List<QuestionPhaseRelationImportDTO> getPhaseRelations() {
+        return phaseRelations;
+    }
+
+    /**
+     * Sets phase relations.
+     *
+     * @param phaseRelations the relations
+     */
+    public void setPhaseRelations(List<QuestionPhaseRelationImportDTO> phaseRelations) {
+        this.phaseRelations = phaseRelations;
+    }
+
+    public QuestionnaireType getQuestionnaireType() {
+        return questionnaireType;
+    }
+
+    public void setQuestionnaireType(QuestionnaireType questionnaireType) {
+        this.questionnaireType = questionnaireType;
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionnairePhaseImportDTO{" +
+                "questions=" + questions +
+                ", phaseRelations=" + phaseRelations +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/DecisionMatrixRowImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/DecisionMatrixRowImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..4fb8f357d7631e7f65a1459678b5df2e9ff7b981
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/DecisionMatrixRowImportDTO.java
@@ -0,0 +1,70 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training;
+
+public class DecisionMatrixRowImportDTO {
+    private int order;
+    private double questionnaireAnswered;
+    private double keywordUsed;
+    private double completedInTime;
+    private double solutionDisplayed;
+    private double wrongAnswers;
+
+    public int getOrder() {
+        return order;
+    }
+
+    public void setOrder(int order) {
+        this.order = order;
+    }
+
+    public double getQuestionnaireAnswered() {
+        return questionnaireAnswered;
+    }
+
+    public void setQuestionnaireAnswered(double questionnaireAnswered) {
+        this.questionnaireAnswered = questionnaireAnswered;
+    }
+
+    public double getKeywordUsed() {
+        return keywordUsed;
+    }
+
+    public void setKeywordUsed(double keywordUsed) {
+        this.keywordUsed = keywordUsed;
+    }
+
+    public double getCompletedInTime() {
+        return completedInTime;
+    }
+
+    public void setCompletedInTime(double completedInTime) {
+        this.completedInTime = completedInTime;
+    }
+
+    public double getSolutionDisplayed() {
+        return solutionDisplayed;
+    }
+
+    public void setSolutionDisplayed(double solutionDisplayed) {
+        this.solutionDisplayed = solutionDisplayed;
+    }
+
+    public double getWrongAnswers() {
+        return wrongAnswers;
+    }
+
+    public void setWrongAnswers(double wrongAnswers) {
+        this.wrongAnswers = wrongAnswers;
+    }
+
+    @Override
+    public String toString() {
+        return "DecisionMatrixRow{" +
+                "order=" + order +
+                ", questionnaireAnswered=" + questionnaireAnswered +
+                ", keywordUsed=" + keywordUsed +
+                ", completedInTime=" + completedInTime +
+                ", solutionDisplayed=" + solutionDisplayed +
+                ", wrongAnswers=" + wrongAnswers +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TaskImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TaskImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..99970839188fed347b2338caf82d468cb05dbf4a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TaskImportDTO.java
@@ -0,0 +1,159 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.*;
+
+/**
+ * Encapsulates information about training phase. Inherits from {@link TaskImportDTO}
+ */
+@ApiModel(value = "TaskImportDTO", description = "Imported task of the training phase.")
+public class TaskImportDTO {
+
+    @ApiModelProperty(value = "Short textual description of the phase.", example = "Training phase title")
+    @NotEmpty(message = "{task.title.NotEmpty.message}")
+    protected String title;
+    @ApiModelProperty(value = "Order of phase, starts with 0", example = "2")
+    @NotNull(message = "{task.order.NotNull.message}")
+    @Min(value = 0, message = "{task.order.Min.message}")
+    protected Integer order;
+    @ApiModelProperty(value = "Keyword found in training, used for access next phase.", example = "secretFlag")
+    @NotEmpty(message = "{task.answer.NotEmpty.message}")
+    @Size(max = 50, message = "{task.answer.Size.message}")
+    private String answer;
+    @ApiModelProperty(value = "The information and experiences that are directed towards a participant.", example = "Play me")
+    @NotEmpty(message = "{task.content.NotEmpty.message}")
+    private String content;
+    @ApiModelProperty(value = "Instruction how to get answer in training.", example = "This is how you do it")
+    @NotEmpty(message = "{task.solution.NotEmpty.message}")
+    private String solution;
+    @ApiModelProperty(value = "How many times player can submit incorrect answer before displaying solution.", example = "5")
+    @NotNull(message = "{task.incorrectAnswerLimit.NotEmpty.message}")
+    @Min(value = 0, message = "{task.incorrectAnswerLimit.Min.message}")
+    @Max(value = 100, message = "{task.incorrectAnswerLimit.Max.message}")
+    private int incorrectAnswerLimit;
+    @ApiModelProperty(value = "Sign if sandbox should be modified if the task is picked.", example = "true")
+    private boolean modifySandbox;
+    @ApiModelProperty(value = "Expected duration of the sandbox change when the task is picked (in seconds).", example = "30")
+    @Min(value = 0, message = "{task.sandboxChangeExpectedDuration.Min.message}")
+    private int sandboxChangeExpectedDuration;
+
+    /**
+     * Gets answer.
+     *
+     * @return the answer
+     */
+    public String getAnswer() {
+        return answer;
+    }
+
+    /**
+     * Sets answer.
+     *
+     * @param answer the answer
+     */
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    /**
+     * Gets content.
+     *
+     * @return the content
+     */
+    public String getContent() {
+        return content;
+    }
+
+    /**
+     * Sets content.
+     *
+     * @param content the content
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    /**
+     * Gets solution.
+     *
+     * @return the solution
+     */
+    public String getSolution() {
+        return solution;
+    }
+
+    /**
+     * Sets solution.
+     *
+     * @param solution the solution
+     */
+    public void setSolution(String solution) {
+        this.solution = solution;
+    }
+
+    /**
+     * Gets incorrect answer limit.
+     *
+     * @return the incorrect answer limit
+     */
+    public int getIncorrectAnswerLimit() {
+        return incorrectAnswerLimit;
+    }
+
+    /**
+     * Sets incorrect answer limit.
+     *
+     * @param incorrectAnswerLimit the incorrect answer limit
+     */
+    public void setIncorrectAnswerLimit(int incorrectAnswerLimit) {
+        this.incorrectAnswerLimit = incorrectAnswerLimit;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public boolean isModifySandbox() {
+        return modifySandbox;
+    }
+
+    public void setModifySandbox(boolean modifySandbox) {
+        this.modifySandbox = modifySandbox;
+    }
+
+    public int getSandboxChangeExpectedDuration() {
+        return sandboxChangeExpectedDuration;
+    }
+
+    public void setSandboxChangeExpectedDuration(int sandboxChangeExpectedDuration) {
+        this.sandboxChangeExpectedDuration = sandboxChangeExpectedDuration;
+    }
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    @Override
+    public String toString() {
+        return "TaskImportDTO{" +
+                "title='" + title + '\'' +
+                ", answer='" + answer + '\'' +
+                ", content='" + content + '\'' +
+                ", solution='" + solution + '\'' +
+                ", incorrectAnswerLimit=" + incorrectAnswerLimit +
+                ", modifySandbox=" + modifySandbox +
+                ", sandboxChangeExpectedDuration=" + sandboxChangeExpectedDuration +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TrainingPhaseImportDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TrainingPhaseImportDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d746892d651988b7365f09a689aa77ed1bff288f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/imports/phases/training/TrainingPhaseImportDTO.java
@@ -0,0 +1,114 @@
+package cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training;
+
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.AbstractPhaseImportDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Min;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates information about training phase. Inherits from {@link AbstractPhaseImportDTO}
+ */
+@ApiModel(value = "TrainingPhaseImportDTO", description = "Imported training phase.", parent = AbstractPhaseImportDTO.class)
+public class TrainingPhaseImportDTO extends AbstractPhaseImportDTO {
+
+    @ApiModelProperty(value = "Estimated time it takes to finish the phase (in seconds).", example = "50")
+    @Min(value = 0, message = "{trainingPhase.estimatedDuration.Size.message}")
+    private int estimatedDuration;
+    @ApiModelProperty(value = "Number of allowed commands that can be used to solve the task (used for data analysis).", example = "10")
+    @Min(value = 0, message = "{trainingPhase.allowedCommands.Min.message}")
+    private int allowedCommands;
+    @ApiModelProperty(value = "How many times player can submit incorrect answer before displaying solution.", example = "4")
+    @Min(value = 0, message = "{trainingPhase.allowedWrongAnswers.Min.message}")
+    private int allowedWrongAnswers;
+    @Valid
+    private List<TaskImportDTO> tasks = new ArrayList<>();
+    @Valid
+    private List<DecisionMatrixRowImportDTO> decisionMatrix;
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public int getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(int estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    /**
+     * Gets allowed commands.
+     *
+     * @return the allowed commands
+     */
+    public int getAllowedCommands() {
+        return allowedCommands;
+    }
+
+    /**
+     * Sets allowed commands.
+     *
+     * @param allowedCommands the allowed commands
+     */
+    public void setAllowedCommands(int allowedCommands) {
+        this.allowedCommands = allowedCommands;
+    }
+
+    /**
+     * Gets allowed wrong answers.
+     *
+     * @return the allowed wrong answers
+     */
+    public int getAllowedWrongAnswers() {
+        return allowedWrongAnswers;
+    }
+
+    /**
+     * Sets allowed wrong answers.
+     *
+     * @param allowedWrongAnswers the allowed wrong answers
+     */
+    public void setAllowedWrongAnswers(int allowedWrongAnswers) {
+        this.allowedWrongAnswers = allowedWrongAnswers;
+    }
+
+
+    public List<TaskImportDTO> getTasks() {
+        return tasks;
+    }
+
+    public void setTasks(List<TaskImportDTO> tasks) {
+        this.tasks = tasks;
+    }
+
+    public List<DecisionMatrixRowImportDTO> getDecisionMatrix() {
+        return decisionMatrix;
+    }
+
+    public void setDecisionMatrix(List<DecisionMatrixRowImportDTO> decisionMatrix) {
+        this.decisionMatrix = decisionMatrix;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingPhaseImportDTO{" +
+                "estimatedDuration=" + estimatedDuration +
+                ", allowedCommands=" + allowedCommands +
+                ", allowedWrongAnswers=" + allowedWrongAnswers +
+                ", title='" + title + '\'' +
+                ", phaseType=" + phaseType +
+                ", order=" + order +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/questionnaire/QuestionnaireUpdateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/questionnaire/QuestionnaireUpdateDTO.java
index 4d1b34fa286897f06a1595922231b59b3f43d663..1b48e736e4ff2e5e367ea4008635f6c34319a566 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/questionnaire/QuestionnaireUpdateDTO.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/questionnaire/QuestionnaireUpdateDTO.java
@@ -18,7 +18,7 @@ public class QuestionnaireUpdateDTO {
     private List<QuestionDTO> questions;
 
     @Valid
-    @ApiModelProperty(value = "The relation between questions in the questionnaire and phases in the training definition", required = true)
+    @ApiModelProperty(value = "The relation between questions in the questionnaire and phase in the training definition", required = true)
     private List<QuestionPhaseRelationDTO> phaseRelations;
 
     public String getTitle() {
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/LockedPoolInfo.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/LockedPoolInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c44c2f110c6ae1ae0c99a57be915252b075807f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/LockedPoolInfo.java
@@ -0,0 +1,53 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+public class LockedPoolInfo {
+
+    @NotNull(message = "{pool.id.NotNull.message}")
+    private long id;
+    @NotNull(message = "{pool.poolId.NotNull.message}")
+    @JsonProperty(value = "pool_id")
+    private long poolId;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public long getPoolId() {
+        return poolId;
+    }
+
+    public void setPoolId(long poolId) {
+        this.poolId = poolId;
+    }
+
+    @Override
+    public String toString() {
+        return "LockedPoolInfo{" +
+                "id=" + id +
+                ", poolId=" + poolId +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof LockedPoolInfo)) return false;
+        LockedPoolInfo that = (LockedPoolInfo) o;
+        return getId() == that.getId() &&
+                getPoolId() == that.getPoolId();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getPoolId());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResource.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc682d31718e50d65d90c13a3496041e6ba9cc6b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResource.java
@@ -0,0 +1,137 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class is used to replace Page class and reduce number of returned elements (standard Page
+ * class contains fields, which are not usefull (backward compatability)).
+ */
+@ApiModel(value = "PageResultResouce", description = "Content (Retrieved data) and meta information about REST API result page. Including page number, number of elements in page, size of elements, total number of elements and total number of pages")
+public class PageResultResource<E> {
+
+    @JsonProperty(required = true)
+    @ApiModelProperty(value = "Content - (Retrieved data) from databases.")
+    private List<E> content;
+    @JsonProperty(required = true)
+    @ApiModelProperty(value = "Pagination including: page number, number of elements in page, size, total elements and total pages.")
+    private Pagination pagination;
+
+    public PageResultResource() {
+    }
+
+    public PageResultResource(List<E> content) {
+        super();
+        this.content = content;
+    }
+
+    public PageResultResource(List<E> content, Pagination pageMetadata) {
+        super();
+        this.content = content;
+        this.pagination = pageMetadata;
+    }
+
+    public List<E> getContent() {
+        return Collections.unmodifiableList(content);
+    }
+
+    public void setContent(List<E> content) {
+        this.content = content;
+    }
+
+    public Pagination getPagination() {
+        return pagination;
+    }
+
+    public void setPagination(Pagination pagination) {
+        this.pagination = pagination;
+    }
+
+    @Override
+    public String toString() {
+        return "PageResultDTO [content=" + content + ", pageMetadata=" + pagination + ", getContent()=" + getContent() + ", getPageMetadata()="
+                + getPagination() + "]";
+    }
+
+    public static class Pagination {
+
+        @ApiModelProperty(value = "Page number.", example = "1")
+        @JsonProperty(required = true)
+        private int number;
+        @ApiModelProperty(value = "Number of elements in page.", example = "20")
+        @JsonProperty(required = true, value = "number_of_elements")
+        private int numberOfElements;
+        @ApiModelProperty(value = "Page size.", example = "20")
+        @JsonProperty(required = true)
+        private int size;
+        @ApiModelProperty(value = "Total number of elements in this resource (in all Pages).", example = "100")
+        @JsonProperty(required = true, value = "total_elements")
+        private long totalElements;
+        @ApiModelProperty(value = "Total number of pages.", example = "5")
+        @JsonProperty(required = true, value = "total_pages")
+        private int totalPages;
+
+        public Pagination() {
+        }
+
+        public Pagination(int number, int numberOfElements, int size, long totalElements, int totalPages) {
+            super();
+            this.number = number;
+            this.numberOfElements = numberOfElements;
+            this.size = size;
+            this.totalElements = totalElements;
+            this.totalPages = totalPages;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+
+        public int getNumberOfElements() {
+            return numberOfElements;
+        }
+
+        public void setNumberOfElements(int numberOfElements) {
+            this.numberOfElements = numberOfElements;
+        }
+
+        public int getSize() {
+            return size;
+        }
+
+        public void setSize(int size) {
+            this.size = size;
+        }
+
+        public long getTotalElements() {
+            return totalElements;
+        }
+
+        public void setTotalElements(long totalElements) {
+            this.totalElements = totalElements;
+        }
+
+        public int getTotalPages() {
+            return totalPages;
+        }
+
+        public void setTotalPages(int totalPages) {
+            this.totalPages = totalPages;
+        }
+
+        @Override
+        public String toString() {
+            return "PageMetadata [number=" + number + ", numberOfElements=" + numberOfElements + ", size=" + size + ", totalElements="
+                    + totalElements + ", totalPages=" + totalPages + "]";
+        }
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResourcePython.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResourcePython.java
new file mode 100644
index 0000000000000000000000000000000000000000..149e6003d467f4b7839b644b430d35ed7dfabfed
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PageResultResourcePython.java
@@ -0,0 +1,146 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+/**
+ * This class is used to replace Page class and reduce number of returned elements. Used for responses from Python api.
+ */
+public class PageResultResourcePython<E> {
+    private int page;
+    @JsonProperty("page_size")
+    private int pageSize;
+    @JsonProperty("page_count")
+    private int pageCount;
+    private int count;
+    @JsonProperty("total_count")
+    private int totalCount;
+    private List<E> results;
+
+    /**
+     * Instantiates a new Page result resource python.
+     */
+    public PageResultResourcePython() {
+    }
+
+    /**
+     * Gets page.
+     *
+     * @return the page
+     */
+    public int getPage() {
+        return page;
+    }
+
+    /**
+     * Sets page.
+     *
+     * @param page the page
+     */
+    public void setPage(int page) {
+        this.page = page;
+    }
+
+    /**
+     * Gets page size.
+     *
+     * @return the page size
+     */
+    public int getPageSize() {
+        return pageSize;
+    }
+
+    /**
+     * Sets page size.
+     *
+     * @param pageSize the page size
+     */
+    public void setPageSize(int pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    /**
+     * Gets page count.
+     *
+     * @return the page count
+     */
+    public int getPageCount() {
+        return pageCount;
+    }
+
+    /**
+     * Sets page count.
+     *
+     * @param pageCount the page count
+     */
+    public void setPageCount(int pageCount) {
+        this.pageCount = pageCount;
+    }
+
+    /**
+     * Gets count.
+     *
+     * @return the count
+     */
+    public int getCount() {
+        return count;
+    }
+
+    /**
+     * Sets count.
+     *
+     * @param count the count
+     */
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    /**
+     * Gets total count.
+     *
+     * @return the total count
+     */
+    public int getTotalCount() {
+        return totalCount;
+    }
+
+    /**
+     * Sets total count.
+     *
+     * @param totalCount the total count
+     */
+    public void setTotalCount(int totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    /**
+     * Gets results.
+     *
+     * @return the results
+     */
+    public List<E> getResults() {
+        return results;
+    }
+
+    /**
+     * Sets results.
+     *
+     * @param results the results
+     */
+    public void setResults(List<E> results) {
+        this.results = results;
+    }
+
+    @Override
+    public String toString() {
+        return "PageResultResourcePython{" +
+                "page=" + page +
+                ", pageSize=" + pageSize +
+                ", pageCount=" + pageCount +
+                ", count=" + count +
+                ", totalCount=" + totalCount +
+                ", results=" + results +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PoolInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PoolInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..313cf3ef5dd5ad2e407189ca9d3a71cdbed0d71a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/PoolInfoDTO.java
@@ -0,0 +1,94 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import javax.validation.constraints.NotNull;
+
+public class PoolInfoDTO {
+    @NotNull(message = "{pool.id.NotNull.message}")
+    private Long id;
+    @NotNull(message = "{pool.definitionId.NotNull.message}")
+    @JsonProperty(value = "definition_id")
+    private Long definitionId;
+    @NotNull(message = "{pool.size.NotNull.message}")
+    private Long size;
+    @NotNull(message = "{pool.maxSize.NotNull.message}")
+    @JsonProperty(value = "max_size")
+    private Long maxSize;
+    @NotNull(message = "{pool.lockId.NotNull.message}")
+    @JsonProperty(value = "lock_id")
+    private Long lockId;
+    @JsonProperty(value = "rev")
+    private String rev;
+    @JsonProperty(value = "rev_sha")
+    private String revSha;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getDefinitionId() {
+        return definitionId;
+    }
+
+    public void setDefinitionId(Long definitionId) {
+        this.definitionId = definitionId;
+    }
+
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    public Long getMaxSize() {
+        return maxSize;
+    }
+
+    public void setMaxSize(Long maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    public Long getLockId() {
+        return lockId;
+    }
+
+    public void setLockId(Long lockId) {
+        this.lockId = lockId;
+    }
+
+    public String getRev() {
+        return rev;
+    }
+
+    public void setRev(String rev) {
+        this.rev = rev;
+    }
+
+    public String getRevSha() {
+        return revSha;
+    }
+
+    public void setRevSha(String revSha) {
+        this.revSha = revSha;
+    }
+
+    @Override
+    public String toString() {
+        return "PoolInfoDto{" +
+                "id=" + id +
+                ", definitionId=" + definitionId +
+                ", size=" + size +
+                ", maxSize=" + maxSize +
+                ", lockId=" + lockId +
+                ", rev=" + rev +
+                ", revSha=" + revSha +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxDefinitionInfo.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxDefinitionInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfda0c9e16356da7c078c609efa752a538b0cde0
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxDefinitionInfo.java
@@ -0,0 +1,69 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import io.swagger.annotations.ApiModel;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel(value = "SandboxDefinitionInfo", description = "Basic information about the sandbox definition.")
+public class SandboxDefinitionInfo {
+    @NotNull(message = "{sandboxDefinition.id.NotNull.message}")
+    private Long id;
+    @NotNull(message = "{sandboxDefinition.name.NotNull.message}")
+    private String name;
+    @NotNull(message = "{sandboxDefinition.url.NotNull.message}")
+    private String url;
+    @NotNull(message = "{sandboxDefinition.rev.NotNull.message}")
+    private String rev;
+
+    public SandboxDefinitionInfo() {
+    }
+
+    public SandboxDefinitionInfo(Long id, String name, String url, String rev) {
+        this.id = id;
+        this.name = name;
+        this.url = url;
+        this.rev = rev;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getRev() {
+        return rev;
+    }
+
+    public void setRev(String rev) {
+        this.rev = rev;
+    }
+
+    @Override
+    public String toString() {
+        return "SandboxDefinitionInfo{" +
+                "id=" + id +
+                ", name='" + name + '\'' +
+                ", url='" + url + '\'' +
+                ", rev='" + rev + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxInfo.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..a35e55277c2f2cbeed871ed86a801009977fe5c6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxInfo.java
@@ -0,0 +1,51 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Represents basic information about Sandbox.
+ */
+public class SandboxInfo {
+
+    @NotNull(message = "{sandbox.id.NotNull.message}")
+    private Long id;
+    @JsonProperty(value = "lock_id")
+    private Integer lockId;
+    @JsonProperty(value = "allocation_unit_id")
+    private Integer allocationUnitId;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getLockId() {
+        return lockId;
+    }
+
+    public void setLockId(Integer lockId) {
+        this.lockId = lockId;
+    }
+
+    public Integer getAllocationUnitId() {
+        return allocationUnitId;
+    }
+
+    public void setAllocationUnitId(Integer allocationUnitId) {
+        this.allocationUnitId = allocationUnitId;
+    }
+
+    @Override
+    public String toString() {
+        return "SandboxInfo{" +
+                "id=" + id +
+                ", lockId=" + lockId +
+                ", allocationUnitId=" + allocationUnitId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxPoolInfo.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxPoolInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..410df31a01b25f689dd9b37a2bfd7c775fbfd334
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/responses/SandboxPoolInfo.java
@@ -0,0 +1,103 @@
+package cz.muni.ics.kypo.training.adaptive.dto.responses;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Represents basic info about Sandbox pools.
+ */
+public class SandboxPoolInfo {
+    @NotNull(message = "{pool.id.NotNull.message}")
+    private Long id;
+    @NotNull(message = "{pool.definitionId.NotNull.message}")
+    @JsonProperty(value = "definition_id")
+    private Long definitionId;
+    @NotNull(message = "{pool.size.NotNull.message}")
+    private Long size;
+    @NotNull(message = "{pool.maxSize.NotNull.message}")
+    @JsonProperty(value = "max_size")
+    private Long maxSize;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets definitionId.
+     *
+     * @return the definitionId
+     */
+    public Long getDefinitionId() {
+        return definitionId;
+    }
+
+    /**
+     * Sets definitionId.
+     *
+     * @param definitionId the definitionId
+     */
+    public void setDefinitionId(Long definitionId) {
+        this.definitionId = definitionId;
+    }
+
+    /**
+     * Gets size.
+     *
+     * @return the size
+     */
+    public Long getSize() {
+        return size;
+    }
+
+    /**
+     * Sets size.
+     *
+     * @param size the size
+     */
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    /**
+     * Gets max size.
+     *
+     * @return the max size
+     */
+    public Long getMaxSize() {
+        return maxSize;
+    }
+
+    /**
+     * Sets max size.
+     *
+     * @param maxSize the max size
+     */
+    public void setMaxSize(Long maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    @Override
+    public String toString() {
+        return "SandboxPoolInfo{" +
+                "id=" + id +
+                ", definitionId=" + definitionId +
+                ", size='" + size + '\'' +
+                ", maxSize=" + maxSize +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/DecisionMatrixRowDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/DecisionMatrixRowDTO.java
index 73bff497613b0616331c3460a8989e55ab9eac29..5b8a64127d059cc60cf642e6de35397f99e67bbc 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/DecisionMatrixRowDTO.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/DecisionMatrixRowDTO.java
@@ -13,7 +13,7 @@ public class DecisionMatrixRowDTO {
     private int order;
 
     @ApiModelProperty(value = "It determines how important the answers of the questions in questionnaires are", required = true, example = "0.5")
-    private double assessmentAnswered;
+    private double questionnaireAnswered;
 
     @ApiModelProperty(value = "It determines how important it is whether the player used the keyword", required = true, example = "0.5")
     private double keywordUsed;
@@ -43,12 +43,12 @@ public class DecisionMatrixRowDTO {
         this.order = order;
     }
 
-    public double getAssessmentAnswered() {
-        return assessmentAnswered;
+    public double getQuestionnaireAnswered() {
+        return questionnaireAnswered;
     }
 
-    public void setAssessmentAnswered(double assessmentAnswered) {
-        this.assessmentAnswered = assessmentAnswered;
+    public void setQuestionnaireAnswered(double questionnaireAnswered) {
+        this.questionnaireAnswered = questionnaireAnswered;
     }
 
     public double getKeywordUsed() {
@@ -90,7 +90,7 @@ public class DecisionMatrixRowDTO {
         DecisionMatrixRowDTO that = (DecisionMatrixRowDTO) o;
         return id == that.id &&
                 order == that.order &&
-                Double.compare(that.assessmentAnswered, assessmentAnswered) == 0 &&
+                Double.compare(that.questionnaireAnswered, questionnaireAnswered) == 0 &&
                 Double.compare(that.keywordUsed, keywordUsed) == 0 &&
                 Double.compare(that.completedInTime, completedInTime) == 0 &&
                 Double.compare(that.solutionDisplayed, solutionDisplayed) == 0 &&
@@ -99,7 +99,7 @@ public class DecisionMatrixRowDTO {
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, order, assessmentAnswered, keywordUsed, completedInTime, solutionDisplayed, wrongAnswers);
+        return Objects.hash(id, order, questionnaireAnswered, keywordUsed, completedInTime, solutionDisplayed, wrongAnswers);
     }
 
     @Override
@@ -107,7 +107,7 @@ public class DecisionMatrixRowDTO {
         return "DecisionMatrixRow{" +
                 "id=" + id +
                 ", order=" + order +
-                ", assessmentAnswered=" + assessmentAnswered +
+                ", questionnaireAnswered=" + questionnaireAnswered +
                 ", keywordUsed=" + keywordUsed +
                 ", completedInTime=" + completedInTime +
                 ", solutionDisplayed=" + solutionDisplayed +
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/ValidateAnswerDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/ValidateAnswerDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..6693e8d5bbba47c5ec9266972fb64c9427631fde
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/training/ValidateAnswerDTO.java
@@ -0,0 +1,40 @@
+package cz.muni.ics.kypo.training.adaptive.dto.training;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.Objects;
+
+public class ValidateAnswerDTO {
+    @ApiModelProperty(value = "Answer to be validated.", required = true, example = "answer")
+    @NotEmpty(message = "{validateAnswer.answer.NotEmpty.message}")
+    private String answer;
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ValidateAnswerDTO)) return false;
+        ValidateAnswerDTO that = (ValidateAnswerDTO) o;
+        return Objects.equals(getAnswer(), that.getAnswer());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getAnswer());
+    }
+
+    @Override
+    public String toString() {
+        return "ValidateAnswerDTO{" +
+                "answer='" + answer + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionByIdDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionByIdDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..2dcfe060eb73ae1b13cdd8487e2e3b4e0592b4d6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionByIdDTO.java
@@ -0,0 +1,270 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about Training Definition.
+ */
+@ApiModel(value = "TrainingDefinitionByIdDTO", description = "A blueprint of abstract phase.")
+public class TrainingDefinitionByIdDTO {
+
+    @ApiModelProperty(value = "Main identifier of training definition.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    private TDState state;
+    @ApiModelProperty(value = "Information about all phase in training definition.")
+    private List<AbstractPhaseDTO> phases = new ArrayList<>();
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    private boolean showStepperBar;
+    @ApiModelProperty(value = "Sign if training definition can be archived or not.", example = "true")
+    private boolean canBeArchived;
+    @ApiModelProperty(value = "Estimated time it takes to finish runs created from this definition.", example = "5")
+    private long estimatedDuration;
+    @ApiModelProperty(value = "Time of last edit done to definition.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime lastEdited;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets phase.
+     *
+     * @return the list of {@link AbstractPhaseDTO}
+     */
+    public List<AbstractPhaseDTO> getPhases() {
+        return phases;
+    }
+
+    /**
+     * Sets phase.
+     *
+     * @param phases the list of {@link AbstractPhaseDTO}
+     */
+    public void setPhases(List<AbstractPhaseDTO> phases) {
+        this.phases = phases;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Definition can be archived if no associated instances are active.
+     *
+     * @return true if definition can be archived
+     */
+    public boolean isCanBeArchived() {
+        return canBeArchived;
+    }
+
+    /**
+     * Definition can be archived if no associated instances are active.
+     *
+     * @param canBeArchived true if definition can be archived
+     */
+    public void setCanBeArchived(boolean canBeArchived) {
+        this.canBeArchived = canBeArchived;
+    }
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public long getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(long estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    /**
+     * Gets time of last edit.
+     *
+     * @return the last edited
+     */
+    public LocalDateTime getLastEdited() {
+        return lastEdited;
+    }
+
+    /**
+     * Sets time of last edit.
+     *
+     * @param lastEdited the last edited
+     */
+    public void setLastEdited(LocalDateTime lastEdited) {
+        this.lastEdited = lastEdited;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof TrainingDefinitionByIdDTO)) return false;
+        TrainingDefinitionByIdDTO that = (TrainingDefinitionByIdDTO) object;
+        return Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                Objects.equals(getState(), that.getState());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getTitle(), getState());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionByIdDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                ", canBeArchived=" + canBeArchived +
+                ", estimatedDuration=" + estimatedDuration +
+                ", lastEdited=" + lastEdited +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionCreateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionCreateDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5447c96bae9b607a27aee2d1eb2f287aaddde0a9
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionCreateDTO.java
@@ -0,0 +1,150 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition;
+
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+
+/**
+ * Encapsulates information about Training definition, intended for creation of new definition.
+ */
+@ApiModel(value = "TrainingDefinitionCreateDTO", description = "Training definition to create.")
+public class TrainingDefinitionCreateDTO {
+
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", required = true, example = "Photo Hunter")
+    @NotEmpty(message = "{trainingDefinition.title.NotEmpty.message}")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Description of Photo Hunter")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "[HTML, http protocol]")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "[outcomes]")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", required = true, example = "UNRELEASED")
+    @NotNull(message = "{trainingDefinition.state.NotNull.message}")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", required = true, example = "true")
+    @NotNull(message = "{trainingDefinition.showStepperBar.NotNull.message}")
+    private Boolean showStepperBar;
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public Boolean getShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(Boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionCreateDTO{" +
+                "title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d818d6f71f04488b16858744f66b5da019c21d37
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionDTO.java
@@ -0,0 +1,253 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about Training Definition
+ */
+@ApiModel(value = "TrainingDefinitionDTO", description = "A blueprint of abstract phase.")
+public class TrainingDefinitionDTO {
+
+    @ApiModelProperty(value = "Main identifier of training definition.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    private boolean showStepperBar;
+    @ApiModelProperty(value = "Sign if training definition can be archived or not.", example = "false")
+    private boolean canBeArchived;
+    @ApiModelProperty(value = "Estimated time it takes to finish runs created from this definition.", example = "5")
+    private long estimatedDuration;
+    @ApiModelProperty(value = "Time of last edit done to definition.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime lastEdited;
+
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Definition can be archived if no associated instances are active.
+     *
+     * @return true if definition can be archived
+     */
+    public boolean isCanBeArchived() {
+        return canBeArchived;
+    }
+
+    /**
+     * Definition can be archived if no associated instances are active.
+     *
+     * @param canBeArchived true if definition can be archived
+     */
+    public void setCanBeArchived(boolean canBeArchived) {
+        this.canBeArchived = canBeArchived;
+    }
+
+    /**
+     * Gets estimated duration.
+     *
+     * @return the estimated duration
+     */
+    public long getEstimatedDuration() {
+        return estimatedDuration;
+    }
+
+    /**
+     * Sets estimated duration.
+     *
+     * @param estimatedDuration the estimated duration
+     */
+    public void setEstimatedDuration(long estimatedDuration) {
+        this.estimatedDuration = estimatedDuration;
+    }
+
+    /**
+     * Gets time of last edit.
+     *
+     * @return the last edited
+     */
+    public LocalDateTime getLastEdited() {
+        return lastEdited;
+    }
+
+    /**
+     * Sets time of last edit.
+     *
+     * @param lastEdited the last edited
+     */
+    public void setLastEdited(LocalDateTime lastEdited) {
+        this.lastEdited = lastEdited;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", prerequisites=" + Arrays.toString(prerequisites) +
+                ", outcomes=" + Arrays.toString(outcomes) +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                ", canBeArchived=" + canBeArchived +
+                ", estimatedDuration=" + estimatedDuration +
+                ", lastEdited=" + lastEdited +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof TrainingDefinitionDTO)) return false;
+        TrainingDefinitionDTO that = (TrainingDefinitionDTO) object;
+        return isCanBeArchived() == that.isCanBeArchived() &&
+                Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                Objects.equals(getDescription(), that.getDescription()) &&
+                Objects.equals(getState(), that.getState());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getTitle(), getDescription(), getState(), isCanBeArchived());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..36413379d66ac798a5d940f16bc5fc34200beac0
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionInfoDTO.java
@@ -0,0 +1,99 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition;
+
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Objects;
+
+/**
+ * Encapsulates basic information about Training Definition.
+ */
+@ApiModel(value = "TrainingDefinitionInfoDTO", description = "Basic training definition information.")
+public class TrainingDefinitionInfoDTO {
+
+    @ApiModelProperty(value = "Main identifier of training definition.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", example = "TrainingDefinition2")
+    private String title;
+    @ApiModelProperty(value = "Current state of training definition.", example = "UNRELEASED")
+    private TDState state;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TrainingDefinitionInfoDTO that = (TrainingDefinitionInfoDTO) o;
+        return Objects.equals(id, that.id) &&
+                Objects.equals(title, that.title) &&
+                state == that.state;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, title, state);
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionInfoDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", state=" + state +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionUpdateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionUpdateDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cccce0e32a30ce81e51bcd4bd24fd9bca24d79a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingdefinition/TrainingDefinitionUpdateDTO.java
@@ -0,0 +1,192 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition;
+
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+import java.util.Objects;
+
+
+/**
+ * Encapsulates information about Training Definition, intended for edit of the definition.
+ */
+@ApiModel(value = "TrainingDefinitionUpdateDTO", description = "Training definition to update.")
+public class TrainingDefinitionUpdateDTO {
+
+    @ApiModelProperty(value = "Main identifier of training definition.", required = true, example = "2")
+    @NotNull(message = "{trainingDefinition.id.NotNull.message}")
+    private Long id;
+    @ApiModelProperty(value = "A name of the training/game (e.g., Photo Hunter) .", required = true, example = "TrainingDefinition2")
+    @NotEmpty(message = "{trainingDefinition.title.NotEmpty.message}")
+    private String title;
+    @ApiModelProperty(value = "Description of training definition that is visible to the participant.", example = "Unreleased training definition")
+    private String description;
+    @ApiModelProperty(value = "List of knowledge and skills necessary to complete the training.", example = "[phishing]")
+    private String[] prerequisites;
+    @ApiModelProperty(value = "A list of knowledge and skills that the participant should learn by attending the training (if it is used for educational purposes) ", example = "")
+    private String[] outcomes;
+    @ApiModelProperty(value = "Current state of training definition.", required = true, example = "UNRELEASED")
+    @NotNull(message = "{trainingDefinition.state.NotNull.message}")
+    private TDState state;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", required = true, example = "false")
+    @NotNull(message = "{trainingDefinition.showStepperBar.NotNull.message}")
+    private boolean showStepperBar;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets description.
+     *
+     * @return the description
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets description.
+     *
+     * @param description the description
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Get prerequisites.
+     *
+     * @return the prerequisites.
+     */
+    public String[] getPrerequisites() {
+        return prerequisites;
+    }
+
+    /**
+     * Sets prerequisites.
+     *
+     * @param prerequisites the prerequisites.
+     */
+    public void setPrerequisites(String[] prerequisites) {
+        this.prerequisites = prerequisites;
+    }
+
+    /**
+     * Get outcomes.
+     *
+     * @return the outcomes
+     */
+    public String[] getOutcomes() {
+        return outcomes;
+    }
+
+    /**
+     * Sets outcomes.
+     *
+     * @param outcomes the outcomes
+     */
+    public void setOutcomes(String[] outcomes) {
+        this.outcomes = outcomes;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TDState}
+     */
+    public TDState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TDState}
+     */
+    public void setState(TDState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TrainingDefinitionUpdateDTO)) return false;
+        TrainingDefinitionUpdateDTO that = (TrainingDefinitionUpdateDTO) o;
+        return Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                Objects.equals(getDescription(), that.getDescription()) &&
+                getState() == that.getState();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getTitle(), getDescription(), getState());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingDefinitionUpdateDTO{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", description='" + description + '\'' +
+                ", prerequisites=" + Arrays.toString(prerequisites) +
+                ", outcomes=" + Arrays.toString(outcomes) +
+                ", state=" + state +
+                ", showStepperBar=" + showStepperBar +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceAssignPoolIdDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceAssignPoolIdDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd7e8ff4fc46467605062f50bfb21b6a28fe2728
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceAssignPoolIdDTO.java
@@ -0,0 +1,48 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about Training Instance, intended for assigning pool id.
+ */
+@ApiModel(value = "TrainingInstanceAssignPoolIdDTO", description = "Training Instance assign pool ID.")
+public class TrainingInstanceAssignPoolIdDTO {
+
+    @ApiModelProperty(value = "Pool associated with training instance.", example = "2", required = true)
+    @NotNull(message = "{assignPool.poolId.NotNull.message}")
+    @Min(value = 0, message = "{assignPool.poolId.Min.message}")
+    private Long poolId;
+
+    public Long getPoolId() {
+        return poolId;
+    }
+
+    public void setPoolId(Long poolId) {
+        this.poolId = poolId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TrainingInstanceAssignPoolIdDTO)) return false;
+        TrainingInstanceAssignPoolIdDTO that = (TrainingInstanceAssignPoolIdDTO) o;
+        return Objects.equals(getPoolId(), that.getPoolId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getPoolId());
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceAssignPoolIdDTO{" +
+                ", poolId=" + poolId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceBasicInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceBasicInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1948631e8263e08b692c1a4ddd05f6e6f55d9e2
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceBasicInfoDTO.java
@@ -0,0 +1,90 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training Instance
+ */
+@ApiModel(value = "TrainingInstanceBasicInfoDTO")
+public class TrainingInstanceBasicInfoDTO {
+
+    @ApiModelProperty(value = "Main identifier of training instance.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Date when training instance starts.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", example = "Concluded Instance")
+    private String title;
+    @ApiModelProperty(value = "Token used to access training run.", required = true, example = "hunter")
+    private String accessToken;
+    @ApiModelProperty(value = "Id of sandbox pool belonging to training instance", example = "1")
+    private Long poolId;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    public Long getPoolId() {
+        return poolId;
+    }
+
+    public void setPoolId(Long poolId) {
+        this.poolId = poolId;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceBasicInfoDTO{" +
+                "id=" + id +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", accessToken='" + accessToken + '\'' +
+                ", poolId=" + poolId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceCreateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceCreateDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ad3fdb406354271909b81a7d81fd6a7c787ef72
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceCreateDTO.java
@@ -0,0 +1,138 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCDeserializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training Instance, intended for creation of new instance.
+ */
+@ApiModel(value = "TrainingInstanceCreateDTO", description = "Training Instance to create.")
+public class TrainingInstanceCreateDTO {
+
+    @ApiModelProperty(value = "Date when training instance starts.", required = true, example = "2020-11-20T10:28:02.727Z")
+    @NotNull(message = "{trainingInstance.startTime.NotNull.message}")
+    @JsonDeserialize(using = LocalDateTimeUTCDeserializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", required = true, example = "2020-11-25T10:26:02.727Z")
+    @NotNull(message = "{trainingInstance.endTime.NotNull.message}")
+    @JsonDeserialize(using = LocalDateTimeUTCDeserializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", required = true, example = "December instance")
+    @NotEmpty(message = "{trainingInstance.title.NotEmpty.message}")
+    private String title;
+    @ApiModelProperty(value = "AccessToken which will be modified and then used for accessing training run.", required = true, example = "hunter")
+    @NotEmpty(message = "{trainingInstance.accessToken.NotEmpty.message}")
+    private String accessToken;
+    @ApiModelProperty(value = "Reference to training definition from which is training instance created.", required = true, example = "1")
+    @NotNull(message = "{trainingInstance.trainingDefinitionId.NotNull.message}")
+    @Min(value = 0, message = "{trainingInstance.trainingDefinitionId.Min.message}")
+    private Long trainingDefinitionId;
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets access token.
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token.
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets training definition id.
+     *
+     * @return the training definition id
+     */
+    public long getTrainingDefinitionId() {
+        return trainingDefinitionId;
+    }
+
+    /**
+     * Sets training definition id.
+     *
+     * @param trainingDefinitionId the training definition id
+     */
+    public void setTrainingDefinitionId(long trainingDefinitionId) {
+        this.trainingDefinitionId = trainingDefinitionId;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceCreateDTO{" +
+                "startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", accessToken='" + accessToken + '\'' +
+                ", trainingDefinitionId=" + trainingDefinitionId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a0ebc337b32aeb57e8ec92f1b2e1226c1b1c59a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceDTO.java
@@ -0,0 +1,204 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.TrainingDefinitionByIdDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about Training Instance
+ */
+@ApiModel(value = "TrainingInstanceDTO", description = "A session of attending a concrete training, which involves a deployment of the training definition in one or more sandbox instances that are then assigned to participants. The instance comprises one or more game runs.")
+public class TrainingInstanceDTO {
+
+    @ApiModelProperty(value = "Main identifier of training instance.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Date when training instance starts.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", example = "Concluded Instance")
+    private String title;
+    @ApiModelProperty(value = "Reference to training definition from which is training instance created.")
+    private TrainingDefinitionByIdDTO trainingDefinition;
+    @ApiModelProperty(value = "Token used to access training run.", required = true, example = "hunter")
+    private String accessToken;
+    @ApiModelProperty(value = "Id of sandbox pool belonging to training instance", example = "1")
+    private Long poolId;
+    @ApiModelProperty(value = "Ids of sandboxes which are assigned to training run.", example = "[3,15]")
+    private List<Long> sandboxesWithTrainingRun = new ArrayList<>();
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets training definition.
+     *
+     * @return the training definition
+     */
+    public TrainingDefinitionByIdDTO getTrainingDefinition() {
+        return trainingDefinition;
+    }
+
+    /**
+     * Sets training definition.
+     *
+     * @param trainingDefinition the training definition
+     */
+    public void setTrainingDefinition(TrainingDefinitionByIdDTO trainingDefinition) {
+        this.trainingDefinition = trainingDefinition;
+    }
+
+    /**
+     * Gets access token.
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token.
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets pool id.
+     *
+     * @return the pool id
+     */
+    public Long getPoolId() {
+        return poolId;
+    }
+
+    /**
+     * Sets pool id.
+     *
+     * @param poolId the pool id
+     */
+    public void setPoolId(Long poolId) {
+        this.poolId = poolId;
+    }
+
+    /**
+     * Gets sandboxes with training run.
+     *
+     * @return the sandboxes with training run
+     */
+    public List<Long> getSandboxesWithTrainingRun() {
+        return sandboxesWithTrainingRun;
+    }
+
+    /**
+     * Sets sandboxes with training run.
+     *
+     * @param sandboxesWithTrainingRun the sandboxes with training run
+     */
+    public void setSandboxesWithTrainingRun(List<Long> sandboxesWithTrainingRun) {
+        this.sandboxesWithTrainingRun = sandboxesWithTrainingRun;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceDTO{" + "id=" + id + ", startTime=" + startTime + ", endTime=" + endTime + ", title='" + title + '\''
+                + ", trainingDefinition=" + trainingDefinition + ", accessToken='"
+                + accessToken + '\'' + ", poolId=" + poolId + '}';
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof TrainingInstanceDTO)) return false;
+        TrainingInstanceDTO that = (TrainingInstanceDTO) object;
+        return Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                Objects.equals(getAccessToken(), that.getAccessToken()) &&
+                Objects.equals(getPoolId(), that.getPoolId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getTitle(), getAccessToken(), getPoolId());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceFindAllResponseDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceFindAllResponseDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5a873830a1f5ef6daaf9d229b3f50d50ca2ea16
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceFindAllResponseDTO.java
@@ -0,0 +1,175 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.TrainingDefinitionByIdDTO;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates basic information about Training Instance
+ */
+public class TrainingInstanceFindAllResponseDTO {
+
+    @ApiModelProperty(value = "Main identifier of training instance.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Date when training instance starts.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", example = "2017-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", example = "Concluded Instance")
+    private String title;
+    @ApiModelProperty(value = "Reference to training definition from which is training instance created.")
+    private TrainingDefinitionByIdDTO trainingDefinition;
+    @ApiModelProperty(value = "Token used to access training run.", required = true, example = "hunter")
+    private String accessToken;
+    @ApiModelProperty(value = "Id of sandbox pool belonging to training instance", example = "1")
+    private Long poolId;
+
+    /**
+     * Instantiates a new Training instance find all response dto.
+     */
+    public TrainingInstanceFindAllResponseDTO() {
+    }
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets training definition.
+     *
+     * @return the training definition
+     */
+    public TrainingDefinitionByIdDTO getTrainingDefinition() {
+        return trainingDefinition;
+    }
+
+    /**
+     * Sets training definition.
+     *
+     * @param trainingDefinition the training definition
+     */
+    public void setTrainingDefinition(TrainingDefinitionByIdDTO trainingDefinition) {
+        this.trainingDefinition = trainingDefinition;
+    }
+
+    /**
+     * Gets access token.
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token.
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets pool id.
+     *
+     * @return the pool id
+     */
+    public Long getPoolId() {
+        return poolId;
+    }
+
+    /**
+     * Sets pool id.
+     *
+     * @param poolId the pool id
+     */
+    public void setPoolId(Long poolId) {
+        this.poolId = poolId;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceFindAllResponseDTO{" +
+                "id=" + id +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", accessToken='" + accessToken + '\'' +
+                ", poolId=" + poolId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceIsFinishedInfoDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceIsFinishedInfoDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0bed508114bd1e77f1cf660dd1756af139aa76e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceIsFinishedInfoDTO.java
@@ -0,0 +1,60 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Encapsulates the information stating if training instance has finished.
+ */
+@ApiModel(value = "TrainingInstanceIsFinishedInfoDTO", description = "Information stating if training instance has finished.")
+public class TrainingInstanceIsFinishedInfoDTO {
+
+    @ApiModelProperty(value = "Sign if training instance is finished or not.", example = "false")
+    private boolean hasFinished;
+    @ApiModelProperty(value = "Message about training instance state.", example = "false")
+    private String message;
+
+    /**
+     * Get if instance has finished.
+     *
+     * @return the boolean
+     */
+    public boolean getHasFinished() {
+        return hasFinished;
+    }
+
+    /**
+     * Sets if instance has finished.
+     *
+     * @param hasFinished the has finished
+     */
+    public void setHasFinished(boolean hasFinished) {
+        this.hasFinished = hasFinished;
+    }
+
+    /**
+     * Gets message.
+     *
+     * @return the message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Sets message.
+     *
+     * @param message the message
+     */
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceIsFinishedInfoDTO{" +
+                "hasFinished=" + hasFinished +
+                ", message='" + message + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceUpdateDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceUpdateDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..acbf815635d3ba903cc0938a29be385882d9c85a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/traininginstance/TrainingInstanceUpdateDTO.java
@@ -0,0 +1,159 @@
+package cz.muni.ics.kypo.training.adaptive.dto.traininginstance;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCDeserializer;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training Instance, intended for edit of the instance.
+ */
+@ApiModel(value = "TrainingInstanceUpdateDTO", description = "Training Instance to update.")
+public class TrainingInstanceUpdateDTO {
+
+    @ApiModelProperty(value = "Main identifier of training instance.", required = true, example = "2")
+    @NotNull(message = "{trainingInstance.id.NotNull.message}")
+    private Long id;
+    @ApiModelProperty(value = "Date when training instance starts.", required = true, example = "2019-10-19T10:28:02.727Z")
+    @NotNull(message = "{trainingInstance.startTime.NotNull.message}")
+    @JsonDeserialize(using = LocalDateTimeUTCDeserializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training instance ends.", required = true, example = "2019-10-25T10:28:02.727Z")
+    @NotNull(message = "{trainingInstance.endTime.NotNull.message}")
+    @JsonDeserialize(using = LocalDateTimeUTCDeserializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Short textual description of the training instance.", required = true, example = "Current Instance")
+    @NotEmpty(message = "{trainingInstance.title.NotEmpty.message}")
+    private String title;
+    @ApiModelProperty(value = "AccessToken which will be modified and then used for accessing training run.", required = true, example = "hello-6578")
+    @NotEmpty(message = "{trainingInstance.accessToken.NotEmpty.message}")
+    private String accessToken;
+    @ApiModelProperty(value = "Reference to training definition from which is training instance created.", required = true, example = "1")
+    @NotNull(message = "{trainingInstance.trainingDefinitionId.NotNull.message}")
+    private Long trainingDefinitionId;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets access token.
+     *
+     * @return the access token
+     */
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    /**
+     * Sets access token.
+     *
+     * @param accessToken the access token
+     */
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
+
+    /**
+     * Gets training definition id.
+     *
+     * @return the training definition id
+     */
+    public Long getTrainingDefinitionId() {
+        return trainingDefinitionId;
+    }
+
+    /**
+     * Sets training definition id.
+     *
+     * @param trainingDefinitionId the training definition id
+     */
+    public void setTrainingDefinitionId(Long trainingDefinitionId) {
+        this.trainingDefinitionId = trainingDefinitionId;
+    }
+
+
+    @Override
+    public String toString() {
+        return "TrainingInstanceUpdateDTO{" +
+                "id=" + id +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", title='" + title + '\'' +
+                ", accessToken='" + accessToken + '\'' +
+                ", trainingDefinitionId=" + trainingDefinitionId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessTrainingRunDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessTrainingRunDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f5474e302d358bb8f082785dce288b13b3585da
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessTrainingRunDTO.java
@@ -0,0 +1,187 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingrun;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.BasicPhaseInfoDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * Encapsulates information about Training Run, intended as a response to run accessing.
+ */
+@ApiModel(value = "AccessTrainingRunDTO", description = "Just accessed training run.")
+public class AccessTrainingRunDTO {
+
+    @ApiModelProperty(value = "Main identifier of training run.", example = "1")
+    private Long trainingRunID;
+    @ApiModelProperty(value = "Sign if stepper bar should be displayed.", example = "false")
+    private boolean showStepperBar;
+    @ApiModelProperty(value = "Main identifier of sandbox which is assigned to training run.", example = "2")
+    private Long sandboxInstanceRefId;
+    @ApiModelProperty(value = "First phase in the current training run.")
+    private AbstractPhaseDTO abstractPhaseDTO;
+    @ApiModelProperty(value = "Information about all phase in training instance.")
+    private List<BasicPhaseInfoDTO> infoAboutPhases;
+    @ApiModelProperty(value = "Id of associated training instance", example = "1")
+    private Long instanceId;
+    @ApiModelProperty(value = "Date when training run started.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Sign if solution of current training phase was taken", example = "true")
+    private String takenSolution;
+
+    /**
+     * Gets training run id.
+     *
+     * @return the training run id
+     */
+    public Long getTrainingRunID() {
+        return trainingRunID;
+    }
+
+    /**
+     * Sets training run id.
+     *
+     * @param trainingRunID the training run id
+     */
+    public void setTrainingRunID(Long trainingRunID) {
+        this.trainingRunID = trainingRunID;
+    }
+
+    /**
+     * Gets if stepper bar is shown while in run.
+     *
+     * @return true if bar is shown
+     */
+    public boolean isShowStepperBar() {
+        return showStepperBar;
+    }
+
+    /**
+     * Sets if stepper bar is shown while in run.
+     *
+     * @param showStepperBar true if bar is shown
+     */
+    public void setShowStepperBar(boolean showStepperBar) {
+        this.showStepperBar = showStepperBar;
+    }
+
+    /**
+     * Gets sandbox instance id.
+     *
+     * @return the sandbox instance id
+     */
+    public Long getSandboxInstanceRefId() {
+        return sandboxInstanceRefId;
+    }
+
+    /**
+     * Sets sandbox instance id.
+     *
+     * @param sandboxInstanceRefId the sandbox instance id
+     */
+    public void setSandboxInstanceRefId(Long sandboxInstanceRefId) {
+        this.sandboxInstanceRefId = sandboxInstanceRefId;
+    }
+
+    /**
+     * Gets current phase.
+     *
+     * @return the {@link AbstractPhaseDTO}
+     */
+    public AbstractPhaseDTO getAbstractPhaseDTO() {
+        return abstractPhaseDTO;
+    }
+
+    /**
+     * Sets current phase.
+     *
+     * @param abstractPhaseDTO the {@link AbstractPhaseDTO}
+     */
+    public void setAbstractPhaseDTO(AbstractPhaseDTO abstractPhaseDTO) {
+        this.abstractPhaseDTO = abstractPhaseDTO;
+    }
+
+    /**
+     * Gets basic info about all phase.
+     *
+     * @return the list of {@link BasicPhaseInfoDTO}
+     */
+    public List<BasicPhaseInfoDTO> getInfoAboutPhases() {
+        return infoAboutPhases;
+    }
+
+    /**
+     * Sets basic info about all phase.
+     *
+     * @param infoAboutPhases the list of {@link BasicPhaseInfoDTO}
+     */
+    public void setInfoAboutPhases(List<BasicPhaseInfoDTO> infoAboutPhases) {
+        this.infoAboutPhases = infoAboutPhases;
+    }
+
+    /**
+     * Gets instance id.
+     *
+     * @return the instance id
+     */
+    public Long getInstanceId() {
+        return instanceId;
+    }
+
+    /**
+     * Sets instance id.
+     *
+     * @param instanceId the instance id
+     */
+    public void setInstanceId(Long instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets taken solution.
+     *
+     * @return the taken solution
+     */
+    public String getTakenSolution() {
+        return takenSolution;
+    }
+
+    /**
+     * Sets taken solution.
+     *
+     * @param takenSolution the taken solution
+     */
+    public void setTakenSolution(String takenSolution) {
+        this.takenSolution = takenSolution;
+    }
+
+    @Override
+    public String toString() {
+        return "AccessTrainingRunDTO{" + "trainingRunID=" + trainingRunID + ", showStepperBar=" + showStepperBar + ", sandboxInstanceRefId="
+                + sandboxInstanceRefId + ", abstractPhaseDTO=" + abstractPhaseDTO + ", infoAboutPhases=" + infoAboutPhases + ", instanceId="
+                + instanceId + ", startTime=" + startTime + '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessedTrainingRunDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessedTrainingRunDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..af2bc13b3769bab78b53c9a2cc1626a0556afc76
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/AccessedTrainingRunDTO.java
@@ -0,0 +1,204 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingrun;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.enums.Actions;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about already accessed training run.
+ */
+@ApiModel(value = "AccessedTrainingRunDTO", description = "Already accessed training run by some participant.")
+public class AccessedTrainingRunDTO {
+
+    @ApiModelProperty(value = "Main identifier of training run.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Short textual description of the training instance.", example = "Concluded Instance")
+    private String title;
+    @ApiModelProperty(value = "Start date of training instance for which the training run was created.", example = "2016-10-19T10:23:54")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime trainingInstanceStartDate;
+    @ApiModelProperty(value = "End date of training instance for which the training run was created.", example = "2017-10-19T10:23:54")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime trainingInstanceEndDate;
+    @ApiModelProperty(value = "Current phase order of training run.", example = "1")
+    private int currentPhaseOrder;
+    @ApiModelProperty(value = "The number of phase in the training instance.", example = "3")
+    private int numberOfPhases;
+    @ApiModelProperty(value = "Possible action which can be executed with training Run.", example = "RESULTS")
+    private Actions possibleAction;
+    @ApiModelProperty(value = "Id of associated training instance", example = "1")
+    private Long instanceId;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets title.
+     *
+     * @return the title
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets title.
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    /**
+     * Gets start date of training instance.
+     *
+     * @return the training instance start date
+     */
+    public LocalDateTime getTrainingInstanceStartDate() {
+        return trainingInstanceStartDate;
+    }
+
+    /**
+     * Sets start date of training instance.
+     *
+     * @param trainingInstanceStartDate the training instance start date
+     */
+    public void setTrainingInstanceStartDate(LocalDateTime trainingInstanceStartDate) {
+        this.trainingInstanceStartDate = trainingInstanceStartDate;
+    }
+
+    /**
+     * Gets end date of training instance.
+     *
+     * @return the training instance end date
+     */
+    public LocalDateTime getTrainingInstanceEndDate() {
+        return trainingInstanceEndDate;
+    }
+
+    /**
+     * Sets end date of training instance.
+     *
+     * @param trainingInstanceEndDate the training instance end date
+     */
+    public void setTrainingInstanceEndDate(LocalDateTime trainingInstanceEndDate) {
+        this.trainingInstanceEndDate = trainingInstanceEndDate;
+    }
+
+    /**
+     * Gets current phase order.
+     *
+     * @return the current phase order
+     */
+    public int getCurrentPhaseOrder() {
+        return currentPhaseOrder;
+    }
+
+    /**
+     * Sets current phase order.
+     *
+     * @param currentPhaseOrder the current phase order
+     */
+    public void setCurrentPhaseOrder(int currentPhaseOrder) {
+        this.currentPhaseOrder = currentPhaseOrder;
+    }
+
+    /**
+     * Gets number of phase.
+     *
+     * @return the number of phase
+     */
+    public int getNumberOfPhases() {
+        return numberOfPhases;
+    }
+
+    /**
+     * Sets number of phase.
+     *
+     * @param numberOfPhases the number of phase
+     */
+    public void setNumberOfPhases(int numberOfPhases) {
+        this.numberOfPhases = numberOfPhases;
+    }
+
+    /**
+     * Gets possible action.
+     *
+     * @return the possible {@link Actions}
+     */
+    public Actions getPossibleAction() {
+        return possibleAction;
+    }
+
+    /**
+     * Sets possible action.
+     *
+     * @param possibleAction the possible {@link Actions}
+     */
+    public void setPossibleAction(Actions possibleAction) {
+        this.possibleAction = possibleAction;
+    }
+
+    /**
+     * Gets instance id.
+     *
+     * @return the instance id
+     */
+    public Long getInstanceId() {
+        return instanceId;
+    }
+
+    /**
+     * Sets instance id.
+     *
+     * @param instanceId the instance id
+     */
+    public void setInstanceId(Long instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    @Override
+    public String toString() {
+        return "AccessedTrainingRunDTO{" + "id=" + id + ", title='" + title + '\'' + ", trainingInstanceStartDate="
+                + trainingInstanceStartDate + ", trainingInstanceEndDate=" + trainingInstanceEndDate + ", currentPhaseOrder="
+                + currentPhaseOrder + ", numberOfPhases=" + numberOfPhases + ", possibleAction=" + possibleAction + ", instanceId=" + instanceId
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof AccessedTrainingRunDTO)) return false;
+        AccessedTrainingRunDTO that = (AccessedTrainingRunDTO) object;
+        return Objects.equals(getCurrentPhaseOrder(), that.getCurrentPhaseOrder()) &&
+                Objects.equals(getNumberOfPhases(), that.getNumberOfPhases()) &&
+                Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getTitle(), that.getTitle()) &&
+                Objects.equals(getInstanceId(), that.getInstanceId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getTitle(), getCurrentPhaseOrder(), getNumberOfPhases(), getInstanceId());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunByIdDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunByIdDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..16d43a2b78b4c932fae656716baa8fa370f06033
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunByIdDTO.java
@@ -0,0 +1,216 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingrun;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+
+/**
+ * Encapsulates information about Training Run.
+ */
+@ApiModel(value = "TrainingRunByIdDTO", description = "The act, or a recording, of performing actions during training from a perspective of one concrete participant.")
+public class TrainingRunByIdDTO {
+
+    @ApiModelProperty(value = "Main identifier of training run.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Date when training run started.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training run ends.", example = "2022-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Current state of training run.", example = "ALLOCATED")
+    private TRState state;
+    @ApiModelProperty(value = "Reference to the received sandbox.")
+    private Long sandboxInstanceRefId;
+    @ApiModelProperty(value = "Reference to participant of training run.")
+    private UserRefDTO participantRef;
+    @ApiModelProperty(value = "Id of associated training definition")
+    private Long definitionId;
+    @ApiModelProperty(value = "Id of associated training instance")
+    private Long instanceId;
+    @ApiModelProperty(value = "Id of a previous sandbox instance assigned to the training run.", example = "12")
+    private Long previousSandboxInstanceRefId;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TRState}
+     */
+    public TRState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TRState}
+     */
+    public void setState(TRState state) {
+        this.state = state;
+    }
+
+
+    /**
+     * Gets sandbox instance id.
+     *
+     * @return the sandbox instance id
+     */
+    public Long getSandboxInstanceRefId() {
+        return sandboxInstanceRefId;
+    }
+
+    /**
+     * Sets sandbox instance id.
+     *
+     * @param sandboxInstanceRefId the sandbox instance id
+     */
+    public void setSandboxInstanceRefId(Long sandboxInstanceRefId) {
+        this.sandboxInstanceRefId = sandboxInstanceRefId;
+    }
+
+    /**
+     * Gets participant ref.
+     *
+     * @return the {@link UserRefDTO}
+     */
+    public UserRefDTO getParticipantRef() {
+        return participantRef;
+    }
+
+    /**
+     * Sets participant ref.
+     *
+     * @param participantRef the {@link UserRefDTO}
+     */
+    public void setParticipantRef(UserRefDTO participantRef) {
+        this.participantRef = participantRef;
+    }
+
+    /**
+     * Gets definition id.
+     *
+     * @return the definition id
+     */
+    public Long getDefinitionId() {
+        return definitionId;
+    }
+
+    /**
+     * Sets definition id.
+     *
+     * @param definitionId the definition id
+     */
+    public void setDefinitionId(Long definitionId) {
+        this.definitionId = definitionId;
+    }
+
+    /**
+     * Gets instance id.
+     *
+     * @return the instance id
+     */
+    public Long getInstanceId() {
+        return instanceId;
+    }
+
+    /**
+     * Sets instance id.
+     *
+     * @param instanceId the instance id
+     */
+    public void setInstanceId(Long instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    /**
+     * Gets ID of previous used sandbox instance ref.
+     *
+     * @return the previous sandbox instance ref id
+     */
+    public Long getPreviousSandboxInstanceRefId() {
+        return previousSandboxInstanceRefId;
+    }
+
+    /**
+     * Sets ID of previous used sandbox instance ref.
+     *
+     * @param previousSandboxInstanceRefId the previous sandbox instance ref id
+     */
+    public void setPreviousSandboxInstanceRefId(Long previousSandboxInstanceRefId) {
+        this.previousSandboxInstanceRefId = previousSandboxInstanceRefId;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingRunByIdDTO{" +
+                "id=" + id +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", state=" + state +
+                ", sandboxInstanceRefId=" + sandboxInstanceRefId +
+                ", participantRef=" + participantRef +
+                ", definitionId=" + definitionId +
+                ", instanceId=" + instanceId +
+                ", previousSandboxInstanceRefId=" + previousSandboxInstanceRefId +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunDTO.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..b084265a87673cd90e3555598b6d8eba5c3e64db
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/dto/trainingrun/TrainingRunDTO.java
@@ -0,0 +1,167 @@
+package cz.muni.ics.kypo.training.adaptive.dto.trainingrun;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import cz.muni.ics.kypo.training.adaptive.converter.LocalDateTimeUTCSerializer;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+/**
+ * Encapsulates information about Training Run.
+ */
+@ApiModel(value = "TrainingRunDTO", description = "The act, or a recording, of performing actions during training from a perspective of one concrete participant.")
+public class TrainingRunDTO {
+
+    @ApiModelProperty(value = "Main identifier of training run.", example = "1")
+    private Long id;
+    @ApiModelProperty(value = "Date when training run started.", example = "2016-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime startTime;
+    @ApiModelProperty(value = "Date when training run ends.", example = "2022-10-19 10:23:54+02")
+    @JsonSerialize(using = LocalDateTimeUTCSerializer.class)
+    private LocalDateTime endTime;
+    @ApiModelProperty(value = "Current state of training run.", example = "ALLOCATED")
+    private TRState state;
+    @ApiModelProperty(value = "Reference to the received sandbox.")
+    private Long sandboxInstanceRefId;
+    @ApiModelProperty(value = "Reference to participant of training run.")
+    private UserRefDTO participantRef;
+
+    /**
+     * Gets id.
+     *
+     * @return the id
+     */
+    public Long getId() {
+        return id;
+    }
+
+    /**
+     * Sets id.
+     *
+     * @param id the id
+     */
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    /**
+     * Gets start time.
+     *
+     * @return the start time
+     */
+    public LocalDateTime getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Sets start time.
+     *
+     * @param startTime the start time
+     */
+    public void setStartTime(LocalDateTime startTime) {
+        this.startTime = startTime;
+    }
+
+    /**
+     * Gets end time.
+     *
+     * @return the end time
+     */
+    public LocalDateTime getEndTime() {
+        return endTime;
+    }
+
+    /**
+     * Sets end time.
+     *
+     * @param endTime the end time
+     */
+    public void setEndTime(LocalDateTime endTime) {
+        this.endTime = endTime;
+    }
+
+    /**
+     * Gets state.
+     *
+     * @return the {@link TRState}
+     */
+    public TRState getState() {
+        return state;
+    }
+
+    /**
+     * Sets state.
+     *
+     * @param state the {@link TRState}
+     */
+    public void setState(TRState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets sandbox instance ref.
+     *
+     * @return the sandbox instance ref id
+     */
+    public Long getSandboxInstanceRefId() {
+        return sandboxInstanceRefId;
+    }
+
+    /**
+     * Sets sandbox instance ref.
+     *
+     * @param sandboxInstanceRefId the sandbox instance ref id
+     */
+    public void setSandboxInstanceRefId(Long sandboxInstanceRefId) {
+        this.sandboxInstanceRefId = sandboxInstanceRefId;
+    }
+
+    /**
+     * Gets participant ref.
+     *
+     * @return the {@link UserRefDTO}
+     */
+    public UserRefDTO getParticipantRef() {
+        return participantRef;
+    }
+
+    /**
+     * Sets participant ref.
+     *
+     * @param participantRef the {@link UserRefDTO}
+     */
+    public void setParticipantRef(UserRefDTO participantRef) {
+        this.participantRef = participantRef;
+    }
+
+    @Override
+    public String toString() {
+        return "TrainingRunDTO{" +
+                "id=" + id +
+                ", startTime=" + startTime +
+                ", endTime=" + endTime +
+                ", state=" + state +
+                ", sandboxInstanceRefId=" + sandboxInstanceRefId +
+                ", participantRef=" + participantRef +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof TrainingRunDTO)) return false;
+        TrainingRunDTO that = (TrainingRunDTO) object;
+        return Objects.equals(getId(), that.getId()) &&
+                Objects.equals(getState(), that.getState()) &&
+                Objects.equals(getParticipantRef(), that.getParticipantRef());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getId(), getState(), getParticipantRef());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/Actions.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/Actions.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdeec1599bbc8545a232c5e6a9171c6c00985aac
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/Actions.java
@@ -0,0 +1,21 @@
+package cz.muni.ics.kypo.training.adaptive.enums;
+
+/**
+ * The enumeration of Actions.
+ */
+public enum Actions {
+
+    /**
+     * None actions.
+     */
+    NONE,
+    /**
+     * Shows results of finished training runs.
+     */
+    RESULTS,
+
+    /**
+     * Resume actions.
+     */
+    RESUME;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/PhaseTypeCreate.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/PhaseTypeCreate.java
deleted file mode 100644
index 4995bb68e1b79fe521b0ed61c35737fb115d66dc..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/PhaseTypeCreate.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.enums;
-
-public enum PhaseTypeCreate {
-    QUESTIONNAIRE_ADAPTIVE,
-    QUESTIONNAIRE_GENERAL,
-    INFO,
-    TRAINING;
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleType.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6df0df45ebbdfd9e173c00c4a743f3378fb4f623
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleType.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.enums;
+
+/**
+ * The enumeration of user roles.
+ */
+public enum RoleType {
+
+    /**
+     * Role training administrator role.
+     */
+    ROLE_TRAINING_ADMINISTRATOR,
+    /**
+     * Role training designer role.
+     */
+    ROLE_TRAINING_DESIGNER,
+    /**
+     * Role training organizer role.
+     */
+    ROLE_TRAINING_ORGANIZER,
+    /**
+     * Role training trainee role.
+     */
+    ROLE_TRAINING_TRAINEE;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleTypeSecurity.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleTypeSecurity.java
new file mode 100644
index 0000000000000000000000000000000000000000..d585095a16a8be2e63f595be5804aa216f02748b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/RoleTypeSecurity.java
@@ -0,0 +1,23 @@
+package cz.muni.ics.kypo.training.adaptive.enums;
+
+/**
+ * The enumeration of Role types used for security.
+ */
+public enum RoleTypeSecurity {
+    /**
+     * Role of training administrator.
+     */
+    ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR,
+    /**
+     * Role of training designer permits user to work with training definitions.
+     */
+    ROLE_ADAPTIVE_TRAINING_DESIGNER,
+    /**
+     * Role of training organizer permits user to work with training instances.
+     */
+    ROLE_ADAPTIVE_TRAINING_ORGANIZER,
+    /**
+     * Role of training trainee permits user to work with training runs.
+     */
+    ROLE_ADAPTIVE_TRAINING_TRAINEE
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TDState.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TDState.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab0b742292ba0d61839acfab33a946922f94dadd
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TDState.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.enums;
+
+/**
+ * States represented in Training Definition entity.
+ */
+public enum TDState {
+
+    /**
+     * Privated definition state.
+     */
+    PRIVATED,
+    /**
+     * Released definition state.
+     */
+    RELEASED,
+    /**
+     * Archived definition state.
+     */
+    ARCHIVED,
+    /**
+     * Unreleased definition state.
+     */
+    UNRELEASED;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TRState.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TRState.java
new file mode 100644
index 0000000000000000000000000000000000000000..bed66770064458df73a73c7badb017900cec4656
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/enums/TRState.java
@@ -0,0 +1,20 @@
+package cz.muni.ics.kypo.training.adaptive.enums;
+
+/**
+ * States represented in Training Run entity.
+ */
+public enum TRState {
+
+    /**
+     * Running run state.
+     */
+    RUNNING,
+    /**
+     * Finished run state.
+     */
+    FINISHED,
+    /**
+     * Archived run state.
+     */
+    ARCHIVED;
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/BadRequestException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/BadRequestException.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e011b8e2e896deb6600bc3f824332a2d9a06dc4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/BadRequestException.java
@@ -0,0 +1,28 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.BAD_REQUEST,
+        reason = "The server cannot or will not process the request due to an apparent client error (e.g., malformed request syntax, size too large, invalid request message framing, or deceptive request routing).")
+public class BadRequestException extends RuntimeException {
+
+    public BadRequestException() {
+    }
+
+    public BadRequestException(String message) {
+        super(message);
+    }
+
+    public BadRequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public BadRequestException(Throwable cause) {
+        super(cause);
+    }
+
+    public BadRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/CustomWebClientException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/CustomWebClientException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8da9ef4c0f0903cfd12a49f6dcd503244a46e71
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/CustomWebClientException.java
@@ -0,0 +1,89 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiSubError;
+import org.springframework.http.HttpStatus;
+
+/**
+ * The type Rest template exception.
+ */
+public class CustomWebClientException extends RuntimeException {
+    private HttpStatus statusCode;
+    private ApiSubError apiSubError;
+
+    /**
+     * Instantiates a new Rest template exception.
+     *
+     * @param message    the message
+     * @param statusCode the status code
+     */
+    public CustomWebClientException(String message, HttpStatus statusCode) {
+        super(message);
+        this.statusCode = statusCode;
+    }
+
+    /**
+     * Instantiates a new Rest template exception.
+     *
+     * @param message    the message
+     * @param ex         the ex
+     * @param statusCode the status code
+     */
+    public CustomWebClientException(String message, Throwable ex, HttpStatus statusCode) {
+        super(message, ex);
+        this.statusCode = statusCode;
+    }
+
+    /**
+     * Instantiates a new Rest template exception.
+     *
+     * @param apiSubError detailed information about error.
+     */
+    public CustomWebClientException(ApiSubError apiSubError) {
+        super();
+        this.apiSubError = apiSubError;
+        this.statusCode = apiSubError.getStatus();
+    }
+
+    /**
+     * Instantiates a new Rest template exception.
+     *
+     * @param message     the message
+     * @param apiSubError detailed information about error
+     */
+    public CustomWebClientException(String message, ApiSubError apiSubError) {
+        super(message);
+        this.apiSubError = apiSubError;
+        this.statusCode = apiSubError.getStatus();
+    }
+
+    /**
+     * Instantiates a new Rest template exception.
+     *
+     * @param message     the message
+     * @param ex          the ex
+     * @param apiSubError detailed information about error
+     */
+    public CustomWebClientException(String message, Throwable ex, ApiSubError apiSubError) {
+        super(message, ex);
+        this.apiSubError = apiSubError;
+        this.statusCode = apiSubError.getStatus();
+    }
+
+    /**
+     * Gets detailed information about error.
+     *
+     * @return detailed information about error
+     */
+    public ApiSubError getApiSubError() {
+        return apiSubError;
+    }
+
+    /**
+     * Gets status code.
+     *
+     * @return the status code
+     */
+    public HttpStatus getStatusCode() {
+        return statusCode;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ElasticsearchTrainingServiceLayerException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ElasticsearchTrainingServiceLayerException.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c43ee11088d1c41f2141650020d34b2f80954d5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ElasticsearchTrainingServiceLayerException.java
@@ -0,0 +1,41 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+/**
+ * The type Elasticsearch training service layer exception.
+ */
+public class ElasticsearchTrainingServiceLayerException extends RuntimeException {
+
+    /**
+     * Instantiates a new Elasticsearch training service layer exception.
+     */
+    public ElasticsearchTrainingServiceLayerException() {
+    }
+
+    /**
+     * Instantiates a new Elasticsearch training service layer exception.
+     *
+     * @param message the message
+     */
+    public ElasticsearchTrainingServiceLayerException(String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new Elasticsearch training service layer exception.
+     *
+     * @param message the message
+     * @param ex      the exception
+     */
+    public ElasticsearchTrainingServiceLayerException(String message, Throwable ex) {
+        super(message, ex);
+    }
+
+    /**
+     * Instantiates a new Elasticsearch training service layer exception.
+     *
+     * @param ex the exception
+     */
+    public ElasticsearchTrainingServiceLayerException(Throwable ex) {
+        super(ex);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityConflictException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityConflictException.java
new file mode 100644
index 0000000000000000000000000000000000000000..23405682904524bd6ff0977ff1dfb0bc8d465ac4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityConflictException.java
@@ -0,0 +1,38 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.CONFLICT, reason = "The request could not be completed due to a conflict with the current state of the target resource.")
+public class EntityConflictException extends ExceptionWithEntity {
+
+    public EntityConflictException() {
+        super();
+    }
+
+    public EntityConflictException(EntityErrorDetail entityErrorDetail) {
+        super(entityErrorDetail);
+    }
+
+    public EntityConflictException(EntityErrorDetail entityErrorDetail, Throwable cause) {
+        super(entityErrorDetail, cause);
+    }
+
+    public EntityConflictException(Throwable cause) {
+        super(cause);
+    }
+
+    protected String createDefaultReason(EntityErrorDetail entityErrorDetail) {
+        StringBuilder reason = new StringBuilder("Conflict with the current state of the target entity ")
+                .append(entityErrorDetail.getEntity());
+        if (entityErrorDetail.getIdentifier() != null && entityErrorDetail.getIdentifierValue() != null) {
+            reason.append(" (")
+                    .append(entityErrorDetail.getIdentifier())
+                    .append(": ")
+                    .append(entityErrorDetail.getIdentifierValue())
+                    .append(")");
+        }
+        reason.append(".");
+        return reason.toString();
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityErrorDetail.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityErrorDetail.java
new file mode 100644
index 0000000000000000000000000000000000000000..023ddba5ed482ac0e70389210f4844bad13ec136
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityErrorDetail.java
@@ -0,0 +1,110 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class EntityErrorDetail {
+    @ApiModelProperty(value = "Class of the entity.", example = "IDMGroup")
+    private String entity;
+    @ApiModelProperty(value = "Identifier of the entity.", example = "id")
+    private String identifier;
+    @ApiModelProperty(value = "Value of the identifier.", example = "1")
+    private Object identifierValue;
+    @ApiModelProperty(value = "Detailed message of the exception", example = "Group with same name already exists.")
+    private String reason;
+
+    public EntityErrorDetail() {
+    }
+
+    public EntityErrorDetail(@NotBlank String reason) {
+        this.reason = reason;
+    }
+
+    public EntityErrorDetail(@NotNull Class<?> entityClass,
+                             @NotBlank String reason) {
+        this(reason);
+        this.entity = entityClass.getSimpleName();
+    }
+
+    public EntityErrorDetail(@NotNull Class<?> entityClass,
+                             @NotBlank String identifier,
+                             @NotNull Class<?> identifierClass,
+                             @NotNull Object identifierValue,
+                             @NotBlank String reason) {
+        this(entityClass, reason);
+        this.identifier = identifier;
+        this.identifierValue = identifierClass.cast(identifierValue);
+    }
+
+    public EntityErrorDetail(@NotNull Class<?> entityClass,
+                             @NotBlank String identifier,
+                             @NotNull Class<?> identifierClass,
+                             @NotNull Object identifierValue) {
+        this.entity = entityClass.getSimpleName();
+        this.identifier = identifier;
+        this.identifierValue = identifierClass.cast(identifierValue);
+    }
+
+    public String getEntity() {
+        return entity;
+    }
+
+    public void setEntity(@NotBlank String entity) {
+        this.entity = entity;
+    }
+
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(@NotBlank String identifier) {
+        this.identifier = identifier;
+    }
+
+    public Object getIdentifierValue() {
+        return identifierValue;
+    }
+
+    public void setIdentifierValue(@NotNull Object identifierValue) {
+        this.identifierValue = identifierValue;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public void setReason(@NotBlank String reason) {
+        this.reason = reason;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        EntityErrorDetail entity = (EntityErrorDetail) o;
+        return Objects.equals(getEntity(), entity.getEntity()) &&
+                Objects.equals(getIdentifier(), entity.getIdentifier()) &&
+                Objects.equals(getIdentifierValue(), entity.getIdentifierValue()) &&
+                Objects.equals(getReason(), entity.getReason());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getEntity(), getIdentifier(), getIdentifierValue(), getReason());
+    }
+
+    @Override
+    public String toString() {
+        return "EntityErrorDetail{" +
+                "entity='" + entity + '\'' +
+                ", identifier='" + identifier + '\'' +
+                ", identifierValue=" + identifierValue +
+                ", reason='" + reason + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityNotFoundException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c30c77f11f5c9b61521caee3d42625525b76a7dc
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/EntityNotFoundException.java
@@ -0,0 +1,37 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "The requested entity could not be found")
+public class EntityNotFoundException extends ExceptionWithEntity {
+    public EntityNotFoundException() {
+        super();
+    }
+
+    public EntityNotFoundException(EntityErrorDetail entityErrorDetail) {
+        super(entityErrorDetail);
+    }
+
+    public EntityNotFoundException(EntityErrorDetail entityErrorDetail, Throwable cause) {
+        super(entityErrorDetail, cause);
+    }
+
+    public EntityNotFoundException(Throwable cause) {
+        super(cause);
+    }
+
+    protected String createDefaultReason(EntityErrorDetail entityErrorDetail) {
+        StringBuilder reason = new StringBuilder("Entity ")
+                .append(entityErrorDetail.getEntity());
+        if (entityErrorDetail.getIdentifier() != null && entityErrorDetail.getIdentifierValue() != null) {
+            reason.append(" (")
+                    .append(entityErrorDetail.getIdentifier())
+                    .append(": ")
+                    .append(entityErrorDetail.getIdentifierValue())
+                    .append(")");
+        }
+        reason.append(" not found.");
+        return reason.toString();
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ExceptionWithEntity.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ExceptionWithEntity.java
new file mode 100644
index 0000000000000000000000000000000000000000..ab0dba8e4bc2f7240aa440a82095f4838dc444ee
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ExceptionWithEntity.java
@@ -0,0 +1,41 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+public abstract class ExceptionWithEntity extends RuntimeException {
+    private EntityErrorDetail entityErrorDetail;
+
+    protected ExceptionWithEntity() {
+        super();
+    }
+
+    protected ExceptionWithEntity(EntityErrorDetail entityErrorDetail) {
+        this.entityErrorDetail = entityErrorDetail;
+        if (entityErrorDetail.getReason() == null) {
+            this.entityErrorDetail.setReason(createDefaultReason(this.entityErrorDetail));
+        }
+    }
+
+    protected ExceptionWithEntity(EntityErrorDetail entityErrorDetail, Throwable cause) {
+        super(cause);
+        this.entityErrorDetail = entityErrorDetail;
+        if (entityErrorDetail.getReason() == null) {
+            this.entityErrorDetail.setReason(createDefaultReason(this.entityErrorDetail));
+        }
+    }
+
+    protected ExceptionWithEntity(Throwable cause) {
+        super(cause);
+    }
+
+    public EntityErrorDetail getEntityErrorDetail() {
+        return entityErrorDetail;
+    }
+
+    /**
+     * Method to get default reason of error based on other attributes when no reason is provided.
+     *
+     * @param entityErrorDetail
+     * @return default detailed reason of error.
+     */
+    protected abstract String createDefaultReason(EntityErrorDetail entityErrorDetail);
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ForbiddenException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ForbiddenException.java
new file mode 100644
index 0000000000000000000000000000000000000000..44ed5c1a9a37812c539acb3105434ace59572894
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ForbiddenException.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Request is formed correctly, but the server doesn't want to carry it out.")
+public class ForbiddenException extends RuntimeException {
+
+    public ForbiddenException() {
+    }
+
+    public ForbiddenException(String message) {
+        super(message);
+    }
+
+    public ForbiddenException(String message, Throwable ex) {
+        super(message, ex);
+    }
+
+    public ForbiddenException(Throwable ex) {
+        super(ex);
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/InternalServerErrorException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/InternalServerErrorException.java
new file mode 100644
index 0000000000000000000000000000000000000000..92455272ab081212c8dadefa8e6253885d088908
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/InternalServerErrorException.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,
+        reason = "A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.")
+public class InternalServerErrorException extends RuntimeException {
+
+    public InternalServerErrorException() {
+    }
+
+    public InternalServerErrorException(String message) {
+        super(message);
+    }
+
+    public InternalServerErrorException(String message, Throwable ex) {
+        super(message, ex);
+    }
+
+    public InternalServerErrorException(Throwable e) {
+        super(e);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/MicroserviceApiException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/MicroserviceApiException.java
new file mode 100644
index 0000000000000000000000000000000000000000..75b11b18339129097707abb34f4ecdde2ee0877b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/MicroserviceApiException.java
@@ -0,0 +1,46 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiSubError;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import javax.validation.ConstraintViolationException;
+
+@ResponseStatus(reason = "Error when calling external service API")
+public class MicroserviceApiException extends RuntimeException {
+    private ApiSubError apiSubError;
+
+    public MicroserviceApiException() {
+        super();
+    }
+
+    public MicroserviceApiException(ApiSubError apiSubError) {
+        super();
+        this.apiSubError = apiSubError;
+
+    }
+
+    public MicroserviceApiException(String message) {
+        super(message);
+    }
+
+    public MicroserviceApiException(String message, ConstraintViolationException exception) {
+        super(message + " Constraint violations: " + exception.getConstraintViolations().toString());
+
+    }
+
+    public MicroserviceApiException(String message, ApiSubError apiSubError) {
+        super(message);
+        this.apiSubError = apiSubError;
+    }
+
+
+    public MicroserviceApiException(ApiSubError apiSubError, Throwable cause) {
+        super(cause);
+        this.apiSubError = apiSubError;
+
+    }
+
+    public ApiSubError getApiSubError() {
+        return apiSubError;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotFoundException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..b65c9ad60182be09148e7fbdbe4e6dfbd2b86efd
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "The requested resource was not found")
+public class ResourceNotFoundException extends RuntimeException {
+
+    public ResourceNotFoundException() {
+    }
+
+    public ResourceNotFoundException(String message) {
+        super(message);
+    }
+
+    public ResourceNotFoundException(String message, Throwable ex) {
+        super(message, ex);
+    }
+
+    public ResourceNotFoundException(Throwable ex) {
+        super(ex);
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotModifiedException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotModifiedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3fc5429d22e1ced36e351c31a89204fd80d2b24
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/ResourceNotModifiedException.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.NOT_MODIFIED, reason = "The requested resource was not modified")
+public class ResourceNotModifiedException extends RuntimeException {
+
+    public ResourceNotModifiedException() {
+    }
+
+    public ResourceNotModifiedException(String message) {
+        super(message);
+    }
+
+    public ResourceNotModifiedException(String message, Throwable ex) {
+        super(message, ex);
+    }
+
+    public ResourceNotModifiedException(Throwable ex) {
+        super(ex);
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/TooManyRequestsException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/TooManyRequestsException.java
new file mode 100644
index 0000000000000000000000000000000000000000..cce0720310bd63c08bfa1558c2eaa0954a29af0f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/TooManyRequestsException.java
@@ -0,0 +1,38 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.TOO_MANY_REQUESTS, reason = "The user has sent too many requests in a given amount of time (\"rate limiting\").")
+public class TooManyRequestsException extends ExceptionWithEntity {
+
+    public TooManyRequestsException() {
+        super();
+    }
+
+    public TooManyRequestsException(EntityErrorDetail entityErrorDetail) {
+        super(entityErrorDetail);
+    }
+
+    public TooManyRequestsException(EntityErrorDetail entityErrorDetail, Throwable cause) {
+        super(entityErrorDetail, cause);
+    }
+
+    public TooManyRequestsException(Throwable cause) {
+        super(cause);
+    }
+
+    protected String createDefaultReason(EntityErrorDetail entityErrorDetail) {
+        StringBuilder reason = new StringBuilder("User has sent too many requests to obtain entity ")
+                .append(entityErrorDetail.getEntity());
+        if (entityErrorDetail.getIdentifier() != null && entityErrorDetail.getIdentifierValue() != null) {
+            reason.append(" (")
+                    .append(entityErrorDetail.getIdentifier())
+                    .append(": ")
+                    .append(entityErrorDetail.getIdentifierValue())
+                    .append(")");
+        }
+        reason.append(".");
+        return reason.toString();
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/UnprocessableEntityException.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/UnprocessableEntityException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a68947d846fe81f36adbf09d8b8a9d3c564d9626
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/UnprocessableEntityException.java
@@ -0,0 +1,39 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(value = HttpStatus.UNPROCESSABLE_ENTITY, reason = "The requested data cannot be processed.")
+public class UnprocessableEntityException extends ExceptionWithEntity {
+
+    public UnprocessableEntityException() {
+        super();
+    }
+
+    public UnprocessableEntityException(EntityErrorDetail entityErrorDetail) {
+        super(entityErrorDetail);
+    }
+
+    public UnprocessableEntityException(EntityErrorDetail entityErrorDetail, Throwable cause) {
+        super(entityErrorDetail, cause);
+    }
+
+    public UnprocessableEntityException(Throwable cause) {
+        super(cause);
+    }
+
+    protected String createDefaultReason(EntityErrorDetail entityErrorDetail) {
+        StringBuilder reason = new StringBuilder("Unable to be process entity ")
+                .append(entityErrorDetail.getEntity());
+        if (entityErrorDetail.getIdentifier() != null && entityErrorDetail.getIdentifierValue() != null) {
+            reason.append(" (")
+                    .append(entityErrorDetail.getIdentifier())
+                    .append(": ")
+                    .append(entityErrorDetail.getIdentifierValue())
+                    .append(")");
+        }
+        reason.append(" not found.");
+        return reason.toString();
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiEntityError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiEntityError.java
new file mode 100644
index 0000000000000000000000000000000000000000..340520ad9681243f50836252b22b36185d46e30c
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiEntityError.java
@@ -0,0 +1,112 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.springframework.http.HttpStatus;
+
+import java.util.List;
+import java.util.Objects;
+
+@ApiModel(value = "ApiEntityError", description = "A detailed error information related to the entity.", parent = ApiError.class)
+public class ApiEntityError extends ApiError {
+    @ApiModelProperty(value = "Detail of the entity which is related to the error.")
+    private EntityErrorDetail entityErrorDetail;
+
+    private ApiEntityError() {
+        super();
+    }
+
+    private ApiEntityError(HttpStatus httpStatus, String message, String path, EntityErrorDetail entityErrorDetail) {
+        super();
+        this.setStatus(httpStatus);
+        this.setMessage(getMessage(entityErrorDetail, message));
+        this.setPath(path);
+        this.setTimestamp(System.currentTimeMillis());
+        this.setEntityErrorDetail(entityErrorDetail);
+    }
+
+    public static ApiEntityError of(HttpStatus httpStatus, String message, List<String> errors, String path, EntityErrorDetail entityErrorDetail) {
+        ApiEntityError apiEntityError = new ApiEntityError(httpStatus, message, path, entityErrorDetail);
+        apiEntityError.setErrors(errors);
+        return apiEntityError;
+    }
+
+    public static ApiEntityError of(HttpStatus httpStatus, String message, String error, String path, EntityErrorDetail entityErrorDetail) {
+        ApiEntityError apiEntityError = new ApiEntityError(httpStatus, message, path, entityErrorDetail);
+        apiEntityError.setError(error);
+        return apiEntityError;
+    }
+
+    public static ApiEntityError of(HttpStatus httpStatus, String message, List<String> errors, EntityErrorDetail entityErrorDetail) {
+        return ApiEntityError.of(httpStatus, message, errors, "", entityErrorDetail);
+    }
+
+    public static ApiEntityError of(HttpStatus httpStatus, String message, String error, EntityErrorDetail entityErrorDetail) {
+        return ApiEntityError.of(httpStatus, message, error, "", entityErrorDetail);
+    }
+
+    private static String generateMessage(EntityErrorDetail entityErrorDetail, String defaultMessage) {
+        if (entityErrorDetail != null && entityErrorDetail.getEntity() != null && entityErrorDetail.getIdentifier() != null) {
+            return "Resource " + entityErrorDetail.getEntity() + " ("
+                    + entityErrorDetail.getIdentifier() + ": "
+                    + entityErrorDetail.getIdentifierValue() + ") not found.";
+        } else if (entityErrorDetail != null && entityErrorDetail.getReason() != null && !entityErrorDetail.getReason().isBlank()) {
+            return entityErrorDetail.getReason();
+        } else {
+            return defaultMessage;
+        }
+    }
+
+    private static String getMessage(EntityErrorDetail entityErrorDetail, String defaultMessage) {
+        if (entityErrorDetail == null) {
+            return defaultMessage;
+        }
+        return entityErrorDetail.getReason() == null ? defaultMessage : entityErrorDetail.getReason();
+    }
+
+
+    /**
+     * Gets entity error detail.
+     *
+     * @return the entity error detail
+     */
+    public EntityErrorDetail getEntityErrorDetail() {
+        return entityErrorDetail;
+    }
+
+    /**
+     * Sets entity error detail.
+     *
+     * @param entityErrorDetail the entity error detail
+     */
+    public void setEntityErrorDetail(EntityErrorDetail entityErrorDetail) {
+        this.entityErrorDetail = entityErrorDetail;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ApiEntityError)) return false;
+        if (!super.equals(o)) return false;
+        ApiEntityError that = (ApiEntityError) o;
+        return Objects.equals(getEntityErrorDetail(), that.getEntityErrorDetail());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), getEntityErrorDetail());
+    }
+
+    @Override
+    public String toString() {
+        return "ApiEntityError{" +
+                "entityErrorDetail=" + entityErrorDetail +
+                ", timestamp=" + getTimestamp() +
+                ", status=" + getStatus() +
+                ", message='" + getMessage() + '\'' +
+                ", errors=" + getErrors() +
+                ", path='" + getPath() + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..57fc0a25a1847421c655322bd9eb102462d15a11
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiError.java
@@ -0,0 +1,231 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.springframework.http.HttpStatus;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The type Api error.
+ */
+@ApiModel(value = "ApiError", subTypes = {ApiEntityError.class, ApiMicroserviceError.class},
+        description = "Superclass for classes ApiEntityError and ApiMicroserviceError")
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = ApiEntityError.class, name = "ApiEntityError"),
+        @JsonSubTypes.Type(value = ApiMicroserviceError.class, name = "ApiMicroserviceError")})
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ApiError {
+
+    @ApiModelProperty(value = "The time when the exception occurred", example = "1574062900 (different for each type of exception)")
+    private long timestamp;
+    @ApiModelProperty(value = "The HTTP response status code", example = "404 Not found (different for each type of exception).")
+    private HttpStatus status;
+    @ApiModelProperty(value = "The specific description of the ApiError.", example = "The IDMGroup could not be found in database (different for each type of exception).")
+    private String message;
+    @ApiModelProperty(value = "The list of main reasons of the ApiError.", example = "[The requested resource was not found (different for each type of exception).]")
+    private List<String> errors;
+    @ApiModelProperty(value = "The requested URI path which caused error.", example = "/kypo2-rest-user-and-group/api/v1/groups/1000 (different for each type of exception).")
+    private String path;
+
+    protected ApiError() {
+    }
+
+    private ApiError(HttpStatus httpStatus, String message, String path) {
+        this.status = httpStatus;
+        this.message = message;
+        this.path = path;
+        this.timestamp = System.currentTimeMillis();
+
+    }
+
+    /**
+     * Of api error.
+     *
+     * @param httpStatus the http status
+     * @param message    the message
+     * @param errors     the errors
+     * @param path       the path
+     * @return the api error
+     */
+    public static ApiError of(HttpStatus httpStatus, String message, List<String> errors, String path) {
+        ApiError apiError = new ApiError(httpStatus, message, path);
+        apiError.setErrors(errors);
+        return apiError;
+    }
+
+    /**
+     * Of api error.
+     *
+     * @param httpStatus the http status
+     * @param message    the message
+     * @param error      the error
+     * @param path       the path
+     * @return the api error
+     */
+    public static ApiError of(HttpStatus httpStatus, String message, String error, String path) {
+        ApiError apiError = new ApiError(httpStatus, message, path);
+        apiError.setError(error);
+        return apiError;
+    }
+
+    /**
+     * Of api error.
+     *
+     * @param httpStatus the http status
+     * @param message    the message
+     * @param errors     the errors
+     * @return the api error
+     */
+    public static ApiError of(HttpStatus httpStatus, String message, List<String> errors) {
+        return ApiError.of(httpStatus, message, errors, "");
+    }
+
+    /**
+     * Of api error.
+     *
+     * @param httpStatus the http status
+     * @param message    the message
+     * @param error      the error
+     * @return the api error
+     */
+    public static ApiError of(HttpStatus httpStatus, String message, String error) {
+        return ApiError.of(httpStatus, message, error, "");
+    }
+
+    /**
+     * Gets timestamp.
+     *
+     * @return the timestamp
+     */
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    /**
+     * Sets timestamp.
+     *
+     * @param timestamp the timestamp
+     */
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    /**
+     * Gets status.
+     *
+     * @return the status
+     */
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    /**
+     * Sets status.
+     *
+     * @param status the status
+     */
+    public void setStatus(final HttpStatus status) {
+        this.status = status;
+    }
+
+    /**
+     * Gets message.
+     *
+     * @return the message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Sets message.
+     *
+     * @param message the message
+     */
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+
+    /**
+     * Gets errors.
+     *
+     * @return the errors
+     */
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    /**
+     * Sets errors.
+     *
+     * @param errors the errors
+     */
+    public void setErrors(final List<String> errors) {
+        this.errors = errors;
+    }
+
+    /**
+     * Sets error.
+     *
+     * @param error the error
+     */
+    public void setError(final String error) {
+        errors = Arrays.asList(error);
+    }
+
+    /**
+     * Gets path.
+     *
+     * @return the path
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Sets path.
+     *
+     * @param path the path
+     */
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + status +
+                ", message='" + message + '\'' +
+                ", errors=" + errors +
+                ", path='" + path + '\'' +
+                '}';
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(timestamp, status, message, errors, path);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof ApiError))
+            return false;
+        ApiError other = (ApiError) obj;
+        return Objects.equals(errors, other.getErrors()) &&
+                Objects.equals(message, other.getMessage()) &&
+                Objects.equals(path, other.getPath()) &&
+                Objects.equals(status, other.getStatus()) &&
+                Objects.equals(timestamp, other.getTimestamp());
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiMicroserviceError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiMicroserviceError.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f5f34bdfa79c61a92d03f20b0ae844291043bd9
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiMicroserviceError.java
@@ -0,0 +1,95 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.util.List;
+import java.util.Objects;
+
+@ApiModel(value = "ApiMicroserviceError", description = "A detailed error information related to the microservice.", parent = ApiError.class)
+public class ApiMicroserviceError extends ApiError {
+
+    @ApiModelProperty(value = "Detailed error from another microservice.")
+    private ApiSubError apiSubError;
+
+    private ApiMicroserviceError() {
+        super();
+    }
+
+    private ApiMicroserviceError(HttpStatus httpStatus, String message, String path, ApiSubError apiSubError) {
+        super();
+        this.setStatus(apiSubError == null ? httpStatus : apiSubError.getStatus());
+        this.setMessage(message == null ? MicroserviceApiException.class.getAnnotation(ResponseStatus.class).reason() : message);
+        this.setMessage(message);
+        this.setPath(path);
+        this.setApiSubError(apiSubError);
+        this.setTimestamp(System.currentTimeMillis());
+    }
+
+    public static ApiError of(HttpStatus httpStatus, String message, List<String> errors, String path, ApiSubError apiSubError) {
+        ApiMicroserviceError apiMicroserviceError = new ApiMicroserviceError(httpStatus, message, path, apiSubError);
+        apiMicroserviceError.setErrors(errors);
+        return apiMicroserviceError;
+    }
+
+    public static ApiError of(HttpStatus httpStatus, String message, String error, String path, ApiSubError apiSubError) {
+        ApiMicroserviceError apiMicroserviceError = new ApiMicroserviceError(httpStatus, message, path, apiSubError);
+        apiMicroserviceError.setError(error);
+        return apiMicroserviceError;
+    }
+
+    public static ApiError of(HttpStatus httpStatus, String message, List<String> errors, ApiSubError apiSubError) {
+        return ApiMicroserviceError.of(httpStatus, message, errors, "", apiSubError);
+    }
+
+    public static ApiError of(HttpStatus httpStatus, String message, String error, ApiSubError apiSubError) {
+        return ApiMicroserviceError.of(httpStatus, message, error, "", apiSubError);
+    }
+
+    /**
+     * Gets api sub error.
+     *
+     * @return the api sub error
+     */
+    public ApiSubError getApiSubError() {
+        return apiSubError;
+    }
+
+    /**
+     * Sets api sub error.
+     *
+     * @param apiSubError the api sub error
+     */
+    public void setApiSubError(ApiSubError apiSubError) {
+        this.apiSubError = apiSubError;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ApiMicroserviceError)) return false;
+        if (!super.equals(o)) return false;
+        ApiMicroserviceError that = (ApiMicroserviceError) o;
+        return Objects.equals(getApiSubError(), that.getApiSubError());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), getApiSubError());
+    }
+
+    @Override
+    public String toString() {
+        return "ApiMicroserviceError{" +
+                "apiSubError=" + apiSubError +
+                ", timestamp=" + getTimestamp() +
+                ", status=" + getStatus() +
+                ", message='" + getMessage() + '\'' +
+                ", errors=" + getErrors() +
+                ", path='" + getPath() + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiSubError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiSubError.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d3b5321ac530354586542845817ebc348eab4ee
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/ApiSubError.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.springframework.http.HttpStatus;
+
+@ApiModel(value = "ApiSubError", subTypes = {JavaApiError.class, PythonApiError.class},
+        description = "Superclass for classes JavaApiError and PythonApiError")
+@JsonSubTypes({
+        @JsonSubTypes.Type(value = JavaApiError.class, name = "JavaApiError"),
+        @JsonSubTypes.Type(value = PythonApiError.class, name = "PythonApiError")})
+public abstract class ApiSubError {
+    @ApiModelProperty(value = "The HTTP response status code", example = "404 Not found (different for each type of exception).")
+    private HttpStatus status;
+
+    public HttpStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(HttpStatus status) {
+        this.status = status;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/JavaApiError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/JavaApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..22e42035b7a193d1901908c2f3b0256ad2919f79
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/JavaApiError.java
@@ -0,0 +1,147 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.springframework.http.HttpStatus;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@ApiModel(value = "JavaApiError", description = "A detailed error from another Java mircorservice.", parent = ApiSubError.class)
+public class JavaApiError extends ApiSubError {
+    @ApiModelProperty(value = "The time when the exception occurred", example = "1574062900 (different for each type of exception)")
+    private long timestamp;
+    @ApiModelProperty(value = "The specific description of the ApiError.", example = "The IDMGroup could not be found in database (different for each type of exception).")
+    private String message;
+    @ApiModelProperty(value = "The list of main reasons of the ApiError.", example = "[The requested resource was not found (different for each type of exception).]")
+    private List<String> errors;
+    @ApiModelProperty(value = "The requested URI path which caused error.", example = "/kypo2-rest-user-and-group/api/v1/groups/1000 (different for each type of exception).")
+    private String path;
+    @ApiModelProperty(value = "Entity detail related to the error.")
+    @JsonProperty("entity_error_detail")
+    private EntityErrorDetail entityErrorDetail;
+
+    private JavaApiError() {
+    }
+
+    public static JavaApiError of(HttpStatus httpStatus, String message, List<String> errors, String path) {
+        JavaApiError apiError = new JavaApiError();
+        apiError.setTimestamp(System.currentTimeMillis());
+        apiError.setStatus(httpStatus);
+        apiError.setMessage(message);
+        apiError.setErrors(errors);
+        apiError.setPath(path);
+        return apiError;
+    }
+
+    public static JavaApiError of(HttpStatus httpStatus, String message, String error, String path) {
+        JavaApiError apiError = new JavaApiError();
+        apiError.setTimestamp(System.currentTimeMillis());
+        apiError.setStatus(httpStatus);
+        apiError.setMessage(message);
+        apiError.setError(error);
+        apiError.setPath(path);
+        return apiError;
+    }
+
+    public static JavaApiError of(HttpStatus httpStatus, String message, List<String> errors) {
+        JavaApiError apiError = new JavaApiError();
+        apiError.setTimestamp(System.currentTimeMillis());
+        apiError.setStatus(httpStatus);
+        apiError.setMessage(message);
+        apiError.setErrors(errors);
+        apiError.setPath("");
+        return apiError;
+    }
+
+    public static JavaApiError of(HttpStatus httpStatus, String message, String error) {
+        JavaApiError apiError = new JavaApiError();
+        apiError.setTimestamp(System.currentTimeMillis());
+        apiError.setStatus(httpStatus);
+        apiError.setMessage(message);
+        apiError.setError(error);
+        apiError.setPath("");
+        return apiError;
+    }
+
+    public EntityErrorDetail getEntityErrorDetail() {
+        return entityErrorDetail;
+    }
+
+    public void setEntityErrorDetail(EntityErrorDetail entityErrorDetail) {
+        this.entityErrorDetail = entityErrorDetail;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+
+    public List<String> getErrors() {
+        return errors;
+    }
+
+    public void setErrors(final List<String> errors) {
+        this.errors = errors;
+    }
+
+    public void setError(final String error) {
+        errors = Arrays.asList(error);
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public String toString() {
+        return "ApiError{" +
+                "timestamp=" + timestamp +
+                ", status=" + getStatus() +
+                ", message='" + message + '\'' +
+                ", errors=" + errors +
+                ", path='" + path + '\'' +
+                ", entityErrorDetail=" + entityErrorDetail +
+                '}';
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(timestamp, getStatus(), message, errors, path);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (!(obj instanceof JavaApiError))
+            return false;
+        JavaApiError other = (JavaApiError) obj;
+        return Objects.equals(errors, other.getErrors()) &&
+                Objects.equals(message, other.getMessage()) &&
+                Objects.equals(path, other.getPath()) &&
+                Objects.equals(getStatus(), other.getStatus()) &&
+                Objects.equals(timestamp, other.getTimestamp());
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/PythonApiError.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/PythonApiError.java
new file mode 100644
index 0000000000000000000000000000000000000000..6cf32aa935ce5e0cc62345048a20218316eb4012
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/errors/PythonApiError.java
@@ -0,0 +1,62 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.errors;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Map;
+import java.util.Objects;
+
+@ApiModel(value = "PythonApiError", description = "A detailed error from another Python mircorservice.", parent = ApiSubError.class)
+public class PythonApiError extends ApiSubError {
+
+    @ApiModelProperty(value = "Detail message of the error.", example = "Sandbox could not be found.")
+    private String detail;
+    @ApiModelProperty(value = "Parameters to specify details of the error.", example = "name: sandbox")
+    private Map<String, String> parameters;
+
+    public PythonApiError() {
+    }
+
+    public PythonApiError(String detail) {
+        this.detail = detail;
+    }
+
+    public String getDetail() {
+        return detail;
+    }
+
+    public void setDetail(String detail) {
+        this.detail = detail;
+    }
+
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(Map<String, String> parameters) {
+        this.parameters = parameters;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PythonApiError)) return false;
+        PythonApiError that = (PythonApiError) o;
+        return Objects.equals(getDetail(), that.getDetail()) &&
+                Objects.equals(getParameters(), that.getParameters());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getDetail(), getParameters());
+    }
+
+    @Override
+    public String toString() {
+        return "PythonApiError{" +
+                "detail='" + detail + '\'' +
+                ", status=" + getStatus() +
+                ", parameters=" + parameters +
+                '}';
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/JavaApiResponseErrorHandler.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/JavaApiResponseErrorHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..c435ac4283afe9eef4e831b8b6e141804a15d88a
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/JavaApiResponseErrorHandler.java
@@ -0,0 +1,45 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.responsehandlers;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.JavaApiError;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.client.ResponseErrorHandler;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import static org.springframework.http.HttpStatus.Series.CLIENT_ERROR;
+import static org.springframework.http.HttpStatus.Series.SERVER_ERROR;
+
+public class JavaApiResponseErrorHandler implements ResponseErrorHandler {
+
+    private ObjectMapper mapper;
+
+    /**
+     * Instantiates a new Python api response error handler.
+     *
+     * @param mapper the mapper
+     */
+    public JavaApiResponseErrorHandler(ObjectMapper mapper) {
+        this.mapper = mapper;
+    }
+
+    @Override
+    public boolean hasError(ClientHttpResponse response) throws IOException {
+        return (
+                response.getStatusCode().series() == CLIENT_ERROR
+                        || response.getStatusCode().series() == SERVER_ERROR);
+    }
+
+    @Override
+    public void handleError(ClientHttpResponse response) throws IOException {
+        String responseBody = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
+        if (responseBody.isBlank()) {
+            throw new CustomWebClientException("Error from external microservice. No specific message provided.", response.getStatusCode());
+        }
+        JavaApiError javaApiError = mapper.readValue(responseBody, JavaApiError.class);
+        throw new CustomWebClientException(javaApiError);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/PythonApiResponseErrorHandler.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/PythonApiResponseErrorHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..c480e26caea400b9674eeabeb4edfcd6f58a6ca3
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/exceptions/responsehandlers/PythonApiResponseErrorHandler.java
@@ -0,0 +1,49 @@
+package cz.muni.ics.kypo.training.adaptive.exceptions.responsehandlers;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.PythonApiError;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.client.ResponseErrorHandler;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import static org.springframework.http.HttpStatus.Series.CLIENT_ERROR;
+import static org.springframework.http.HttpStatus.Series.SERVER_ERROR;
+
+/**
+ * Handler used for errors returned from Python api.
+ */
+public class PythonApiResponseErrorHandler implements ResponseErrorHandler {
+
+    private ObjectMapper mapper;
+
+    /**
+     * Instantiates a new Python api response error handler.
+     *
+     * @param mapper the mapper
+     */
+    public PythonApiResponseErrorHandler(ObjectMapper mapper) {
+        this.mapper = mapper;
+    }
+
+    @Override
+    public boolean hasError(ClientHttpResponse response) throws IOException {
+        return (
+                response.getStatusCode().series() == CLIENT_ERROR
+                        || response.getStatusCode().series() == SERVER_ERROR);
+    }
+
+    @Override
+    public void handleError(ClientHttpResponse response) throws IOException {
+        String responseBody = StreamUtils.copyToString(response.getBody(), Charset.defaultCharset());
+        if (responseBody.isBlank()) {
+            throw new CustomWebClientException("Error from external microservice. No specific message provided.", response.getStatusCode());
+        }
+        PythonApiError pythonApiError = mapper.readValue(response.getBody(), PythonApiError.class);
+        pythonApiError.setStatus(response.getStatusCode());
+        throw new CustomWebClientException(pythonApiError);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/ExportImportFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/ExportImportFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..df6210bdb8793f2f73f782e7efe034d51d156303
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/ExportImportFacade.java
@@ -0,0 +1,328 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.muni.csirt.kypo.events.adaptive.trainings.PhaseStarted;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsDesignerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsDesignerOrOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingDefinitionArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingInstanceArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingRunArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.FileToReturnDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.training.TrainingDefinitionWithPhasesExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.ImportTrainingDefinitionDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.AbstractPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.info.InfoPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionnairePhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.TrainingPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.SandboxDefinitionInfo;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.TrainingDefinitionByIdDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.InternalServerErrorException;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.ExportImportMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.PhaseMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.QuestionPhaseRelationMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TrainingDefinitionMapper;
+import cz.muni.ics.kypo.training.adaptive.service.ExportImportService;
+import cz.muni.ics.kypo.training.adaptive.service.api.ElasticsearchServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.api.SandboxServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService;
+import cz.muni.ics.kypo.training.adaptive.utils.AbstractFileExtensions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * The type Export import facade.
+ */
+@Service
+@Transactional
+public class ExportImportFacade {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ExportImportFacade.class);
+    private static final String LOGS_FOLDER = "logs";
+    private static final String EVENTS_FOLDER = "training_events";
+    private static final String RUNS_FOLDER = "training_runs";
+
+    private ExportImportService exportImportService;
+    private TrainingDefinitionService trainingDefinitionService;
+    private SandboxServiceApi sandboxServiceApi;
+    private ElasticsearchServiceApi elasticsearchServiceApi;
+    private ExportImportMapper exportImportMapper;
+    private PhaseMapper phaseMapper;
+    private QuestionPhaseRelationMapper questionPhaseRelationMapper;
+    private TrainingDefinitionMapper trainingDefinitionMapper;
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Export import facade.
+     *
+     * @param exportImportService       the export import service
+     * @param exportImportMapper        the export import mapper
+     * @param phaseMapper               the phase mapper
+     * @param trainingDefinitionService the training definition service
+     * @param trainingDefinitionMapper  the training definition mapper
+     * @param objectMapper              the object mapper
+     */
+    @Autowired
+    public ExportImportFacade(ExportImportService exportImportService,
+                              TrainingDefinitionService trainingDefinitionService,
+                              SandboxServiceApi sandboxServiceApi,
+                              ElasticsearchServiceApi elasticsearchServiceApi,
+                              ExportImportMapper exportImportMapper,
+                              PhaseMapper phaseMapper,
+                              QuestionPhaseRelationMapper questionPhaseRelationMapper,
+                              TrainingDefinitionMapper trainingDefinitionMapper,
+                              ObjectMapper objectMapper) {
+        this.exportImportService = exportImportService;
+        this.trainingDefinitionService = trainingDefinitionService;
+        this.sandboxServiceApi = sandboxServiceApi;
+        this.elasticsearchServiceApi = elasticsearchServiceApi;
+        this.exportImportMapper = exportImportMapper;
+        this.phaseMapper = phaseMapper;
+        this.questionPhaseRelationMapper = questionPhaseRelationMapper;
+        this.trainingDefinitionMapper = trainingDefinitionMapper;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Exports Training Definition to file
+     *
+     * @param trainingDefinitionId the id of the definition to be exported
+     * @return the file containing definition, {@link FileToReturnDTO}
+     */
+    @IsDesignerOrOrganizerOrAdmin
+    @TransactionalRO
+    public FileToReturnDTO dbExport(Long trainingDefinitionId) {
+        TrainingDefinition td = exportImportService.findById(trainingDefinitionId);
+        TrainingDefinitionWithPhasesExportDTO dbExport = exportImportMapper.mapToDTO(td);
+        if (dbExport != null) {
+            dbExport.setPhases(mapAbstractPhaseToAbstractPhaseDTO(trainingDefinitionId));
+        }
+        try {
+            FileToReturnDTO fileToReturnDTO = new FileToReturnDTO();
+            fileToReturnDTO.setContent(objectMapper.writeValueAsBytes(dbExport));
+            if (dbExport != null && dbExport.getTitle() != null) {
+                fileToReturnDTO.setTitle(dbExport.getTitle());
+            } else {
+                fileToReturnDTO.setTitle("");
+            }
+            return fileToReturnDTO;
+        } catch (IOException ex) {
+            throw new InternalServerErrorException(ex);
+        }
+    }
+
+    private List<AbstractPhaseExportDTO> mapAbstractPhaseToAbstractPhaseDTO(Long trainingDefinitionId) {
+        List<AbstractPhaseExportDTO> abstractPhaseExportDTOs = new ArrayList<>();
+        List<AbstractPhase> abstractPhases = trainingDefinitionService.findAllPhasesFromDefinition(trainingDefinitionId);
+        abstractPhases.forEach(phase ->
+                abstractPhaseExportDTOs.add(phaseMapper.mapToExportDTO(phase)));
+        return abstractPhaseExportDTOs;
+    }
+
+    private List<AbstractPhaseArchiveDTO> mapAbstractPhasesToArchiveDTO(Long trainingDefinitionId) {
+        List<AbstractPhaseArchiveDTO> abstractPhaseArchiveDTOs = new ArrayList<>();
+        List<AbstractPhase> abstractPhases = trainingDefinitionService.findAllPhasesFromDefinition(trainingDefinitionId);
+        abstractPhases.forEach(phase ->
+                abstractPhaseArchiveDTOs.add(phaseMapper.mapToArchiveDTO(phase)));
+        return abstractPhaseArchiveDTOs;
+    }
+
+    /**
+     * Imports training definition.
+     *
+     * @param importTrainingDefinitionDTO the training definition to be imported
+     * @return the {@link TrainingDefinitionByIdDTO}
+     */
+    @IsDesignerOrAdmin
+    @TransactionalWO
+    public TrainingDefinitionByIdDTO dbImport(ImportTrainingDefinitionDTO importTrainingDefinitionDTO) {
+        importTrainingDefinitionDTO.setState(TDState.UNRELEASED);
+        if (importTrainingDefinitionDTO.getTitle() != null && !importTrainingDefinitionDTO.getTitle().startsWith("Uploaded")) {
+            importTrainingDefinitionDTO.setTitle("Uploaded " + importTrainingDefinitionDTO.getTitle());
+        }
+
+        TrainingDefinition newDefinition = exportImportMapper.mapToEntity(importTrainingDefinitionDTO);
+        TrainingDefinition newTrainingDefinition = trainingDefinitionService.create(newDefinition);
+        List<AbstractPhaseImportDTO> phases = importTrainingDefinitionDTO.getPhases();
+        phases.forEach(phase -> {
+            if (phase.getPhaseType().equals(PhaseType.TRAINING)) {
+                exportImportService.createTrainingPhase(phaseMapper.mapToEntity((TrainingPhaseImportDTO) phase), newTrainingDefinition);
+            } else if (phase.getPhaseType().equals(PhaseType.INFO)) {
+                exportImportService.createInfoPhase(phaseMapper.mapToEntity((InfoPhaseImportDTO) phase), newTrainingDefinition);
+            }
+        });
+        phases.forEach(phase -> {
+            if (phase.getPhaseType().equals(PhaseType.QUESTIONNAIRE)) {
+                QuestionnairePhase createdQuestionnairePhase = exportImportService.createQuestionnairePhase(phaseMapper.mapToEntity((QuestionnairePhaseImportDTO) phase), newTrainingDefinition);
+                ((QuestionnairePhaseImportDTO) phase).getPhaseRelations().forEach(questionPhaseRelation ->
+                        exportImportService.createQuestionPhaseRelationPhase(
+                                questionPhaseRelationMapper.mapToEntity(questionPhaseRelation),
+                                createdQuestionnairePhase,
+                                questionPhaseRelation.getPhaseId(),
+                                questionPhaseRelation.getQuestionIds()));
+            }
+        });
+        return trainingDefinitionMapper.mapToDTOById(newTrainingDefinition);
+    }
+
+    /**
+     * Exports Training Instance to file
+     *
+     * @param trainingInstanceId the id of the instance to be exported
+     * @return the file containing instance, {@link FileToReturnDTO}
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalRO
+    public FileToReturnDTO archiveTrainingInstance(Long trainingInstanceId) {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+             ZipOutputStream zos = new ZipOutputStream(baos)) {
+            TrainingInstance trainingInstance = exportImportService.findInstanceById(trainingInstanceId);
+
+            TrainingInstanceArchiveDTO archivedInstance = exportImportMapper.mapToDTO(trainingInstance);
+            archivedInstance.setDefinitionId(trainingInstance.getTrainingDefinition().getId());
+            Set<Long> organizersRefIds = trainingInstance.getOrganizers().stream()
+                    .map(UserRef::getUserRefId)
+                    .collect(Collectors.toSet());
+            archivedInstance.setOrganizersRefIds(new HashSet<>(organizersRefIds));
+
+            writeTrainingInstanceGeneralInfo(zos, trainingInstance.getId(), archivedInstance);
+            writeTrainingDefinitionInfo(zos, trainingInstance);
+            writeTrainingRunsInfo(zos, trainingInstance);
+            writeSandboxDefinitionInfo(zos, trainingInstance);
+
+            zos.closeEntry();
+            zos.close();
+            FileToReturnDTO fileToReturnDTO = new FileToReturnDTO();
+            fileToReturnDTO.setContent(baos.toByteArray());
+            fileToReturnDTO.setTitle(trainingInstance.getTitle());
+            return fileToReturnDTO;
+        } catch (IOException ex) {
+            throw new InternalServerErrorException("The .zip file was not created since there were some processing error.", ex);
+        }
+    }
+
+    private void writeTrainingInstanceGeneralInfo(ZipOutputStream zos, Long trainingInstanceId, TrainingInstanceArchiveDTO archivedInstance) throws IOException {
+        ZipEntry instanceEntry = new ZipEntry("training_instance-id" + trainingInstanceId + AbstractFileExtensions.JSON_FILE_EXTENSION);
+        zos.putNextEntry(instanceEntry);
+        zos.write(objectMapper.writeValueAsBytes(archivedInstance));
+    }
+
+    private void writeTrainingRunsInfo(ZipOutputStream zos, TrainingInstance trainingInstance) throws IOException {
+        Set<TrainingRun> runs = exportImportService.findRunsByInstanceId(trainingInstance.getId());
+        for (TrainingRun run : runs) {
+            TrainingRunArchiveDTO archivedRun = exportImportMapper.mapToArchiveDTO(run);
+            archivedRun.setInstanceId(trainingInstance.getId());
+            archivedRun.setParticipantRefId(run.getParticipantRef().getUserRefId());
+            ZipEntry runEntry = new ZipEntry(RUNS_FOLDER + "/training_run-id" + run.getId() + AbstractFileExtensions.JSON_FILE_EXTENSION);
+            zos.putNextEntry(runEntry);
+            zos.write(objectMapper.writeValueAsBytes(archivedRun));
+
+            List<Map<String, Object>> events = elasticsearchServiceApi.findAllEventsFromTrainingRun(run);
+            Map<Integer, Long> phaseStartTimestampMapping = writeEventsAndGetPhaseStartTimestampMapping(zos, run, events);
+            writeEventsByPhases(zos, run, events);
+
+            List<Map<String, Object>> consoleCommands = elasticsearchServiceApi.findAllConsoleCommandsFromSandbox(run.getSandboxInstanceRefId());
+            Integer sandboxId = (Integer) events.get(0).get("sandbox_id");
+            writeConsoleCommands(zos, sandboxId, consoleCommands);
+            writeConsoleCommandsDetails(zos, sandboxId, phaseStartTimestampMapping);
+        }
+    }
+
+    private Map<Integer, Long> writeEventsAndGetPhaseStartTimestampMapping(ZipOutputStream zos, TrainingRun run, List<Map<String, Object>> events) throws IOException {
+        ZipEntry eventsEntry = new ZipEntry(EVENTS_FOLDER + "/training_run-id" + run.getId() + "-events" + AbstractFileExtensions.JSON_FILE_EXTENSION);
+        zos.putNextEntry(eventsEntry);
+        //Obtain start timestamp of each phase, so it can be used later
+        Map<Integer, Long> phaseStartTimestampMapping = new LinkedHashMap<>();
+
+        for (Map<String, Object> event : events) {
+            zos.write(objectMapper.writer(new MinimalPrettyPrinter()).writeValueAsBytes(event));
+            zos.write(System.lineSeparator().getBytes());
+            if (event.get("type").equals(PhaseStarted.class.getCanonicalName())) {
+                phaseStartTimestampMapping.put(((Integer) event.get("phase_id")), (Long) event.get("timestamp"));
+            }
+        }
+        return phaseStartTimestampMapping;
+    }
+
+    private void writeEventsByPhases(ZipOutputStream zos, TrainingRun run, List<Map<String, Object>> events) throws IOException {
+        Integer currentPhase = ((Integer) events.get(0).get("phase_id"));
+        ZipEntry eventsDetailEntry = new ZipEntry(EVENTS_FOLDER + "/training_run-id" + run.getId() + "-details" + "/phase" + currentPhase + "-events" + AbstractFileExtensions.JSON_FILE_EXTENSION);
+        zos.putNextEntry(eventsDetailEntry);
+        for (Map<String, Object> event : events) {
+            if (!event.get("phase_id").equals(currentPhase)) {
+                currentPhase = ((Integer) event.get("phase_id"));
+                eventsDetailEntry = new ZipEntry(EVENTS_FOLDER + "/training_run-id" + run.getId() + "-details" + "/phase" + currentPhase + "-events" + AbstractFileExtensions.JSON_FILE_EXTENSION);
+                zos.putNextEntry(eventsDetailEntry);
+            }
+            zos.write(objectMapper.writer(new MinimalPrettyPrinter()).writeValueAsBytes(event));
+            zos.write(System.lineSeparator().getBytes());
+        }
+    }
+
+    private void writeConsoleCommands(ZipOutputStream zos, Integer sandboxId, List<Map<String, Object>> consoleCommands) throws IOException {
+        ZipEntry consoleCommandsEntry = new ZipEntry(LOGS_FOLDER + "/sandbox-" + sandboxId + "-useractions" + AbstractFileExtensions.JSON_FILE_EXTENSION);
+        zos.putNextEntry(consoleCommandsEntry);
+        for (Map<String, Object> command : consoleCommands) {
+            zos.write(objectMapper.writer(new MinimalPrettyPrinter()).writeValueAsBytes(command));
+            zos.write(System.lineSeparator().getBytes());
+        }
+    }
+
+    private void writeConsoleCommandsDetails(ZipOutputStream zos, Integer sandboxId, Map<Integer, Long> phaseStartTimestampMapping) throws IOException {
+        List<Long> phaseTimestampRanges = new ArrayList<>(phaseStartTimestampMapping.values());
+        List<Integer> phaseIds = new ArrayList<>(phaseStartTimestampMapping.keySet());
+        phaseTimestampRanges.add(Long.MAX_VALUE);
+
+        for (int i = 0; i < phaseIds.size(); i++) {
+            List<Map<String, Object>> consoleCommandsByPhase = elasticsearchServiceApi.findAllConsoleCommandsFromSandboxAndTimeRange(sandboxId, phaseTimestampRanges.get(i), phaseTimestampRanges.get(i + 1));
+            ZipEntry consoleCommandsEntryDetails = new ZipEntry(LOGS_FOLDER + "/sandbox-" + sandboxId + "-details" + "/phase" + phaseIds.get(i) + "-useractions" + AbstractFileExtensions.JSON_FILE_EXTENSION);
+            zos.putNextEntry(consoleCommandsEntryDetails);
+            for (Map<String, Object> command : consoleCommandsByPhase) {
+                zos.write(objectMapper.writer(new MinimalPrettyPrinter()).writeValueAsBytes(command));
+                zos.write(System.lineSeparator().getBytes());
+            }
+        }
+    }
+
+    private void writeTrainingDefinitionInfo(ZipOutputStream zos, TrainingInstance trainingInstance) throws IOException {
+        TrainingDefinitionArchiveDTO tD = exportImportMapper.mapToArchiveDTO(exportImportService.findById(trainingInstance.getTrainingDefinition().getId()));
+        if (tD != null) {
+            tD.setPhases(mapAbstractPhasesToArchiveDTO(trainingInstance.getTrainingDefinition().getId()));
+            ZipEntry definitionEntry = new ZipEntry("training_definition-id" + trainingInstance.getTrainingDefinition().getId() + AbstractFileExtensions.JSON_FILE_EXTENSION);
+            zos.putNextEntry(definitionEntry);
+            zos.write(objectMapper.writeValueAsBytes(tD));
+        }
+    }
+
+    private void writeSandboxDefinitionInfo(ZipOutputStream zos, TrainingInstance trainingInstance) throws IOException {
+        if (trainingInstance.getPoolId() != null) {
+            SandboxDefinitionInfo sandboxDefinitionInfo = sandboxServiceApi.getSandboxDefinitionId(trainingInstance.getPoolId());
+            ZipEntry sandboxDefinitionEntry = new ZipEntry("sandbox_definition-id" + sandboxDefinitionInfo.getId() + AbstractFileExtensions.JSON_FILE_EXTENSION);
+            zos.putNextEntry(sandboxDefinitionEntry);
+            zos.write(objectMapper.writeValueAsBytes(sandboxDefinitionInfo));
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/PhaseFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/PhaseFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..c39cf6570c0ae911bf9e01216f84e9a75502b7da
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/PhaseFacade.java
@@ -0,0 +1,191 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.BasicPhaseInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.PhaseCreateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionPhaseRelationDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import cz.muni.ics.kypo.training.adaptive.exceptions.BadRequestException;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.PhaseMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.QuestionPhaseRelationMapper;
+import cz.muni.ics.kypo.training.adaptive.service.phases.InfoPhaseService;
+import cz.muni.ics.kypo.training.adaptive.service.phases.PhaseService;
+import cz.muni.ics.kypo.training.adaptive.service.phases.QuestionnairePhaseService;
+import cz.muni.ics.kypo.training.adaptive.service.phases.TrainingPhaseService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@Transactional
+public class PhaseFacade {
+
+    private final PhaseService phaseService;
+    private final InfoPhaseService infoPhaseService;
+    private final QuestionnairePhaseService questionnairePhaseService;
+    private final TrainingPhaseService trainingPhaseService;
+    private PhaseMapper phaseMapper;
+    private QuestionPhaseRelationMapper questionPhaseRelationMapper;
+
+    @Autowired
+    public PhaseFacade(PhaseService phaseService,
+                       InfoPhaseService infoPhaseService,
+                       QuestionnairePhaseService questionnairePhaseService,
+                       TrainingPhaseService trainingPhaseService,
+                       PhaseMapper phaseMapper,
+                       QuestionPhaseRelationMapper questionPhaseRelationMapper) {
+        this.phaseService = phaseService;
+        this.infoPhaseService = infoPhaseService;
+        this.questionnairePhaseService = questionnairePhaseService;
+        this.trainingPhaseService = trainingPhaseService;
+        this.phaseMapper = phaseMapper;
+        this.questionPhaseRelationMapper = questionPhaseRelationMapper;
+    }
+
+    /**
+     * Creates new phase in training definition
+     *
+     * @param definitionId - id of definition in which phase will be created
+     * @return {@link BasicPhaseInfoDTO} of new questionnaire phase
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTrainingDefinition(#definitionId)")
+    @TransactionalWO
+    public AbstractPhaseDTO createPhase(Long definitionId, PhaseCreateDTO phaseCreateDTO) {
+        if (phaseCreateDTO.getPhaseType() == PhaseType.INFO) {
+            return phaseMapper.mapToDTO(infoPhaseService.createDefaultInfoPhase(definitionId));
+        } else if (phaseCreateDTO.getPhaseType() == PhaseType.TRAINING) {
+            return phaseMapper.mapToDTO(trainingPhaseService.createDefaultTrainingPhase(definitionId));
+        } else {
+            return phaseMapper.mapToDTO(questionnairePhaseService.createDefaultQuestionnairePhase(definitionId, phaseCreateDTO.getQuestionnaireType()));
+        }
+    }
+
+    /**
+     * Deletes specific phase by id
+     *
+     * @param phaseId - id of phase to be deleted
+     * @return the list of {@link AbstractPhaseDTO} all phase from given definition
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public List<AbstractPhaseDTO> deletePhase(Long phaseId) {
+        Long definitionId = phaseService.deletePhase(phaseId);
+        trainingPhaseService.alignDecisionMatrixForPhasesInTrainingDefinition(definitionId);
+        return this.getPhases(definitionId);
+    }
+
+    /**
+     * Finds specific phase by id
+     *
+     * @param phaseId - id of wanted phase
+     * @return wanted {@link AbstractPhaseDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalRO
+    public AbstractPhaseDTO getPhase(Long phaseId) {
+        return phaseMapper.mapToDTO(phaseService.getPhase(phaseId));
+    }
+
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTrainingDefinition(#definitionId)")
+    @TransactionalRO
+    public List<AbstractPhaseDTO> getPhases(Long definitionId) {
+        return phaseMapper.mapToListDTO(phaseService.getPhases(definitionId));
+    }
+
+    /**
+     * Updates info phase from training definition
+     *
+     * @param phaseId            - id of the phase to be updated
+     * @param infoPhaseUpdateDTO to be updated
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public InfoPhaseDTO updateInfoPhase(Long phaseId, InfoPhaseUpdateDTO infoPhaseUpdateDTO) {
+        return this.phaseMapper.mapToInfoPhaseDTO(infoPhaseService.updateInfoPhase(phaseId, phaseMapper.mapToEntity(infoPhaseUpdateDTO)));
+    }
+
+    /**
+     * Updates training phase from training definition
+     *
+     * @param phaseId                - id of the phase to be updated
+     * @param trainingPhaseUpdateDTO training phase with the updated data
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public TrainingPhaseDTO updateTrainingPhase(Long phaseId, TrainingPhaseUpdateDTO trainingPhaseUpdateDTO) {
+        return this.phaseMapper.mapToTrainingPhaseDTO(trainingPhaseService.updateTrainingPhase(phaseId, phaseMapper.mapToEntity(trainingPhaseUpdateDTO)));
+    }
+
+    /**
+     * updates questionnaire phase from training definition
+     *
+     * @param phaseId                - id of the phase to be updated
+     * @param questionnaireUpdateDTO questionnaire phase with the updated data
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public QuestionnairePhaseDTO updateQuestionnairePhase(Long phaseId, QuestionnaireUpdateDTO questionnaireUpdateDTO) {
+        this.checkOrderOfPhaseRelations(questionnaireUpdateDTO.getPhaseRelations());
+        QuestionnairePhase questionnairePhaseToUpdate = questionnairePhaseService.updateQuestionnairePhase(phaseId, this.phaseMapper.mapToEntity(questionnaireUpdateDTO));
+
+        List<QuestionPhaseRelation> updatedQuestionPhaseRelations = questionnaireUpdateDTO.getPhaseRelations()
+                .stream()
+                .map(phaseRelationDTO -> this.questionnairePhaseService.updateQuestionPhaseRelation(
+                        questionnairePhaseToUpdate,
+                        this.questionPhaseRelationMapper.mapToEntity(phaseRelationDTO),
+                        phaseRelationDTO.getQuestionIds(),
+                        phaseRelationDTO.getPhaseId())
+                )
+                .collect(Collectors.toList());
+        questionnairePhaseToUpdate.getQuestionPhaseRelations().clear();
+        questionnairePhaseToUpdate.getQuestionPhaseRelations().addAll(updatedQuestionPhaseRelations);
+        return phaseMapper.mapToQuestionnairePhaseDTO(questionnairePhaseToUpdate);
+    }
+
+    private void checkOrderOfPhaseRelations(List<QuestionPhaseRelationDTO> questionPhaseRelationDTOS) {
+        questionPhaseRelationDTOS.sort(Comparator.comparing(QuestionPhaseRelationDTO::getOrder));
+        Integer order = 0;
+        for (QuestionPhaseRelationDTO questionPhaseRelationDTO : questionPhaseRelationDTOS) {
+            if (!questionPhaseRelationDTO.getOrder().equals(order)) {
+                throw new BadRequestException("The question phase relation has wrong order. The current order is " + questionPhaseRelationDTO.getOrder() +
+                        " but should be " + order);
+            }
+            order++;
+        }
+    }
+
+    /**
+     * Move phase to the different position and modify orders of phase between moved phase and new position.
+     *
+     * @param phaseIdFrom - id of the phase to be moved to the new position
+     * @param newPosition - position where phase will be moved
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseIdFrom)")
+    @TransactionalWO
+    public void movePhaseToSpecifiedOrder(Long phaseIdFrom, int newPosition) {
+        phaseService.movePhaseToSpecifiedOrder(phaseIdFrom, newPosition);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TaskFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TaskFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..0aa20e746533da8fb5bb994b16ea0af03a6860dd
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TaskFacade.java
@@ -0,0 +1,122 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.Task;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.dto.BasicPhaseInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskCopyDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TaskMapper;
+import cz.muni.ics.kypo.training.adaptive.service.SecurityService;
+import cz.muni.ics.kypo.training.adaptive.service.phases.PhaseService;
+import cz.muni.ics.kypo.training.adaptive.service.phases.TaskService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+
+@Service
+@Transactional
+public class TaskFacade {
+
+    private final TaskService taskService;
+    private final PhaseService phaseService;
+    private TaskMapper taskMapper;
+
+    @Autowired
+    public TaskFacade(TaskService taskService,
+                      PhaseService phaseService,
+                      TaskMapper taskMapper) {
+        this.taskService = taskService;
+        this.phaseService = phaseService;
+        this.taskMapper = taskMapper;
+    }
+
+    /**
+     * Creates new task in phase
+     *
+     * @param phaseId - id of phase in which task will be created
+     * @return {@link BasicPhaseInfoDTO} of new questionnaire phase
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public TaskDTO createDefaultTask(Long phaseId) {
+        AbstractPhase trainingPhase = phaseService.getPhase(phaseId);
+        if (!(trainingPhase instanceof TrainingPhase)) {
+            throw new EntityConflictException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, "The specified phase isn't training phase."));
+        }
+        return taskMapper.mapToTaskDTO(this.taskService.createDefaultTask((TrainingPhase) trainingPhase));
+    }
+
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenPhase(#phaseId)")
+    @TransactionalWO
+    public TaskDTO createTask(Long phaseId, TaskCopyDTO taskCopyDTO) {
+        AbstractPhase trainingPhase = this.phaseService.getPhase(phaseId);
+        if (!(trainingPhase instanceof TrainingPhase)) {
+            throw new EntityConflictException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, "The specified phase isn't training phase."));
+        }
+        Task taskToCreate = this.taskMapper.mapToEntity(taskCopyDTO);
+        taskToCreate.setTrainingPhase((TrainingPhase) trainingPhase);
+        return this.taskMapper.mapToTaskDTO(this.taskService.createTask(taskToCreate));
+    }
+
+    /**
+     * Finds specific task by id
+     *
+     * @param taskId - id of wanted task
+     * @return wanted {@link TaskDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTask(#taskId)")
+    @TransactionalRO
+    public TaskDTO getTask(Long taskId) {
+        return taskMapper.mapToTaskDTO(taskService.getTask(taskId));
+    }
+
+    /**
+     * Updates info phase from training definition
+     *
+     * @param taskId        - id of task to be updated
+     * @param taskUpdateDto task with the updated values
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTask(#taskId)")
+    @TransactionalWO
+    public TaskDTO updateTask(Long taskId, TaskUpdateDTO taskUpdateDto) {
+        return this.taskMapper.mapToTaskDTO(this.taskService.updateTask(taskId, this.taskMapper.mapToEntity(taskUpdateDto)));
+    }
+
+    /**
+     * Deletes specific task by id
+     *
+     * @param taskId - id of task to be deleted
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTask(#taskId)")
+    @TransactionalWO
+    public void removeTask(Long taskId) {
+        Task taskToRemove = this.taskService.getTask(taskId);
+        this.taskService.removeTask(taskToRemove);
+    }
+
+    /**
+     * Move task to the different position and modify orders of task between moved task and new position.
+     *
+     * @param taskIdFrom  - id of the task to be moved to the new position
+     * @param newPosition - position where task will be moved
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTask(#taskIdFrom)")
+    @TransactionalWO
+    public void moveTaskToSpecifiedOrder(Long taskIdFrom, int newPosition) {
+        taskService.moveTaskToSpecifiedOrder(taskIdFrom, newPosition);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingDefinitionFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingDefinitionFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..2bbb7f45934f469ac2f7b59c005a5ead8b6beb4d
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingDefinitionFacade.java
@@ -0,0 +1,336 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsDesignerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsDesignerOrOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.*;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleType;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.InternalServerErrorException;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.PhaseMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TrainingDefinitionMapper;
+import cz.muni.ics.kypo.training.adaptive.service.SecurityService;
+import cz.muni.ics.kypo.training.adaptive.service.UserService;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * The type Training definition facade.
+ */
+@Service
+@Transactional
+public class TrainingDefinitionFacade {
+
+    private TrainingDefinitionService trainingDefinitionService;
+    private UserService userService;
+    private UserManagementServiceApi userManagementServiceApi;
+    private SecurityService securityService;
+    private TrainingDefinitionMapper trainingDefinitionMapper;
+    private PhaseMapper phaseMapper;
+
+    /**
+     * Instantiates a new Training definition facade.
+     *
+     * @param trainingDefinitionService the training definition service
+     * @param trainingDefMapper         the training def mapper
+     * @param phaseMapper               the phase mapper
+     * @param userService               the user service
+     * @param securityService           the security service
+     */
+    @Autowired
+    public TrainingDefinitionFacade(TrainingDefinitionService trainingDefinitionService,
+                                    UserService userService,
+                                    UserManagementServiceApi userManagementServiceApi,
+                                    SecurityService securityService,
+                                    TrainingDefinitionMapper trainingDefMapper,
+                                    PhaseMapper phaseMapper) {
+        this.trainingDefinitionService = trainingDefinitionService;
+        this.userService = userService;
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.securityService = securityService;
+        this.trainingDefinitionMapper = trainingDefMapper;
+        this.phaseMapper = phaseMapper;
+    }
+
+    /**
+     * Finds specific Training Definition by id
+     *
+     * @param id of a Training Definition that would be returned
+     * @return specific {@link TrainingDefinitionByIdDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_TRAINEE)")
+    @TransactionalRO
+    public TrainingDefinitionByIdDTO findById(Long id) {
+        TrainingDefinition trainingDefinition = trainingDefinitionService.findById(id);
+        TrainingDefinitionByIdDTO trainingDefinitionByIdDTO = trainingDefinitionMapper.mapToDTOById(trainingDefinition);
+        trainingDefinitionByIdDTO.setPhases(gatherPhases(id));
+        return trainingDefinitionByIdDTO;
+    }
+
+    private List<AbstractPhaseDTO> gatherPhases(Long definitionId) {
+        List<AbstractPhase> phases = trainingDefinitionService.findAllPhasesFromDefinition(definitionId);
+        return phases.stream()
+                .map(phase -> this.phaseMapper.mapToDTO(phase))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Find all Training Definitions.
+     *
+     * @param predicate represents a predicate (boolean-valued function) of one argument.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return page of all {@link TrainingDefinitionDTO}
+     */
+    @IsDesignerOrAdmin
+    @TransactionalRO
+    public PageResultResource<TrainingDefinitionDTO> findAll(Predicate predicate, Pageable pageable) {
+        if (securityService.hasRole(RoleTypeSecurity.ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)) {
+            return mapToDtoAndAddArchivingInfo(trainingDefinitionService.findAll(predicate, pageable));
+        } else {
+            Long loggedInUserId = userManagementServiceApi.getLoggedInUserRefId();
+            return mapToDtoAndAddArchivingInfo(trainingDefinitionService.findAll(predicate, pageable, loggedInUserId));
+        }
+    }
+
+    private PageResultResource<TrainingDefinitionDTO> mapToDtoAndAddArchivingInfo(Page<TrainingDefinition> trainingDefinitionPage) {
+        PageResultResource<TrainingDefinitionDTO> resource = trainingDefinitionMapper.mapToPageResultResource(trainingDefinitionPage);
+        for (TrainingDefinitionDTO trainingDefinitionDTO : resource.getContent()) {
+            trainingDefinitionDTO.setCanBeArchived(checkIfCanBeArchived(trainingDefinitionDTO.getId()));
+        }
+        return resource;
+    }
+
+    /**
+     * Find all Training Definitions.
+     *
+     * @param state    represents a string if the training definitions should be relased or not.
+     * @param pageable pageable parameter with information about pagination.
+     * @return page of all {@link TrainingDefinitionInfoDTO} accessible for organizers
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalRO
+    public PageResultResource<TrainingDefinitionInfoDTO> findAllForOrganizers(TDState state, Pageable pageable) {
+        Long loggedInUserId = userManagementServiceApi.getLoggedInUserRefId();
+        if (state == TDState.RELEASED) {
+            return trainingDefinitionMapper.mapToPageResultResourceInfoDTO(
+                    trainingDefinitionService.findAllForOrganizers(TDState.RELEASED, pageable));
+        } else if (state == TDState.UNRELEASED) {
+            if (securityService.hasRole(RoleTypeSecurity.ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)) {
+                return trainingDefinitionMapper.mapToPageResultResourceInfoDTO(
+                        trainingDefinitionService.findAllForOrganizers(TDState.UNRELEASED, pageable));
+            } else if (securityService.hasRole(RoleTypeSecurity.ROLE_ADAPTIVE_TRAINING_DESIGNER) && securityService.hasRole(RoleTypeSecurity.ROLE_ADAPTIVE_TRAINING_ORGANIZER)) {
+                return trainingDefinitionMapper.mapToPageResultResourceInfoDTO(
+                        trainingDefinitionService.findAllForDesigner(loggedInUserId, pageable));
+            } else {
+                return new PageResultResource<>(new ArrayList<>(), new PageResultResource.Pagination());
+            }
+        }
+        throw new InternalServerErrorException("It is required to provide training definition state that is RELEASED or UNRELEASED");
+    }
+
+    /**
+     * Creates new training definition
+     *
+     * @param trainingDefinition to be created
+     * @return DTO of created definition, {@link TrainingDefinitionCreateDTO}
+     */
+    @IsDesignerOrAdmin
+    @TransactionalWO
+    public TrainingDefinitionByIdDTO create(TrainingDefinitionCreateDTO trainingDefinition) {
+        TrainingDefinition newTrainingDefinition = trainingDefinitionMapper.mapCreateToEntity(trainingDefinition);
+        TrainingDefinition createdTrainingDefinition = trainingDefinitionService.create(newTrainingDefinition);
+        return trainingDefinitionMapper.mapToDTOById(createdTrainingDefinition);
+    }
+
+    /**
+     * Updates training definition
+     *
+     * @param trainingDefinitionUpdateDTO to be updated
+     */
+    @IsDesignerOrAdmin
+    @TransactionalWO
+    public void update(TrainingDefinitionUpdateDTO trainingDefinitionUpdateDTO) {
+        TrainingDefinition mappedTrainingDefinition = trainingDefinitionMapper.mapUpdateToEntity(trainingDefinitionUpdateDTO);
+        TrainingDefinition trainingDefinition = trainingDefinitionService.findById(trainingDefinitionUpdateDTO.getId());
+        mappedTrainingDefinition.setAuthors(new HashSet<>(trainingDefinition.getAuthors()));
+        trainingDefinitionService.update(mappedTrainingDefinition);
+    }
+
+    private UserRef createUserRefFromDTO(UserRefDTO userToBeCreated) {
+        UserRef userRef = new UserRef();
+        userRef.setUserRefId(userToBeCreated.getUserRefId());
+        return userRef;
+    }
+
+    /**
+     * Clones Training Definition by id
+     *
+     * @param id    of definition to be cloned
+     * @param title the title of cloned definition
+     * @return DTO of cloned definition, {@link TrainingDefinitionByIdDTO}
+     */
+    @IsDesignerOrAdmin
+    @TransactionalWO
+    public TrainingDefinitionByIdDTO clone(Long id, String title) {
+        TrainingDefinitionByIdDTO clonedDefinition = trainingDefinitionMapper.mapToDTOById(trainingDefinitionService.clone(id, title));
+        clonedDefinition.setPhases(gatherPhases(clonedDefinition.getId()));
+        return clonedDefinition;
+    }
+
+    /**
+     * Deletes specific training instance based on id
+     *
+     * @param id of definition to be deleted
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTrainingDefinition(#id)")
+    @TransactionalWO
+    public void delete(Long id) {
+        trainingDefinitionService.delete(id);
+    }
+
+    /**
+     * Get users with given role
+     *
+     * @param roleType   the wanted role type
+     * @param pageable   pageable parameter with information about pagination.
+     * @param givenName  the given name
+     * @param familyName the family name
+     * @return list of users {@link UserRefDTO}
+     */
+    @IsDesignerOrAdmin
+    @TransactionalRO
+    public PageResultResource<UserRefDTO> getUsersWithGivenRole(RoleType roleType, Pageable pageable, String givenName, String familyName) {
+        return userManagementServiceApi.getUserRefsByRole(roleType, pageable, givenName, familyName);
+    }
+
+    /**
+     * Switch state of definition to unreleased
+     *
+     * @param definitionId - id of training definition
+     * @param state        - new state of TD
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isDesignerOfGivenTrainingDefinition(#definitionId)")
+    public void switchState(Long definitionId, TDState state) {
+        trainingDefinitionService.switchState(definitionId, state);
+    }
+
+    private boolean checkIfCanBeArchived(Long definitionId) {
+        List<TrainingInstance> instances = trainingDefinitionService.findAllTrainingInstancesByTrainingDefinitionId(definitionId);
+        for (TrainingInstance trainingInstance : instances) {
+            if (trainingInstance.getEndTime().isAfter(LocalDateTime.now(Clock.systemUTC()))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Retrieve all authors for given training definition.
+     *
+     * @param trainingDefinitionId id of the training definition for which to get the authors
+     * @param pageable             pageable parameter with information about pagination.
+     * @param givenName            optional parameter used for filtration
+     * @param familyName           optional parameter used for filtration
+     * @return returns all authors in given training definition.
+     */
+    @IsDesignerOrOrganizerOrAdmin
+    public PageResultResource<UserRefDTO> getAuthors(Long trainingDefinitionId, Pageable pageable, String givenName, String familyName) {
+        TrainingDefinition trainingDefinition = trainingDefinitionService.findById(trainingDefinitionId);
+        return userManagementServiceApi.getUserRefDTOsByUserIds(trainingDefinition.getAuthors().stream()
+                        .map(UserRef::getUserRefId)
+                        .collect(Collectors.toSet()),
+                pageable, givenName, familyName);
+    }
+
+    /**
+     * Retrieve all designers not in the given training definition.
+     *
+     * @param trainingDefinitionId id of the training definition which users should be excluded from the result list.
+     * @param pageable             pageable parameter with information about pagination.
+     * @param givenName            optional parameter used for filtration
+     * @param familyName           optional parameter used for filtration
+     * @return returns all designers not in the given training definition.
+     */
+    @IsDesignerOrOrganizerOrAdmin
+    @TransactionalRO
+    public PageResultResource<UserRefDTO> getDesignersNotInGivenTrainingDefinition(Long trainingDefinitionId, Pageable pageable, String givenName, String familyName) {
+        TrainingDefinition trainingDefinition = trainingDefinitionService.findById(trainingDefinitionId);
+        Set<Long> excludedUsers = trainingDefinition.getAuthors().stream()
+                .map(UserRef::getUserRefId)
+                .collect(Collectors.toSet());
+        return userManagementServiceApi.getUserRefsByRoleAndNotWithIds(RoleType.ROLE_TRAINING_DESIGNER, excludedUsers, pageable, givenName, familyName);
+    }
+
+    /**
+     * Concurrently add authors to the given training definition and remove authors from the training definition.
+     *
+     * @param trainingDefinitionId if of the training definition to be updated
+     * @param authorsAddition      ids of the authors to be added to the training definition
+     * @param authorsRemoval       ids of the authors to be removed from the training definition.
+     */
+    @IsDesignerOrAdmin
+    @TransactionalWO
+    public void editAuthors(Long trainingDefinitionId, Set<Long> authorsAddition, Set<Long> authorsRemoval) {
+        TrainingDefinition trainingDefinition = trainingDefinitionService.findById(trainingDefinitionId);
+        Long loggedInUserRefId = userManagementServiceApi.getLoggedInUserRefId();
+        if (authorsRemoval != null && !authorsRemoval.isEmpty()) {
+            authorsRemoval.remove(loggedInUserRefId);
+            trainingDefinition.removeAuthorsByUserRefIds(authorsRemoval);
+        }
+        if (authorsAddition != null && !authorsAddition.isEmpty()) {
+            addAuthorsToTrainingDefinition(trainingDefinition, authorsAddition);
+        }
+    }
+
+    private void addAuthorsToTrainingDefinition(TrainingDefinition trainingDefinition, Set<Long> userRefIds) {
+        PageResultResource<UserRefDTO> authors;
+        int page = 0;
+        do {
+            authors = userManagementServiceApi.getUserRefDTOsByUserIds(userRefIds, PageRequest.of(page, 999), null, null);
+            Set<Long> actualAuthorsIds = trainingDefinition.getAuthors().stream()
+                    .map(UserRef::getUserRefId)
+                    .collect(Collectors.toSet());
+            page++;
+            for (UserRefDTO author : authors.getContent()) {
+                if (actualAuthorsIds.contains(author.getUserRefId())) {
+                    continue;
+                }
+                try {
+                    trainingDefinition.addAuthor(userService.getUserByUserRefId(author.getUserRefId()));
+                } catch (EntityNotFoundException ex) {
+                    trainingDefinition.addAuthor(userService.createUserRef(createUserRefFromDTO(author)));
+                }
+            }
+        } while (authors.getPagination().getTotalPages() != page);
+
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingInstanceFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingInstanceFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..9064ff46c1919f05d489879fbe88fcc31e3b8bc8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingInstanceFacade.java
@@ -0,0 +1,380 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.traininginstance.*;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleType;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TrainingInstanceMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TrainingRunMapper;
+import cz.muni.ics.kypo.training.adaptive.service.SecurityService;
+import cz.muni.ics.kypo.training.adaptive.service.UserService;
+import cz.muni.ics.kypo.training.adaptive.service.api.ElasticsearchServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.api.SandboxServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingInstanceService;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingRunService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * The type Training instance facade.
+ */
+@Service
+public class TrainingInstanceFacade {
+
+    private TrainingInstanceService trainingInstanceService;
+    private TrainingDefinitionService trainingDefinitionService;
+    private TrainingRunService trainingRunService;
+    private UserService userService;
+    private UserManagementServiceApi userManagementServiceApi;
+    private SecurityService securityService;
+    private SandboxServiceApi sandboxServiceApi;
+    private ElasticsearchServiceApi elasticsearchServiceApi;
+    private TrainingInstanceMapper trainingInstanceMapper;
+    private TrainingRunMapper trainingRunMapper;
+
+
+    /**
+     * Instantiates a new Training instance facade.
+     *
+     * @param trainingInstanceService   the training instance service
+     * @param trainingDefinitionService the training definition service
+     * @param trainingRunService        the training run service
+     * @param trainingInstanceMapper    the training instance mapper
+     * @param trainingRunMapper         the training run mapper
+     * @param userService               the user service
+     * @param elasticsearchServiceApi   the elasticsearch api service
+     * @param securityService           the security service
+     */
+    @Autowired
+    public TrainingInstanceFacade(TrainingInstanceService trainingInstanceService,
+                                  TrainingDefinitionService trainingDefinitionService,
+                                  TrainingRunService trainingRunService,
+                                  UserService userService,
+                                  UserManagementServiceApi userManagementServiceApi,
+                                  ElasticsearchServiceApi elasticsearchServiceApi,
+                                  SandboxServiceApi sandboxServiceApi,
+                                  SecurityService securityService,
+                                  TrainingInstanceMapper trainingInstanceMapper,
+                                  TrainingRunMapper trainingRunMapper) {
+        this.trainingInstanceService = trainingInstanceService;
+        this.trainingDefinitionService = trainingDefinitionService;
+        this.trainingRunService = trainingRunService;
+        this.userService = userService;
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.elasticsearchServiceApi = elasticsearchServiceApi;
+        this.sandboxServiceApi = sandboxServiceApi;
+        this.securityService = securityService;
+        this.trainingInstanceMapper = trainingInstanceMapper;
+        this.trainingRunMapper = trainingRunMapper;
+    }
+
+    /**
+     * Finds specific Training Instance by id
+     *
+     * @param id of a Training Instance that would be returned
+     * @return specific {@link TrainingInstanceDTO} by id
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#id)")
+    @TransactionalRO
+    public TrainingInstanceDTO findById(Long id) {
+        return trainingInstanceMapper.mapToDTO(trainingInstanceService.findByIdIncludingDefinition(id));
+    }
+
+    /**
+     * Find all Training Instances.
+     *
+     * @param predicate represents a predicate (boolean-valued function) of one argument.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return page of all {@link TrainingInstanceFindAllResponseDTO}
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalRO
+    public PageResultResource<TrainingInstanceFindAllResponseDTO> findAll(Predicate predicate, Pageable pageable) {
+        if (securityService.hasRole(RoleTypeSecurity.ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)) {
+            return trainingInstanceMapper.mapToPageResultResourceBasicView(trainingInstanceService.findAll(predicate, pageable));
+        }
+        return trainingInstanceMapper.mapToPageResultResourceBasicView(trainingInstanceService.findAll(predicate, pageable, userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Updates training instance
+     *
+     * @param trainingInstanceUpdateDTO to be updated
+     * @return new access token if it was changed
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalWO
+    public String update(TrainingInstanceUpdateDTO trainingInstanceUpdateDTO) {
+        TrainingInstance trainingInstanceToUpdate = trainingInstanceMapper.mapUpdateToEntity(trainingInstanceUpdateDTO);
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceUpdateDTO.getId());
+
+        if (LocalDateTime.now(Clock.systemUTC()).isAfter(trainingInstance.getStartTime()) &&
+                !trainingInstance.getTrainingDefinition().getId().equals(trainingInstanceUpdateDTO.getTrainingDefinitionId())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstance.getId(),
+                    "The training definition assigned to running training instance cannot be changed."));
+        }
+        trainingInstanceToUpdate.setTrainingDefinition(trainingDefinitionService.findById(trainingInstanceUpdateDTO.getTrainingDefinitionId()));
+        return trainingInstanceService.update(trainingInstanceToUpdate);
+    }
+
+    /**
+     * Creates new training instance
+     *
+     * @param trainingInstanceCreateDTO to be created
+     * @return created {@link TrainingInstanceDTO}
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalWO
+    public TrainingInstanceDTO create(TrainingInstanceCreateDTO trainingInstanceCreateDTO) {
+        TrainingInstance trainingInstance = trainingInstanceMapper.mapCreateToEntity(trainingInstanceCreateDTO);
+        trainingInstance.setTrainingDefinition(trainingDefinitionService.findById(trainingInstanceCreateDTO.getTrainingDefinitionId()));
+        trainingInstance.setId(null);
+        return trainingInstanceMapper.mapToDTO(trainingInstanceService.create(trainingInstance));
+    }
+
+    private void addOrganizersToTrainingInstance(TrainingInstance trainingInstance, Set<Long> userRefIdsOfOrganizers) {
+        if (userRefIdsOfOrganizers.isEmpty()) return;
+        PageResultResource<UserRefDTO> organizers;
+        int page = 0;
+        do {
+            organizers = userManagementServiceApi.getUserRefDTOsByUserIds(userRefIdsOfOrganizers, PageRequest.of(page, 999), null, null);
+            Set<Long> actualOrganizersIds = trainingInstance.getOrganizers().stream()
+                    .map(UserRef::getUserRefId)
+                    .collect(Collectors.toSet());
+            page++;
+            for (UserRefDTO organizer : organizers.getContent()) {
+                if (actualOrganizersIds.contains(organizer.getUserRefId())) {
+                    continue;
+                }
+                try {
+                    trainingInstance.addOrganizer(userService.getUserByUserRefId(organizer.getUserRefId()));
+                } catch (EntityNotFoundException ex) {
+                    trainingInstance.addOrganizer(userService.createUserRef(createUserRefFromDTO(organizer)));
+                }
+            }
+        } while (organizers.getPagination().getTotalPages() != page);
+    }
+
+    private UserRef createUserRefFromDTO(UserRefDTO userToBeCreated) {
+        UserRef userRef = new UserRef();
+        userRef.setUserRefId(userToBeCreated.getUserRefId());
+        return userRef;
+    }
+
+    /**
+     * Deletes specific training instance based on id
+     *
+     * @param trainingInstanceId of training instance to be deleted
+     * @param forceDelete        indicates if this training run should be force deleted.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalWO
+    public void delete(Long trainingInstanceId, boolean forceDelete) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        if (forceDelete) {
+            Set<TrainingRun> trainingRunsInTrainingInstance = trainingRunService.findAllByTrainingInstanceId(trainingInstanceId);
+            trainingRunsInTrainingInstance.forEach(tr -> trainingRunService.deleteTrainingRun(tr.getId(), true));
+            if (trainingInstance.getPoolId() != null) {
+                sandboxServiceApi.unlockPool(trainingInstance.getPoolId());
+                deleteBashCommandsFromPool(trainingInstance.getPoolId());
+            }
+        } else if (!trainingInstanceService.checkIfInstanceIsFinished(trainingInstanceId) && trainingRunService.existsAnyForTrainingInstance(trainingInstanceId)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstanceId,
+                    "Active training instance with already assigned training runs cannot be deleted. Please delete training runs assigned to training instance and try again."));
+            // not possible to delete active training instances with associated training runs
+        } else if (trainingInstance.getPoolId() != null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstanceId,
+                    "First, you must unassign pool id from training instance then try it again."));
+            // not possible to delete training instance with associated pool
+        }
+        trainingInstanceService.delete(trainingInstance);
+        elasticsearchServiceApi.deleteEventsByTrainingInstanceId(trainingInstance.getId());
+    }
+
+    private void deleteBashCommandsFromPool(Long poolId) {
+        try {
+            elasticsearchServiceApi.deleteBashCommandsFromPool(poolId);
+        } catch (MicroserviceApiException ignored) {
+        }
+    }
+
+    /**
+     * Assign pool in training instance new training instance
+     *
+     * @param trainingInstanceId              the training instance id
+     * @param trainingInstanceAssignPoolIdDTO of training instance to be deleted
+     * @return the training instance basic info dto
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalWO
+    public TrainingInstanceBasicInfoDTO assignPoolToTrainingInstance(Long trainingInstanceId, TrainingInstanceAssignPoolIdDTO trainingInstanceAssignPoolIdDTO) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        if (trainingInstance.getPoolId() != null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", trainingInstance.getId().getClass(), trainingInstance.getId(),
+                    "Training instance already contains pool Id. Please first unassign pool id and then assign another pool again."));
+        }
+        // lock pool and update pool
+        sandboxServiceApi.lockPool(trainingInstanceAssignPoolIdDTO.getPoolId());
+        trainingInstance.setPoolId(trainingInstanceAssignPoolIdDTO.getPoolId());
+        TrainingInstance updatedTrainingInstance = trainingInstanceService.updateTrainingInstancePool(trainingInstance);
+        return trainingInstanceMapper.mapEntityToTIBasicInfo(updatedTrainingInstance);
+    }
+
+    /**
+     * Reassign pool in training instance  or assignes new training instance
+     *
+     * @param trainingInstanceId of training instance to be deleted
+     * @return the training instance basic info dto
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalWO
+    public TrainingInstanceBasicInfoDTO unassignPoolInTrainingInstance(Long trainingInstanceId) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        if (trainingInstance.getPoolId() == null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", trainingInstance.getId().getClass(), trainingInstance.getId(),
+                    "The training instance does not contain any assigned pool already."));
+        }
+        // unlock previously assigned pool
+        sandboxServiceApi.unlockPool(trainingInstance.getPoolId());
+        deleteBashCommandsFromPool(trainingInstance.getPoolId());
+
+        trainingInstance.setPoolId(null);
+        TrainingInstance updatedTrainingInstance = trainingInstanceService.updateTrainingInstancePool(trainingInstance);
+        return trainingInstanceMapper.mapEntityToTIBasicInfo(updatedTrainingInstance);
+    }
+
+    /**
+     * Finds all Training Runs by specific Training Instance.
+     *
+     * @param trainingInstanceId id of Training Instance whose Training Runs would be returned.
+     * @param isActive           if isActive attribute is True, only active runs are returned
+     * @param pageable           pageable parameter with information about pagination.
+     * @return Page of {@link TrainingRunDTO} of specific Training Instance
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalRO
+    public PageResultResource<TrainingRunDTO> findTrainingRunsByTrainingInstance(Long trainingInstanceId, Boolean isActive, Pageable pageable) {
+        Page<TrainingRun> trainingRuns = trainingInstanceService.findTrainingRunsByTrainingInstance(trainingInstanceId, isActive, pageable);
+        PageResultResource<TrainingRunDTO> trainingRunDTOsPageResult = trainingRunMapper.mapToPageResultResource(trainingRuns);
+        addParticipantsToTrainingRunDTOs(trainingRunDTOsPageResult.getContent());
+        return trainingRunDTOsPageResult;
+    }
+
+    private void addParticipantsToTrainingRunDTOs(List<TrainingRunDTO> trainingRunDTOS) {
+        trainingRunDTOS.forEach(trainingRunDTO ->
+                trainingRunDTO.setParticipantRef(userManagementServiceApi.getUserRefDTOByUserRefId(trainingRunDTO.getParticipantRef().getUserRefId())));
+    }
+
+    /**
+     * Check if instance can be deleted.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return true if instance can be deleted, false if not and message. {@link TrainingInstanceIsFinishedInfoDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalRO
+    public TrainingInstanceIsFinishedInfoDTO checkIfInstanceCanBeDeleted(Long trainingInstanceId) {
+        TrainingInstanceIsFinishedInfoDTO infoDTO = new TrainingInstanceIsFinishedInfoDTO();
+        if (trainingInstanceService.checkIfInstanceIsFinished(trainingInstanceId)) {
+            infoDTO.setHasFinished(true);
+            infoDTO.setMessage("Training instance has already finished and can be safely deleted.");
+        } else {
+            infoDTO.setHasFinished(false);
+            infoDTO.setMessage("WARNING: Training instance is still running! Are you sure you want to delete it?");
+        }
+        return infoDTO;
+    }
+
+    /**
+     * Retrieve all organizers for given training instance .
+     *
+     * @param trainingInstanceId id of the training instance for which to get the organizers
+     * @param pageable           pageable parameter with information about pagination.
+     * @param givenName          optional parameter used for filtration
+     * @param familyName         optional parameter used for filtration
+     * @return returns all organizers in given training instance.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalRO
+    public PageResultResource<UserRefDTO> getOrganizersOfTrainingInstance(Long trainingInstanceId, Pageable pageable, String givenName, String familyName) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        return userManagementServiceApi.getUserRefDTOsByUserIds(trainingInstance.getOrganizers().stream()
+                .map(UserRef::getUserRefId)
+                .collect(Collectors.toSet()), pageable, givenName, familyName);
+    }
+
+    /**
+     * Retrieve all organizers not in the given training instance.
+     *
+     * @param trainingInstanceId id of the training instance which users should be excluded from the result list.
+     * @param pageable           pageable parameter with information about pagination.
+     * @param givenName          optional parameter used for filtration
+     * @param familyName         optional parameter used for filtration
+     * @return returns all organizers not in the given training instance.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalRO
+    public PageResultResource<UserRefDTO> getOrganizersNotInGivenTrainingInstance(Long trainingInstanceId, Pageable pageable, String givenName, String familyName) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        Set<Long> excludedOrganizers = trainingInstance.getOrganizers().stream()
+                .map(UserRef::getUserRefId)
+                .collect(Collectors.toSet());
+        return userManagementServiceApi.getUserRefsByRoleAndNotWithIds(RoleType.ROLE_TRAINING_ORGANIZER, excludedOrganizers, pageable, givenName, familyName);
+    }
+
+    /**
+     * Concurrently add organizers to the given training instance and remove authors from the training instance.
+     *
+     * @param trainingInstanceId if of the training instance to be updated
+     * @param organizersAddition ids of the organizers to be added to the training instance
+     * @param organizersRemoval  ids of the organizers to be removed from the training instance.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingInstance(#trainingInstanceId)")
+    @TransactionalWO
+    public void editOrganizers(Long trainingInstanceId, Set<Long> organizersAddition, Set<Long> organizersRemoval) {
+        TrainingInstance trainingInstance = trainingInstanceService.findById(trainingInstanceId);
+        Long loggedInUserRefId = userManagementServiceApi.getLoggedInUserRefId();
+        if (organizersRemoval != null && !organizersRemoval.isEmpty()) {
+            organizersRemoval.remove(loggedInUserRefId);
+            trainingInstance.removeOrganizersByUserRefIds(organizersRemoval);
+        }
+        if (organizersAddition != null && !organizersAddition.isEmpty()) {
+            addOrganizersToTrainingInstance(trainingInstance, organizersAddition);
+        }
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingPhaseFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingPhaseFacade.java
deleted file mode 100644
index f2fbf5e1f60bf53d92527018eb327b1ba9f5a661..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingPhaseFacade.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.facade;
-
-import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.PhaseCreateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.enums.PhaseTypeCreate;
-import cz.muni.ics.kypo.training.adaptive.service.InfoPhaseService;
-import cz.muni.ics.kypo.training.adaptive.service.PhaseService;
-import cz.muni.ics.kypo.training.adaptive.service.QuestionnairePhaseService;
-import cz.muni.ics.kypo.training.adaptive.service.TrainingPhaseService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-@Service
-public class TrainingPhaseFacade {
-
-    private final PhaseService phaseService;
-    private final InfoPhaseService infoPhaseService;
-    private final QuestionnairePhaseService questionnairePhaseService;
-    private final TrainingPhaseService trainingPhaseService;
-
-    @Autowired
-    public TrainingPhaseFacade(PhaseService phaseService, InfoPhaseService infoPhaseService,
-                               QuestionnairePhaseService questionnairePhaseService, TrainingPhaseService trainingPhaseService) {
-        this.phaseService = phaseService;
-        this.infoPhaseService = infoPhaseService;
-        this.questionnairePhaseService = questionnairePhaseService;
-        this.trainingPhaseService = trainingPhaseService;
-    }
-
-    public AbstractPhaseDTO createPhase(Long trainingDefinitionId, PhaseCreateDTO phaseCreateDTO) {
-        AbstractPhaseDTO abstractPhaseDto;
-        if (PhaseTypeCreate.INFO.equals(phaseCreateDTO.getPhaseType())) {
-            abstractPhaseDto = infoPhaseService.createDefaultInfoPhase(trainingDefinitionId);
-        } else if (PhaseTypeCreate.TRAINING.equals(phaseCreateDTO.getPhaseType())) {
-            abstractPhaseDto = trainingPhaseService.createDefaultTrainingPhase(trainingDefinitionId);
-        } else {
-            abstractPhaseDto = questionnairePhaseService.createDefaultQuestionnairePhase(trainingDefinitionId, phaseCreateDTO);
-        }
-
-        return abstractPhaseDto;
-    }
-
-
-    public List<AbstractPhaseDTO> deletePhase(Long definitionId, Long phaseId) {
-        phaseService.deletePhase(definitionId, phaseId);
-        trainingPhaseService.alignDecisionMatrixForPhasesInTrainingDefinition(definitionId);
-        return getPhases(definitionId);
-    }
-
-    public AbstractPhaseDTO getPhase(Long definitionId, Long phaseId) {
-        return phaseService.getPhase(definitionId, phaseId);
-    }
-
-    public List<AbstractPhaseDTO> getPhases(Long definitionId) {
-        return phaseService.getPhases(definitionId);
-    }
-
-    public InfoPhaseDTO updateInfoPhase(Long definitionId, Long phaseId, InfoPhaseUpdateDTO infoPhaseUpdateDto) {
-        return infoPhaseService.updateInfoPhase(definitionId, phaseId, infoPhaseUpdateDto);
-    }
-
-    public TrainingPhaseDTO updateTrainingPhase(Long definitionId, Long phaseId, TrainingPhaseUpdateDTO trainingPhaseUpdate) {
-        return trainingPhaseService.updateTrainingPhase(definitionId, phaseId, trainingPhaseUpdate);
-    }
-
-    public QuestionnairePhaseDTO updateQuestionnairePhase(Long definitionId, Long phaseId, QuestionnaireUpdateDTO questionnaireUpdateDto) {
-        return questionnairePhaseService.updateQuestionnairePhase(definitionId, phaseId, questionnaireUpdateDto);
-    }
-
-    public void movePhaseToSpecifiedOrder(Long phaseIdFrom, int newPosition) {
-        phaseService.movePhaseToSpecifiedOrder(phaseIdFrom, newPosition);
-    }
-
-
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingRunFacade.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingRunFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..980b96fb6d86a0f617678abc2f5c4fedeaaa1a24
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/facade/TrainingRunFacade.java
@@ -0,0 +1,439 @@
+package cz.muni.ics.kypo.training.adaptive.facade;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsOrganizerOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsTrainee;
+import cz.muni.ics.kypo.training.adaptive.annotations.security.IsTraineeOrAdmin;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.BasicPhaseInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.IsCorrectAnswerDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionChoiceDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.AccessTrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.AccessedTrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunByIdDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.enums.Actions;
+import cz.muni.ics.kypo.training.adaptive.enums.PhaseType;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.PhaseMapper;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.TrainingRunMapper;
+import cz.muni.ics.kypo.training.adaptive.service.SecurityService;
+import cz.muni.ics.kypo.training.adaptive.service.UserService;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingRunService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * The type Training run facade.
+ */
+@Service
+public class TrainingRunFacade {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrainingRunFacade.class);
+
+    private TrainingRunService trainingRunService;
+    private UserService userService;
+    private UserManagementServiceApi userManagementServiceApi;
+    private TrainingRunMapper trainingRunMapper;
+    private PhaseMapper phaseMapper;
+
+
+    /**
+     * Instantiates a new Training run facade.
+     *
+     * @param trainingRunService the training run service
+     * @param userService        the user service
+     * @param trainingRunMapper  the training run mapper
+     * @param phaseMapper        the phase mapper
+     */
+    @Autowired
+    public TrainingRunFacade(TrainingRunService trainingRunService,
+                             UserService userService,
+                             UserManagementServiceApi userManagementServiceApi,
+                             TrainingRunMapper trainingRunMapper,
+                             PhaseMapper phaseMapper) {
+        this.trainingRunService = trainingRunService;
+        this.userService = userService;
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.trainingRunMapper = trainingRunMapper;
+        this.phaseMapper = phaseMapper;
+    }
+
+    /**
+     * Finds specific Training Run by id
+     *
+     * @param id of a Training Run that would be returned
+     * @return specific {@link TrainingRunByIdDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#id)")
+    @TransactionalRO
+    public TrainingRunByIdDTO findById(Long id) {
+        TrainingRun trainingRun = trainingRunService.findById(id);
+        TrainingRunByIdDTO trainingRunByIdDTO = trainingRunMapper.mapToFindByIdDTO(trainingRun);
+        trainingRunByIdDTO.setDefinitionId(trainingRun.getTrainingInstance().getTrainingDefinition().getId());
+        trainingRunByIdDTO.setInstanceId(trainingRun.getTrainingInstance().getId());
+        trainingRunByIdDTO.setParticipantRef(userManagementServiceApi.getUserRefDTOByUserRefId(trainingRunByIdDTO.getParticipantRef().getUserRefId()));
+        return trainingRunByIdDTO;
+    }
+
+    /**
+     * Find all Training Runs.
+     *
+     * @param predicate specifies query to the database.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return page of all {@link TrainingRunDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)")
+    @TransactionalRO
+    public PageResultResource<TrainingRunDTO> findAll(Predicate predicate, Pageable pageable) {
+        PageResultResource<TrainingRunDTO> trainingRunDTOPageResultResource = trainingRunMapper.mapToPageResultResource(trainingRunService.findAll(predicate, pageable));
+        addParticipantsToTrainingRunDTOs(trainingRunDTOPageResultResource.getContent());
+        return trainingRunDTOPageResultResource;
+    }
+
+    /**
+     * Delete selected training runs.
+     *
+     * @param trainingRunIds training runs to delete
+     * @param forceDelete    indicates if this training run should be force deleted.
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalWO
+    public void deleteTrainingRuns(List<Long> trainingRunIds, boolean forceDelete) {
+        trainingRunIds.forEach(trainingRunId -> trainingRunService.deleteTrainingRun(trainingRunId, forceDelete));
+    }
+
+    /**
+     * Delete selected training run.
+     *
+     * @param trainingRunId training run to delete
+     * @param forceDelete   indicates if this training run should be force deleted.
+     */
+    @IsOrganizerOrAdmin
+    @TransactionalWO
+    public void deleteTrainingRun(Long trainingRunId, boolean forceDelete) {
+        trainingRunService.deleteTrainingRun(trainingRunId, forceDelete);
+    }
+
+    /**
+     * Finds all Training Runs of logged in user.
+     *
+     * @param pageable    pageable parameter with information about pagination.
+     * @param sortByTitle optional parameter. "asc" for ascending sort, "desc" for descending and null if sort is not wanted
+     * @return Page of all {@link AccessedTrainingRunDTO} of logged in user.
+     */
+    @IsTraineeOrAdmin
+    @TransactionalRO
+    public PageResultResource<AccessedTrainingRunDTO> findAllAccessedTrainingRuns(Pageable pageable, String sortByTitle) {
+        Page<TrainingRun> trainingRuns = trainingRunService.findAllByParticipantRefUserRefId(pageable);
+        return convertToAccessedRunDTO(trainingRuns, sortByTitle);
+    }
+
+    /**
+     * Resume given training run.
+     *
+     * @param trainingRunId id of Training Run to be resumed.
+     * @return {@link AccessTrainingRunDTO} response
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public AccessTrainingRunDTO resumeTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = trainingRunService.resumeTrainingRun(trainingRunId);
+        return convertToAccessTrainingRunDTO(trainingRun);
+    }
+
+    /**
+     * Access Training Run by logged in user based on given accessToken.
+     *
+     * @param accessToken of one training instance
+     * @return {@link AccessTrainingRunDTO} response
+     */
+    @IsTraineeOrAdmin
+    @Transactional
+    public AccessTrainingRunDTO accessTrainingRun(String accessToken) {
+        TrainingInstance trainingInstance = trainingRunService.getTrainingInstanceForParticularAccessToken(accessToken);
+        // checking if the user is not accessing to his existing training run (resume action)
+        Long participantRefId = userManagementServiceApi.getLoggedInUserRefId();
+        Optional<TrainingRun> accessedTrainingRun = trainingRunService.findRunningTrainingRunOfUser(accessToken, participantRefId);
+        if (accessedTrainingRun.isPresent()) {
+            return convertToAccessTrainingRunDTO(trainingRunService.resumeTrainingRun(accessedTrainingRun.get().getId()));
+        }
+        // Check if the user already clicked access training run, in that case, it returns an exception (it prevents concurrent accesses).
+        trainingRunService.trAcquisitionLockToPreventManyRequestsFromSameUser(participantRefId, trainingInstance.getId(), accessToken);
+        try {
+            // During this action we create a new TrainingRun and lock and get sandbox from OpenStack Sandbox API
+            TrainingRun trainingRun = trainingRunService.createTrainingRun(trainingInstance, participantRefId);
+            trainingRunService.assignSandbox(trainingRun, trainingInstance.getPoolId());
+            return convertToAccessTrainingRunDTO(trainingRun);
+        } catch (Exception e) {
+            // delete/rollback acquisition lock when no training run either sandbox is assigned
+            trainingRunService.deleteTrAcquisitionLockToPreventManyRequestsFromSameUser(participantRefId, trainingInstance.getId());
+            throw e;
+        }
+    }
+
+    private AccessTrainingRunDTO convertToAccessTrainingRunDTO(TrainingRun trainingRun) {
+        AccessTrainingRunDTO accessTrainingRunDTO = new AccessTrainingRunDTO();
+        accessTrainingRunDTO.setTrainingRunID(trainingRun.getId());
+        accessTrainingRunDTO.setAbstractPhaseDTO(getCorrectAbstractPhaseDTO(trainingRun.getCurrentPhase()));
+        accessTrainingRunDTO.setShowStepperBar(trainingRun.getTrainingInstance().getTrainingDefinition().isShowStepperBar());
+        accessTrainingRunDTO.setInfoAboutPhases(getInfoAboutPhases(trainingRun.getCurrentPhase().getTrainingDefinition().getId()));
+        accessTrainingRunDTO.setSandboxInstanceRefId(trainingRun.getSandboxInstanceRefId());
+        accessTrainingRunDTO.setInstanceId(trainingRun.getTrainingInstance().getId());
+        accessTrainingRunDTO.setStartTime(trainingRun.getStartTime());
+        if (trainingRun.getCurrentPhase() instanceof TrainingPhase && trainingRun.isSolutionTaken()) {
+            accessTrainingRunDTO.setTakenSolution(trainingRun.getCurrentTask().getSolution());
+        }
+        return accessTrainingRunDTO;
+    }
+
+    private List<BasicPhaseInfoDTO> getInfoAboutPhases(Long definitionId) {
+        List<BasicPhaseInfoDTO> infoAboutPhases = new ArrayList<>();
+        List<AbstractPhase> phases = trainingRunService.getPhases(definitionId);
+        for (AbstractPhase abstractPhase : phases) {
+            if (abstractPhase instanceof QuestionnairePhase) {
+                infoAboutPhases.add(new BasicPhaseInfoDTO(abstractPhase.getId(), abstractPhase.getTitle(), PhaseType.QUESTIONNAIRE, abstractPhase.getOrder()));
+            } else if (abstractPhase instanceof TrainingPhase) {
+                infoAboutPhases.add(new BasicPhaseInfoDTO(abstractPhase.getId(), abstractPhase.getTitle(), PhaseType.TRAINING, abstractPhase.getOrder()));
+            } else {
+                infoAboutPhases.add(new BasicPhaseInfoDTO(abstractPhase.getId(), abstractPhase.getTitle(), PhaseType.INFO, abstractPhase.getOrder()));
+            }
+        }
+        return infoAboutPhases;
+    }
+
+    /**
+     * Finds all Training Runs by specific Training Definition and logged in user.
+     *
+     * @param trainingDefinitionId id of Training Definition
+     * @param pageable             pageable parameter with information about pagination.
+     * @return Page of all {@link AccessedTrainingRunDTO} of logged in user and given definition.
+     */
+    @IsTrainee
+    @TransactionalRO
+    public PageResultResource<TrainingRunDTO> findAllByTrainingDefinitionAndParticipant(Long trainingDefinitionId, Pageable pageable) {
+        Page<TrainingRun> trainingRuns = trainingRunService.findAllByTrainingDefinitionAndParticipant(trainingDefinitionId, pageable);
+        PageResultResource<TrainingRunDTO> trainingRunDTOPageResultResource = trainingRunMapper.mapToPageResultResource(trainingRuns);
+        addParticipantsToTrainingRunDTOs(trainingRunDTOPageResultResource.getContent());
+        return trainingRunDTOPageResultResource;
+    }
+
+    /**
+     * Finds all Training Runs of specific training definition.
+     *
+     * @param trainingDefinitionId id of Training Definition whose Training Runs would be returned.
+     * @param pageable             pageable parameter with information about pagination.
+     * @return Page of all {@link AccessedTrainingRunDTO} of given definition.
+     */
+    @IsTrainee
+    @TransactionalRO
+    public PageResultResource<TrainingRunDTO> findAllByTrainingDefinition(Long trainingDefinitionId, Pageable pageable) {
+        Page<TrainingRun> trainingRuns = trainingRunService.findAllByTrainingDefinition(trainingDefinitionId, pageable);
+        PageResultResource<TrainingRunDTO> trainingRunDTOPageResultResource = trainingRunMapper.mapToPageResultResource(trainingRuns);
+        addParticipantsToTrainingRunDTOs(trainingRunDTOPageResultResource.getContent());
+        return trainingRunDTOPageResultResource;
+    }
+
+    /**
+     * Gets next phase of given Training Run and set new current phase.
+     *
+     * @param trainingRunId id of Training Run whose next phase should be returned.
+     * @return {@link AbstractPhaseDTO}
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public AbstractPhaseDTO getNextPhase(Long trainingRunId) {
+        AbstractPhase abstractPhase;
+        abstractPhase = trainingRunService.getNextPhase(trainingRunId);
+        return getCorrectAbstractPhaseDTO(abstractPhase);
+    }
+
+    /**
+     * Gets solution of current phase of given Training Run.
+     *
+     * @param trainingRunId id of Training Run which current phase gets solution for.
+     * @return solution of current phase.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public String getSolution(Long trainingRunId) {
+        return trainingRunService.getSolution(trainingRunId);
+    }
+
+    /**
+     * Check given answer of given Training Run.
+     *
+     * @param trainingRunId id of Training Run to check answer.
+     * @param answer        string which player submit.
+     * @return true if answer is correct, false if answer is wrong.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public IsCorrectAnswerDTO isCorrectAnswer(Long trainingRunId, String answer) {
+        IsCorrectAnswerDTO correctAnswerDTO = new IsCorrectAnswerDTO();
+        correctAnswerDTO.setCorrect(trainingRunService.isCorrectAnswer(trainingRunId, answer));
+        correctAnswerDTO.setRemainingAttempts(trainingRunService.getRemainingAttempts(trainingRunId));
+        if (correctAnswerDTO.getRemainingAttempts() == 0) {
+            correctAnswerDTO.setSolution(getSolution(trainingRunId));
+        }
+        return correctAnswerDTO;
+    }
+
+    /**
+     * Finish training run.
+     *
+     * @param trainingRunId id of Training Run to be finished.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public void finishTrainingRun(Long trainingRunId) {
+        trainingRunService.finishTrainingRun(trainingRunId);
+    }
+
+    /**
+     * Archive training run.
+     *
+     * @param trainingRunId id of Training Run to be archived.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isOrganizerOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalWO
+    public void archiveTrainingRun(Long trainingRunId) {
+        trainingRunService.archiveTrainingRun(trainingRunId);
+    }
+
+    /**
+     * Evaluate and store responses to questionnaire.
+     *
+     * @param trainingRunId     id of Training Run to be finish.
+     * @param responsesAsString responses to questionnaire
+     */
+    @IsTrainee
+    @TransactionalWO
+    public void evaluateResponsesToQuestionnaire(Long trainingRunId, String responsesAsString) {
+        trainingRunService.evaluateResponsesToQuestionnaire(trainingRunId, responsesAsString);
+    }
+
+    /**
+     * Retrieve info about the participant of the given training run.
+     *
+     * @param trainingRunId id of the training run whose participant you want to get.
+     * @return returns participant of the given training run.
+     */
+    @PreAuthorize("hasAuthority(T(cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity).ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR)" +
+            "or @securityService.isTraineeOfGivenTrainingRun(#trainingRunId)")
+    @TransactionalRO
+    public UserRefDTO getParticipant(Long trainingRunId) {
+        TrainingRun trainingRun = trainingRunService.findById(trainingRunId);
+        return userManagementServiceApi.getUserRefDTOByUserRefId(trainingRun.getParticipantRef().getUserRefId());
+    }
+
+    private void addParticipantsToTrainingRunDTOs(List<TrainingRunDTO> trainingRunDTOS) {
+        trainingRunDTOS.forEach(trainingRunDTO ->
+                trainingRunDTO.setParticipantRef(userManagementServiceApi.getUserRefDTOByUserRefId(trainingRunDTO.getParticipantRef().getUserRefId())));
+    }
+
+    private PageResultResource<AccessedTrainingRunDTO> convertToAccessedRunDTO(Page<TrainingRun> trainingRuns, String sortByTitle) {
+        List<AccessedTrainingRunDTO> accessedTrainingRunDTOS = new ArrayList<>();
+        for (TrainingRun trainingRun : trainingRuns) {
+            AccessedTrainingRunDTO accessedTrainingRunDTO = generateAccessedTrainingRunDTO(trainingRun);
+            accessedTrainingRunDTOS.add(accessedTrainingRunDTO);
+        }
+        return new PageResultResource<>(sortByTitle(accessedTrainingRunDTOS, sortByTitle), trainingRunMapper.createPagination(trainingRuns));
+    }
+
+    private List<AccessedTrainingRunDTO> sortByTitle(List<AccessedTrainingRunDTO> runs, String sortByTitle) {
+        if (sortByTitle != null && !sortByTitle.isBlank()) {
+            if (!runs.isEmpty()) {
+                if (sortByTitle.equals("asc")) {
+                    runs.sort(Comparator.comparing(AccessedTrainingRunDTO::getTitle));
+                } else if (sortByTitle.equals("desc")) {
+                    runs.sort(Comparator.comparing(AccessedTrainingRunDTO::getTitle).reversed());
+                }
+            }
+        }
+        return runs;
+    }
+
+    private AccessedTrainingRunDTO generateAccessedTrainingRunDTO(TrainingRun trainingRun) {
+        AccessedTrainingRunDTO accessedTrainingRunDTO = new AccessedTrainingRunDTO();
+        accessedTrainingRunDTO.setId(trainingRun.getId());
+        accessedTrainingRunDTO.setTitle(trainingRun.getTrainingInstance().getTitle());
+        accessedTrainingRunDTO.setTrainingInstanceStartDate(trainingRun.getTrainingInstance().getStartTime());
+        accessedTrainingRunDTO.setTrainingInstanceEndDate(trainingRun.getTrainingInstance().getEndTime());
+        accessedTrainingRunDTO.setInstanceId(trainingRun.getTrainingInstance().getId());
+        accessedTrainingRunDTO.setNumberOfPhases(trainingRunService.getMaxPhaseOrder(trainingRun.getTrainingInstance().getTrainingDefinition().getId()) + 1);
+        accessedTrainingRunDTO.setCurrentPhaseOrder(trainingRun.getCurrentPhase().getOrder() + 1);
+        accessedTrainingRunDTO.setPossibleAction(resolvePossibleActions(accessedTrainingRunDTO, trainingRun.isPhaseAnswered()));
+        return accessedTrainingRunDTO;
+    }
+
+    private Actions resolvePossibleActions(AccessedTrainingRunDTO trainingRunDTO, boolean isCurrentPhaseAnswered) {
+        boolean isTrainingRunFinished = isCurrentPhaseAnswered && trainingRunDTO.getCurrentPhaseOrder() == trainingRunDTO.getNumberOfPhases();
+        boolean isTrainingInstanceRunning = LocalDateTime.now(Clock.systemUTC()).isBefore(trainingRunDTO.getTrainingInstanceEndDate());
+        if (isTrainingRunFinished || !isTrainingInstanceRunning) {
+            return Actions.RESULTS;
+        } else {
+            return Actions.RESUME;
+        }
+    }
+
+    private AbstractPhaseDTO getCorrectAbstractPhaseDTO(AbstractPhase abstractPhase) {
+        AbstractPhaseDTO abstractPhaseDTO;
+        if (abstractPhase instanceof QuestionnairePhase) {
+            QuestionnairePhase questionnairePhase = (QuestionnairePhase) abstractPhase;
+            abstractPhaseDTO = phaseMapper.mapToDTO(questionnairePhase);
+            abstractPhaseDTO.setPhaseType(PhaseType.QUESTIONNAIRE);
+            deleteInfoAboutCorrectnessFromQuestions((QuestionnairePhaseDTO) abstractPhaseDTO);
+        } else if (abstractPhase instanceof TrainingPhase) {
+            TrainingPhase trainingPhase = (TrainingPhase) abstractPhase;
+            abstractPhaseDTO = phaseMapper.mapToDTO(trainingPhase);
+            //TODO change the DTO with one task and remove the correct answer
+            abstractPhaseDTO.setPhaseType(PhaseType.TRAINING);
+        } else {
+            InfoPhase infoPhase = (InfoPhase) abstractPhase;
+            abstractPhaseDTO = phaseMapper.mapToDTO(infoPhase);
+            abstractPhaseDTO.setPhaseType(PhaseType.INFO);
+        }
+        return abstractPhaseDTO;
+    }
+
+    private void deleteInfoAboutCorrectnessFromQuestions(QuestionnairePhaseDTO questionnairePhaseDTO) {
+        for (QuestionDTO questionDTO : questionnairePhaseDTO.getQuestions()) {
+            for (QuestionChoiceDTO questionChoiceDTO : questionDTO.getChoices()) {
+                questionChoiceDTO.setCorrect(null);
+            }
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/handler/CustomRestExceptionHandler.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/handler/CustomRestExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..7087c03922c066c58eefae90dd0b5c67c46ec528
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/handler/CustomRestExceptionHandler.java
@@ -0,0 +1,433 @@
+package cz.muni.ics.kypo.training.adaptive.handler;
+
+import cz.muni.ics.kypo.training.adaptive.exceptions.*;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiEntityError;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiError;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.ApiMicroserviceError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.multipart.support.MissingServletRequestPartException;
+import org.springframework.web.servlet.NoHandlerFoundException;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+import org.springframework.web.util.UrlPathHelper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ConstraintViolationException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+/**
+ * The type Custom rest exception handler training.
+ */
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@RestControllerAdvice
+public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
+
+    private static final UrlPathHelper URL_PATH_HELPER = new UrlPathHelper();
+    private static Logger LOG = LoggerFactory.getLogger(CustomRestExceptionHandler.class);
+
+    @Override
+    protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status,
+                                                        final WebRequest request) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex, final HttpHeaders headers,
+                                                                     final HttpStatus status, final WebRequest request) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, final HttpHeaders headers,
+                                                                          final HttpStatus status, final WebRequest request) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status,
+                                                                   final WebRequest request) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.NOT_FOUND,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers,
+                                                                         final HttpStatus status, final WebRequest request) {
+        final StringBuilder supportedHttpMethods = new StringBuilder();
+        supportedHttpMethods.append(ex.getMethod());
+        supportedHttpMethods.append(" method is not supported for this request. Supported methods are ");
+        ex.getSupportedHttpMethods().forEach(t -> supportedHttpMethods.append(t + " "));
+
+        final ApiError apiError = ApiError.of(
+                HttpStatus.NOT_FOUND,
+                getInitialException(ex).getLocalizedMessage(),
+                supportedHttpMethods.toString(),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers,
+                                                                     final HttpStatus status, final WebRequest request) {
+        final StringBuilder supportedMediaTypes = new StringBuilder();
+        supportedMediaTypes.append(ex.getContentType());
+        supportedMediaTypes.append(" media type is not supported. Supported media types are ");
+        ex.getSupportedMediaTypes().forEach(t -> supportedMediaTypes.append(t + " "));
+
+        final ApiError apiError = ApiError.of(
+                HttpStatus.UNSUPPORTED_MEDIA_TYPE,
+                getInitialException(ex).getLocalizedMessage(),
+                supportedMediaTypes.toString(),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers,
+                                                                  final HttpStatus status, final WebRequest request) {
+        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
+        List<ObjectError> objectErrors = ex.getBindingResult().getGlobalErrors();
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                (fieldErrors.isEmpty() ? objectErrors : fieldErrors)
+                        .stream()
+                        .map(DefaultMessageSourceResolvable::getDefaultMessage)
+                        .collect(java.util.stream.Collectors.joining(", ")),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    @Override
+    protected ResponseEntity<Object> handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers,
+                                                                  final HttpStatus status, final WebRequest request) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                ex.getMostSpecificCause().getMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+
+    // Handling of own exceptions
+
+    @ExceptionHandler({InsufficientAuthenticationException.class})
+    protected ResponseEntity<Object> handleAuthenticationException(final InsufficientAuthenticationException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.UNAUTHORIZED,
+                ex.getMessage(),
+                getFullStackTrace(ex),
+                request.getContextPath());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle constraint violation response entity.
+     *
+     * @param ex  the ex
+     * @param req the req
+     * @return the response entity
+     */
+    @ExceptionHandler({ConstraintViolationException.class})
+    public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex,
+                                                            HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle bad request exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler(BadRequestException.class)
+    public ResponseEntity<Object> handleBadRequestException(final BadRequestException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                BadRequestException.class.getAnnotation(ResponseStatus.class).value(),
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle forbidden exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler(ForbiddenException.class)
+    public ResponseEntity<Object> handleForbiddenException(final ForbiddenException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                ForbiddenException.class.getAnnotation(ResponseStatus.class).value(),
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle internal server error exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler(InternalServerErrorException.class)
+    public ResponseEntity<Object> handleInternalServerErrorException(final InternalServerErrorException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                InternalServerErrorException.class.getAnnotation(ResponseStatus.class).value(),
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle entity not found exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({EntityNotFoundException.class})
+    public ResponseEntity<Object> handleEntityNotFoundException(final EntityNotFoundException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiEntityError apiError = ApiEntityError.of(
+                EntityNotFoundException.class.getAnnotation(ResponseStatus.class).value(),
+                EntityNotFoundException.class.getAnnotation(ResponseStatus.class).reason(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req),
+                ex.getEntityErrorDetail());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle spring access denied exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({AccessDeniedException.class})
+    public ResponseEntity<Object> handleSpringAccessDeniedException(AccessDeniedException ex, WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.FORBIDDEN,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle illegal argument exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+// thrown from SERVICE layer (nullpointers, illegal argument etc.)
+    @ExceptionHandler(IllegalArgumentException.class)
+    public ResponseEntity<Object> handleIllegalArgumentException(final IllegalArgumentException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.NOT_ACCEPTABLE,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle null pointer exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler(NullPointerException.class)
+    public ResponseEntity<Object> handleNullPointerException(final NullPointerException ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.BAD_REQUEST,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    // thrown from REST controllers
+
+    /**
+     * Handle entity conflict exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({EntityConflictException.class})
+    public ResponseEntity<Object> handleEntityConflictException(final EntityConflictException ex, final WebRequest request,
+                                                                HttpServletRequest req) {
+        final ApiError apiError = ApiEntityError.of(
+                EntityConflictException.class.getAnnotation(ResponseStatus.class).value(),
+                EntityConflictException.class.getAnnotation(ResponseStatus.class).reason(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req),
+                ex.getEntityErrorDetail());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle too many requests exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({TooManyRequestsException.class})
+    public ResponseEntity<Object> handleTooManyRequestsException(final TooManyRequestsException ex, final WebRequest request,
+                                                                 HttpServletRequest req) {
+        final ApiError apiError = ApiEntityError.of(
+                TooManyRequestsException.class.getAnnotation(ResponseStatus.class).value(),
+                TooManyRequestsException.class.getAnnotation(ResponseStatus.class).reason(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req),
+                ex.getEntityErrorDetail());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle unprocessable entity exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({UnprocessableEntityException.class})
+    public ResponseEntity<Object> handleUnprocessableEntityException(final UnprocessableEntityException ex, final WebRequest request,
+                                                                     HttpServletRequest req) {
+        final ApiError apiError = ApiEntityError.of(
+                UnprocessableEntityException.class.getAnnotation(ResponseStatus.class).value(),
+                UnprocessableEntityException.class.getAnnotation(ResponseStatus.class).reason(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req),
+                ex.getEntityErrorDetail());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle user and group api exception response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({MicroserviceApiException.class})
+    public ResponseEntity<Object> handleUserAndGroupApiException(final MicroserviceApiException ex, final WebRequest request,
+                                                                 HttpServletRequest req) {
+        final ApiError apiError = ApiMicroserviceError.of(
+                HttpStatus.FORBIDDEN,
+                ex.getMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req),
+                ex.getApiSubError());
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    /**
+     * Handle all response entity.
+     *
+     * @param ex      the ex
+     * @param request the request
+     * @param req     the req
+     * @return the response entity
+     */
+    @ExceptionHandler({Exception.class})
+    public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request, HttpServletRequest req) {
+        final ApiError apiError = ApiError.of(
+                HttpStatus.INTERNAL_SERVER_ERROR,
+                getInitialException(ex).getLocalizedMessage(),
+                getFullStackTrace(ex),
+                URL_PATH_HELPER.getRequestUri(req));
+        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
+    }
+
+    private Exception getInitialException(Exception exception) {
+        while (exception.getCause() != null) {
+            exception = (Exception) exception.getCause();
+        }
+        return exception;
+    }
+
+    private String getFullStackTrace(Exception exception) {
+        try (StringWriter sw = new StringWriter();
+             PrintWriter pw = new PrintWriter(sw)) {
+            exception.printStackTrace(pw);
+            String fullStackTrace = sw.toString();
+            LOG.error(fullStackTrace);
+            return fullStackTrace;
+        } catch (IOException e) {
+            LOG.error("It was not possible to get the stack trace for that exception: ", e);
+        }
+        return "It was not possible to get the stack trace for that exception.";
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapper/BeanMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/BeanMapper.java
similarity index 68%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/mapper/BeanMapper.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/BeanMapper.java
index 391cab66e878e5a8246d4b63850e39147d040338..8a6c14dbeaeb1bb7b080dabf5aab3a15daa6300b 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapper/BeanMapper.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/BeanMapper.java
@@ -1,28 +1,14 @@
-package cz.muni.ics.kypo.training.adaptive.mapper;
-
-import cz.muni.ics.kypo.training.adaptive.domain.AbstractPhase;
-import cz.muni.ics.kypo.training.adaptive.domain.DecisionMatrixRow;
-import cz.muni.ics.kypo.training.adaptive.domain.InfoPhase;
-import cz.muni.ics.kypo.training.adaptive.domain.Question;
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionChoice;
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionPhaseRelation;
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionnairePhase;
-import cz.muni.ics.kypo.training.adaptive.domain.Task;
-import cz.muni.ics.kypo.training.adaptive.domain.TrainingPhase;
+package cz.muni.ics.kypo.training.adaptive.mapping;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.*;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionChoice;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
 import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseDTO;
 import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionChoiceDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionPhaseRelationDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.DecisionMatrixRowDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskCopyDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.*;
+import cz.muni.ics.kypo.training.adaptive.dto.training.*;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
 import org.mapstruct.factory.Mappers;
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/CloneMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/CloneMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e9ee5d4324b9e7f6beb59eb44227002613521e8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/CloneMapper.java
@@ -0,0 +1,66 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.*;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionChoice;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.List;
+
+/**
+ * The TrainingDefinitionMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type TrainingDefinitionMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring",
+        uses = {UserRefMapper.class},
+        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface CloneMapper extends ParentMapper {
+
+    @Mapping(target = "state", constant = "UNRELEASED")
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "authors", expression = "java(new java.util.HashSet<>())")
+    TrainingDefinition clone(TrainingDefinition entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "trainingDefinition", ignore = true)
+    InfoPhase clone(InfoPhase entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "trainingDefinition", ignore = true)
+    TrainingPhase clone(TrainingPhase entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "trainingDefinition", ignore = true)
+    QuestionnairePhase clone(QuestionnairePhase entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "questionPhaseRelations", ignore = true)
+    @Mapping(target = "questionnairePhase", ignore = true)
+    @Mapping(target = "choices", ignore = true)
+    Question clone(Question entity);
+
+    List<Question> clone(List<Question> entities);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "question", ignore = true)
+    QuestionChoice clone(QuestionChoice entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "trainingPhase", ignore = true)
+    Task clone(Task entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "trainingPhase", ignore = true)
+    DecisionMatrixRow clone(DecisionMatrixRow entity);
+
+    @Mapping(target = "id", ignore = true)
+    @Mapping(target = "questions", ignore = true)
+    @Mapping(target = "relatedTrainingPhase", ignore = true)
+    @Mapping(target = "questionnairePhase", ignore = true)
+    QuestionPhaseRelation clone(QuestionPhaseRelation entity);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/DecisionMatrixMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/DecisionMatrixMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ebcacab5a52df60fc53cbd71babfcebae3b62ca
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/DecisionMatrixMapper.java
@@ -0,0 +1,41 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.DecisionMatrixRow;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training.DecisionMatrixRowArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.training.DecisionMatrixRowExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.DecisionMatrixRowImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.DecisionMatrixRowDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The DecisionMatrixMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type DecisionMatrixRow and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+        unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface DecisionMatrixMapper extends ParentMapper {
+    // INFO PHASE
+    DecisionMatrixRow mapToEntity(DecisionMatrixRowDTO dto);
+
+    DecisionMatrixRow mapToEntity(DecisionMatrixRowImportDTO dto);
+
+    DecisionMatrixRowDTO mapToDecisionMatrixRowDTO(DecisionMatrixRow entity);
+
+    DecisionMatrixRowExportDTO mapToDecisionMatrixRowExportDTO(DecisionMatrixRow entity);
+
+    DecisionMatrixRowArchiveDTO mapToDecisionMatrixRowArchiveDTO(DecisionMatrixRow entity);
+
+    List<DecisionMatrixRow> mapToList(Collection<DecisionMatrixRowDTO> dtos);
+
+    List<DecisionMatrixRowDTO> mapToListDTO(Collection<DecisionMatrixRow> entities);
+
+    Set<DecisionMatrixRow> mapToSet(Collection<DecisionMatrixRowDTO> dtos);
+
+    Set<DecisionMatrixRowDTO> mapToSetDTO(Collection<DecisionMatrixRow> entities);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ExportImportMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ExportImportMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..71958e4a953cc7f1d2572a00bd3f284bdb1c9491
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ExportImportMapper.java
@@ -0,0 +1,34 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingDefinitionArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingInstanceArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.training.TrainingRunArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.training.TrainingDefinitionWithPhasesExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.training.TrainingRunExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.ImportTrainingDefinitionDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+/**
+ * The ExportImportMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type ExportImportMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring",
+        uses = {UserRefMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface ExportImportMapper extends ParentMapper {
+
+    TrainingDefinitionWithPhasesExportDTO mapToDTO(TrainingDefinition entity);
+
+    TrainingDefinition mapToEntity(ImportTrainingDefinitionDTO dto);
+
+    TrainingInstanceArchiveDTO mapToDTO(TrainingInstance entity);
+
+    TrainingRunExportDTO mapToDTO(TrainingRun entity);
+
+    TrainingDefinitionArchiveDTO mapToArchiveDTO(TrainingDefinition entity);
+
+    TrainingRunArchiveDTO mapToArchiveDTO(TrainingRun entity);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ParentMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ParentMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..67432b86066f02e6f685c7df08573d455297802b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/ParentMapper.java
@@ -0,0 +1,21 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import org.springframework.data.domain.Page;
+
+/**
+ * ParentMapper is parent class for mappers which contains only one method for creating pagination.
+ */
+
+public interface ParentMapper {
+
+    default PageResultResource.Pagination createPagination(Page<?> objects) {
+        PageResultResource.Pagination pageMetadata = new PageResultResource.Pagination();
+        pageMetadata.setNumber(objects.getNumber());
+        pageMetadata.setNumberOfElements(objects.getNumberOfElements());
+        pageMetadata.setSize(objects.getSize());
+        pageMetadata.setTotalElements(objects.getTotalElements());
+        pageMetadata.setTotalPages(objects.getTotalPages());
+        return pageMetadata;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/PhaseMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/PhaseMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..30d09b42b7e026eaefcf5629445284d6330b9bdd
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/PhaseMapper.java
@@ -0,0 +1,145 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.BasicPhaseInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.AbstractPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.info.InfoPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire.QuestionnairePhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training.TrainingPhaseArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.AbstractPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.info.InfoPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire.QuestionnairePhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.training.TrainingPhaseExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.info.InfoPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionnairePhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.TrainingPhaseImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.InternalServerErrorException;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * The PhaseMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type Phase and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", uses = {QuestionMapper.class, TaskMapper.class, DecisionMatrixMapper.class, QuestionPhaseRelationMapper.class},
+        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface PhaseMapper extends ParentMapper {
+    // INFO PHASE
+    InfoPhase mapToEntity(InfoPhaseDTO dto);
+
+    InfoPhase mapToEntity(InfoPhaseUpdateDTO dto);
+
+    InfoPhase mapToEntity(InfoPhaseImportDTO dto);
+
+    BasicPhaseInfoDTO mapToBasicPhaseInfoDTO(InfoPhase entity);
+
+    @Mapping(target = "phaseType", constant = "INFO")
+    InfoPhaseDTO mapToInfoPhaseDTO(InfoPhase entity);
+
+    @Mapping(target = "phaseType", constant = "INFO")
+    InfoPhaseExportDTO mapToInfoPhaseExportDTO(InfoPhase entity);
+
+    @Mapping(target = "phaseType", constant = "INFO")
+    InfoPhaseArchiveDTO mapToInfoPhaseArchiveDTO(InfoPhase entity);
+
+    // QUESTIONNAIRE PHASE
+    QuestionnairePhase mapToEntity(QuestionnairePhaseDTO dto);
+
+    QuestionnairePhase mapToEntity(QuestionnairePhaseImportDTO dto);
+
+    QuestionnairePhase mapToEntity(QuestionnaireUpdateDTO dto);
+
+    BasicPhaseInfoDTO mapToBasicPhaseInfoDTO(QuestionnairePhase entity);
+
+    @Mapping(target = "phaseRelations", source = "questionPhaseRelations")
+    @Mapping(target = "phaseType", constant = "QUESTIONNAIRE")
+    QuestionnairePhaseDTO mapToQuestionnairePhaseDTO(QuestionnairePhase entity);
+
+    @Mapping(target = "phaseRelations", source = "questionPhaseRelations")
+    @Mapping(target = "phaseType", constant = "QUESTIONNAIRE")
+    QuestionnairePhaseArchiveDTO mapToQuestionnairePhaseArchiveDTO(QuestionnairePhase entity);
+
+    @Mapping(target = "phaseRelations", source = "questionPhaseRelations")
+    @Mapping(target = "phaseType", constant = "QUESTIONNAIRE")
+    QuestionnairePhaseExportDTO mapToQuestionnairePhaseExportDTO(QuestionnairePhase entity);
+
+    // TRAINING PHASE
+    TrainingPhase mapToEntity(TrainingPhaseDTO dto);
+
+    TrainingPhase mapToEntity(TrainingPhaseUpdateDTO dto);
+
+    TrainingPhase mapToEntity(TrainingPhaseImportDTO dto);
+
+    @Mapping(target = "phaseType", constant = "TRAINING")
+    BasicPhaseInfoDTO mapToBasicPhaseInfoDTO(TrainingPhase entity);
+
+    @Mapping(target = "phaseType", constant = "TRAINING")
+    TrainingPhaseDTO mapToTrainingPhaseDTO(TrainingPhase entity);
+
+    @Mapping(target = "phaseType", constant = "TRAINING")
+    TrainingPhaseExportDTO mapToTrainingPhaseExportDTO(TrainingPhase entity);
+
+    @Mapping(target = "phaseType", constant = "TRAINING")
+    TrainingPhaseArchiveDTO mapToTrainingPhaseArchiveDTO(TrainingPhase entity);
+
+    // ABSTRACT
+    List<AbstractPhaseDTO> mapToListDTO(Collection<AbstractPhase> entities);
+
+    default AbstractPhaseDTO mapToDTO(AbstractPhase entity) {
+        AbstractPhaseDTO abstractPhaseDTO;
+        if (entity instanceof TrainingPhase) {
+            abstractPhaseDTO = mapToTrainingPhaseDTO((TrainingPhase) entity);
+        } else if (entity instanceof InfoPhase) {
+            abstractPhaseDTO = mapToInfoPhaseDTO((InfoPhase) entity);
+        } else if (entity instanceof QuestionnairePhase) {
+            abstractPhaseDTO = mapToQuestionnairePhaseDTO((QuestionnairePhase) entity);
+        } else {
+            throw new InternalServerErrorException("Phase with id: " + entity.getId() + " in given training definition with id: " + entity.getTrainingDefinition().getId() +
+                    " is not instance of questionnaire, training or info phase.");
+        }
+        return abstractPhaseDTO;
+    }
+
+    default AbstractPhaseArchiveDTO mapToArchiveDTO(AbstractPhase entity) {
+        AbstractPhaseArchiveDTO abstractPhaseArchiveDTO;
+        if (entity instanceof TrainingPhase) {
+            abstractPhaseArchiveDTO = mapToTrainingPhaseArchiveDTO((TrainingPhase) entity);
+        } else if (entity instanceof InfoPhase) {
+            abstractPhaseArchiveDTO = mapToInfoPhaseArchiveDTO((InfoPhase) entity);
+        } else if (entity instanceof QuestionnairePhase) {
+            abstractPhaseArchiveDTO = mapToQuestionnairePhaseArchiveDTO((QuestionnairePhase) entity);
+        } else {
+            throw new InternalServerErrorException("Phase with id: " + entity.getId() + " is not instance of questionnaire, training or info phase.");
+        }
+        return abstractPhaseArchiveDTO;
+    }
+
+    default AbstractPhaseExportDTO mapToExportDTO(AbstractPhase entity) {
+        AbstractPhaseExportDTO abstractPhaseExportDTO;
+        if (entity instanceof TrainingPhase) {
+            abstractPhaseExportDTO = mapToTrainingPhaseExportDTO((TrainingPhase) entity);
+        } else if (entity instanceof InfoPhase) {
+            abstractPhaseExportDTO = mapToInfoPhaseExportDTO((InfoPhase) entity);
+        } else if (entity instanceof QuestionnairePhase) {
+            abstractPhaseExportDTO = mapToQuestionnairePhaseExportDTO((QuestionnairePhase) entity);
+        } else {
+            throw new InternalServerErrorException("Phase with id: " + entity.getId() + " is not instance of questionnaire, training or info phase.");
+        }
+        return abstractPhaseExportDTO;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b18a831e1cf322284b68b7a41a2860b57f9cc5bc
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionMapper.java
@@ -0,0 +1,63 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionChoice;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire.QuestionArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire.QuestionChoiceArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire.QuestionChoiceExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire.QuestionExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionChoiceImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionChoiceDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The HintMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type HintMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface QuestionMapper extends ParentMapper {
+    //    QUESTIONS
+    Question mapToEntity(QuestionDTO dto);
+
+    Question mapToEntity(QuestionImportDTO dto);
+
+    QuestionArchiveDTO mapToQuestionArchiveDTO(Question entity);
+
+    QuestionExportDTO mapToQuestionExportDTO(Question entity);
+
+    QuestionDTO mapToQuestionDTO(Question entity);
+
+    List<Question> mapToList(Collection<QuestionDTO> dtos);
+
+    List<QuestionDTO> mapToListDTO(Collection<Question> entities);
+
+    Set<Question> mapToSet(Collection<QuestionDTO> dtos);
+
+    Set<QuestionDTO> mapToSetDTO(Collection<Question> entities);
+
+    //    QUESTION CHOICES
+    QuestionChoice mapToEntity(QuestionChoiceDTO dto);
+
+    QuestionChoice mapToEntity(QuestionChoiceImportDTO dto);
+
+    QuestionChoiceArchiveDTO mapToQuestionArchiveDTO(QuestionChoice entity);
+
+    QuestionChoiceExportDTO mapToQuestionExportDTO(QuestionChoice entity);
+
+    QuestionChoiceDTO mapToQuestionDTO(QuestionChoice entity);
+
+    List<QuestionChoice> mapChoicesToList(Collection<QuestionChoiceDTO> dtos);
+
+    List<QuestionDTO> mapToChoicesListDTO(Collection<QuestionChoice> entities);
+
+    Set<QuestionChoice> mapChoicesToSet(Collection<QuestionChoiceDTO> dtos);
+
+    Set<QuestionDTO> mapToChoicesSetDTO(Collection<QuestionChoice> entities);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionPhaseRelationMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionPhaseRelationMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a733bb81c86cf5e24963af274751fa4033f63ec
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/QuestionPhaseRelationMapper.java
@@ -0,0 +1,66 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.questionnaire.QuestionPhaseRelationArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.questionnaire.QuestionPhaseRelationExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.questionnaire.QuestionPhaseRelationImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionPhaseRelationDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The DecisionMatrixMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type DecisionMatrixRow and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+        unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface QuestionPhaseRelationMapper extends ParentMapper {
+    // INFO PHASE
+    QuestionPhaseRelation mapToEntity(QuestionPhaseRelationImportDTO dto);
+
+    QuestionPhaseRelation mapToEntity(QuestionPhaseRelationDTO dto);
+
+    @Mapping(target = "phaseId", source = "relatedTrainingPhase")
+    @Mapping(target = "questionIds", source = "questions")
+    QuestionPhaseRelationDTO mapToQuestionPhaseRelationDTO(QuestionPhaseRelation entity);
+
+    @Mapping(target = "phaseId", source = "relatedTrainingPhase")
+    @Mapping(target = "questionIds", source = "questions")
+    QuestionPhaseRelationExportDTO mapToQuestionPhaseRelationExportDTO(QuestionPhaseRelation entity);
+
+    @Mapping(target = "phaseId", source = "relatedTrainingPhase")
+    @Mapping(target = "questionIds", source = "questions")
+    QuestionPhaseRelationArchiveDTO mapToQuestionPhaseRelationArchiveDTO(QuestionPhaseRelation entity);
+
+    List<QuestionPhaseRelation> mapToList(Collection<QuestionPhaseRelationDTO> dtos);
+
+    List<QuestionPhaseRelationDTO> mapToListDTO(Collection<QuestionPhaseRelation> entities);
+
+    Set<QuestionPhaseRelation> mapToSet(Collection<QuestionPhaseRelationDTO> dtos);
+
+    Set<QuestionPhaseRelationDTO> mapToSetDTO(Collection<QuestionPhaseRelation> entities);
+
+    default Long mapTrainingPhaseId(TrainingPhase trainingPhase) {
+        if (trainingPhase == null) {
+            return null;
+        } else {
+            return trainingPhase.getId();
+        }
+    }
+
+    default Long mapQuestionId(Question question) {
+        if (question == null) {
+            return null;
+        } else {
+            return question.getId();
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TaskMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TaskMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..c8719d2dcf24b2343f58d46362293948d1729ef6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TaskMapper.java
@@ -0,0 +1,45 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.Task;
+import cz.muni.ics.kypo.training.adaptive.dto.archive.phases.training.TaskArchiveDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.phases.training.TaskExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.imports.phases.training.TaskImportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskCopyDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.training.TaskUpdateDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The HintMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type HintMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface TaskMapper extends ParentMapper {
+    Task mapToEntity(TaskDTO dto);
+
+    Task mapToEntity(TaskCopyDTO dto);
+
+    Task mapToEntity(TaskUpdateDTO dto);
+
+    Task mapToEntity(TaskImportDTO dto);
+
+    TaskArchiveDTO mapToTaskArchiveDTO(Task entity);
+
+    TaskExportDTO mapToTaskExportDTO(Task entity);
+
+    TaskDTO mapToTaskDTO(Task entity);
+
+    List<Task> mapToList(Collection<TaskDTO> dtos);
+
+    List<TaskDTO> mapToListDTO(Collection<Task> entities);
+
+    Set<Task> mapToSet(Collection<TaskDTO> dtos);
+
+    Set<TaskDTO> mapToSetDTO(Collection<Task> entities);
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingDefinitionMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingDefinitionMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..14a294526cdbb0031b364977963ace3926bfa7a5
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingDefinitionMapper.java
@@ -0,0 +1,72 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingdefinition.*;
+import org.mapstruct.Mapper;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.ReportingPolicy;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+
+import java.util.*;
+
+/**
+ * The TrainingDefinitionMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type TrainingDefinitionMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring",
+        uses = {UserRefMapper.class},
+        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface TrainingDefinitionMapper extends ParentMapper {
+
+    TrainingDefinition mapToEntity(TrainingDefinitionByIdDTO dto);
+
+    TrainingDefinitionByIdDTO mapToDTOById(TrainingDefinition entity);
+
+    TrainingDefinitionDTO mapToDTO(TrainingDefinition entity);
+
+    TrainingDefinitionInfoDTO mapToInfoDTO(TrainingDefinition entity);
+
+    TrainingDefinition mapCreateToEntity(TrainingDefinitionCreateDTO dto);
+
+    TrainingDefinition mapUpdateToEntity(TrainingDefinitionUpdateDTO dto);
+
+    List<TrainingDefinition> mapToList(Collection<TrainingDefinitionByIdDTO> dtos);
+
+    List<TrainingDefinitionByIdDTO> mapToListDTO(Collection<TrainingDefinition> entities);
+
+    Set<TrainingDefinition> mapToSet(Collection<TrainingDefinitionByIdDTO> dtos);
+
+    Set<TrainingDefinitionByIdDTO> mapToSetDTO(Collection<TrainingDefinition> entities);
+
+    default Optional<TrainingDefinition> mapToOptional(TrainingDefinitionByIdDTO dto) {
+        return Optional.ofNullable(mapToEntity(dto));
+    }
+
+    default Optional<TrainingDefinitionByIdDTO> mapToOptional(TrainingDefinition entity) {
+        return Optional.ofNullable(mapToDTOById(entity));
+    }
+
+    default Page<TrainingDefinitionByIdDTO> mapToPageDTO(Page<TrainingDefinition> objects) {
+        List<TrainingDefinitionByIdDTO> mapped = mapToListDTO(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default Page<TrainingDefinition> mapToPage(Page<TrainingDefinitionByIdDTO> objects) {
+        List<TrainingDefinition> mapped = mapToList(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default PageResultResource<TrainingDefinitionDTO> mapToPageResultResource(Page<TrainingDefinition> objects) {
+        List<TrainingDefinitionDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+
+    default PageResultResource<TrainingDefinitionInfoDTO> mapToPageResultResourceInfoDTO(Page<TrainingDefinition> objects) {
+        List<TrainingDefinitionInfoDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToInfoDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingInstanceMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingInstanceMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e6524f63c1cd0372c7fe1e98cde7721ee20b33f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingInstanceMapper.java
@@ -0,0 +1,71 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.traininginstance.*;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+
+import java.util.*;
+
+/**
+ * The TrainingInstanceMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type TrainingInstanceMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", uses = {TrainingDefinitionMapper.class, UserRefMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface TrainingInstanceMapper extends ParentMapper {
+    TrainingInstance mapToEntity(TrainingInstanceDTO dto);
+
+    TrainingInstance mapUpdateToEntity(TrainingInstanceUpdateDTO dto);
+
+    TrainingInstance mapCreateToEntity(TrainingInstanceCreateDTO dto);
+
+    TrainingInstance mapPartialUpdateToEntity(TrainingInstanceAssignPoolIdDTO dto);
+
+    TrainingInstanceBasicInfoDTO mapEntityToTIBasicInfo(TrainingInstance dto);
+
+    TrainingInstanceDTO mapToDTO(TrainingInstance entity);
+
+    TrainingInstanceFindAllResponseDTO mapToFindAllViewDTO(TrainingInstance entity);
+
+    List<TrainingInstance> mapToList(Collection<TrainingInstanceDTO> dtos);
+
+    List<TrainingInstanceDTO> mapToListDTO(Collection<TrainingInstance> entities);
+
+    Set<TrainingInstance> mapToSet(Collection<TrainingInstanceDTO> dtos);
+
+    Set<TrainingInstanceDTO> mapToSetDTO(Collection<TrainingInstance> entities);
+
+    default Optional<TrainingInstance> mapToOptional(TrainingInstanceDTO dto) {
+        return Optional.ofNullable(mapToEntity(dto));
+    }
+
+    default Optional<TrainingInstanceDTO> mapToOptional(TrainingInstance entity) {
+        return Optional.ofNullable(mapToDTO(entity));
+    }
+
+    default Page<TrainingInstanceDTO> mapToPageDTO(Page<TrainingInstance> objects) {
+        List<TrainingInstanceDTO> mapped = mapToListDTO(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default Page<TrainingInstance> mapToPage(Page<TrainingInstanceDTO> objects) {
+        List<TrainingInstance> mapped = mapToList(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default PageResultResource<TrainingInstanceDTO> mapToPageResultResource(Page<TrainingInstance> objects) {
+        List<TrainingInstanceDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+
+    default PageResultResource<TrainingInstanceFindAllResponseDTO> mapToPageResultResourceBasicView(Page<TrainingInstance> objects) {
+        List<TrainingInstanceFindAllResponseDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToFindAllViewDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingRunMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingRunMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f00d16b7d4e5683c8100425aa0ccd0a4f6ba1c3
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/TrainingRunMapper.java
@@ -0,0 +1,64 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.AccessedTrainingRunDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunByIdDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.trainingrun.TrainingRunDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+
+import java.util.*;
+
+/**
+ * The TrainingRunMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type TrainingRunMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface TrainingRunMapper extends ParentMapper {
+    TrainingRun mapToEntity(TrainingRunDTO dto);
+
+    TrainingRunDTO mapToDTO(TrainingRun entity);
+
+    TrainingRunByIdDTO mapToFindByIdDTO(TrainingRun entity);
+
+    List<TrainingRun> mapToList(Collection<TrainingRunDTO> dtos);
+
+    List<TrainingRunDTO> mapToListDTO(Collection<TrainingRun> entities);
+
+    Set<TrainingRun> mapToSet(Collection<TrainingRunDTO> dtos);
+
+    Set<TrainingRunDTO> mapToSetDTO(Collection<TrainingRun> entities);
+
+    default Optional<TrainingRun> mapToOptional(TrainingRunDTO dto) {
+        return Optional.ofNullable(mapToEntity(dto));
+    }
+
+    default Optional<TrainingRunDTO> mapToOptional(TrainingRun entity) {
+        return Optional.ofNullable(mapToDTO(entity));
+    }
+
+    default Page<TrainingRunDTO> mapToPageDTO(Page<TrainingRun> objects) {
+        List<TrainingRunDTO> mapped = mapToListDTO(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default Page<TrainingRun> mapToPage(Page<TrainingRunDTO> objects) {
+        List<TrainingRun> mapped = mapToList(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default PageResultResource<TrainingRunDTO> mapToPageResultResource(Page<TrainingRun> objects) {
+        List<TrainingRunDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+
+    default PageResultResource<AccessedTrainingRunDTO> mapToPageResultResourceAccessed(Page<AccessedTrainingRunDTO> objects) {
+        List<AccessedTrainingRunDTO> mapped = new ArrayList<>();
+        objects.forEach(mapped::add);
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/UserRefMapper.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/UserRefMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd878297b37de9791f44236058f1521f7ac120c3
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/mapping/mapstruct/UserRefMapper.java
@@ -0,0 +1,58 @@
+package cz.muni.ics.kypo.training.adaptive.mapping.mapstruct;
+
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.export.UserRefExportDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+
+import java.util.*;
+
+/**
+ * The UserRefMapper is an utility class to map items into data transfer objects. It provides the implementation of mappings between Java bean type UserRefMapper and
+ * DTOs classes. Code is generated during compile time.
+ */
+@Mapper(componentModel = "spring", uses = {TrainingInstanceMapper.class}, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface UserRefMapper extends ParentMapper {
+
+    UserRef mapToEntity(UserRefDTO dto);
+
+    UserRefDTO mapToDTO(UserRef entity);
+
+    List<UserRef> mapToList(Collection<UserRefDTO> dtos);
+
+    List<UserRefDTO> mapToListDTO(Collection<UserRef> entities);
+
+    Set<UserRef> mapToSet(Collection<UserRefDTO> dtos);
+
+    Set<UserRefDTO> mapToSetDTO(Collection<UserRef> entities);
+
+    List<UserRefExportDTO> mapUserRefExportDTOToUserRefDTO(Collection<UserRefDTO> userRefDTOs);
+
+    default Optional<UserRef> mapToOptional(UserRefDTO dto) {
+        return Optional.ofNullable(mapToEntity(dto));
+    }
+
+    default Optional<UserRefDTO> mapToOptional(UserRef entity) {
+        return Optional.ofNullable(mapToDTO(entity));
+    }
+
+    default Page<UserRefDTO> mapToPageDTO(Page<UserRef> objects) {
+        List<UserRefDTO> mapped = mapToListDTO(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default Page<UserRef> mapToPage(Page<UserRefDTO> objects) {
+        List<UserRef> mapped = mapToList(objects.getContent());
+        return new PageImpl<>(mapped, objects.getPageable(), mapped.size());
+    }
+
+    default PageResultResource<UserRefDTO> mapToPageResultResource(Page<UserRef> objects) {
+        List<UserRefDTO> mapped = new ArrayList<>();
+        objects.forEach(object -> mapped.add(mapToDTO(object)));
+        return new PageResultResource<>(mapped, createPagination(objects));
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AccessTokenRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AccessTokenRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..3be7e0d766c8cbf6f2775ecc7f798eb815be04a9
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AccessTokenRepository.java
@@ -0,0 +1,24 @@
+package cz.muni.ics.kypo.training.adaptive.repository;
+
+import cz.muni.ics.kypo.training.adaptive.domain.AccessToken;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+/**
+ * The JPA repository interface to manage {@link AccessToken} instances.
+ */
+@Repository
+public interface AccessTokenRepository extends JpaRepository<AccessToken, Long>, QuerydslPredicateExecutor<AccessToken> {
+
+    /**
+     * Find access token entity by its token string.
+     *
+     * @param accessToken the token string
+     * @return the {@link AccessToken} with corresponding token string
+     */
+    Optional<AccessToken> findOneByAccessToken(@Param("accessToken") String accessToken);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TRAcquisitionLockRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TRAcquisitionLockRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..97f8a61bc7de4b1cf8f2089d58ad4e86692da9e0
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TRAcquisitionLockRepository.java
@@ -0,0 +1,26 @@
+package cz.muni.ics.kypo.training.adaptive.repository;
+
+import cz.muni.ics.kypo.training.adaptive.domain.TRAcquisitionLock;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+/**
+ * The JPA repository interface to manage {@link TRAcquisitionLock} instances.
+ */
+@Repository
+public interface TRAcquisitionLockRepository extends JpaRepository<TRAcquisitionLock, Long>,
+        QuerydslPredicateExecutor<TRAcquisitionLockRepository> {
+
+    /**
+     * Deletes Acquisition Lock by participant and training instance
+     *
+     * @param participantRefId   - id of participant associated with lock
+     * @param trainingInstanceId - id of training instance associated with lock
+     */
+    @Modifying
+    void deleteByParticipantRefIdAndTrainingInstanceId(@Param("participantRefId") Long participantRefId,
+                                                       @Param("trainingInstanceId") Long trainingInstanceId);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/UserRefRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/UserRefRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8fe2b8b33c626fae2fbf66be712b79ec22c0adf
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/UserRefRepository.java
@@ -0,0 +1,42 @@
+package cz.muni.ics.kypo.training.adaptive.repository;
+
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * The JPA repository interface to manage {@link UserRef} instances.
+ */
+@Repository
+public interface UserRefRepository extends JpaRepository<UserRef, Long>, QuerydslPredicateExecutor<UserRef> {
+
+    /**
+     * Find all users by userRefIds.
+     *
+     * @param userRefId the user ref id
+     * @return the set of {@link UserRef}
+     */
+    Set<UserRef> findUsers(@Param("userRefId") Set<Long> userRefId);
+
+    /**
+     * Find user by user ref id.
+     *
+     * @param userRefId the user id
+     * @return the {@link UserRef}
+     */
+    Optional<UserRef> findUserByUserRefId(@Param("userRefId") Long userRefId);
+
+    /**
+     * Find all participants of given training instance.
+     *
+     * @param trainingInstanceId id of the training instance
+     * @return the {@link UserRef}
+     */
+    Set<Long> findParticipantsRefByTrainingInstanceId(@Param("trainingInstanceId") Long trainingInstanceId);
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AbstractPhaseRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/AbstractPhaseRepository.java
similarity index 58%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AbstractPhaseRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/AbstractPhaseRepository.java
index b3613abbebfdfe90c156ea7f0e36178668fef5d7..9aca65256ac30fc93ee7460dfc6308cf25432c04 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/AbstractPhaseRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/AbstractPhaseRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
@@ -8,25 +8,26 @@ import org.springframework.data.repository.query.Param;
 import org.springframework.stereotype.Repository;
 
 import java.util.List;
+import java.util.Optional;
 
 @Repository
 public interface AbstractPhaseRepository extends JpaRepository<AbstractPhase, Long> {
 
     List<AbstractPhase> findAllByTrainingDefinitionIdOrderByOrder(long trainingDefinitionId);
 
-    @Query("SELECT COALESCE(MAX(l.order), -1) FROM AbstractPhase l WHERE l.trainingDefinitionId = :trainingDefinitionId")
+    @Query("SELECT COALESCE(MAX(l.order), -1) FROM AbstractPhase l WHERE l.trainingDefinition.id = :trainingDefinitionId")
     Integer getCurrentMaxOrder(@Param("trainingDefinitionId") Long trainingDefinitionId);
 
     @Modifying
     @Query("UPDATE AbstractPhase l SET l.order = l.order - 1 " +
-            "WHERE l.trainingDefinitionId = :trainingDefinitionId " +
+            "WHERE l.trainingDefinition.id = :trainingDefinitionId " +
             "AND l.order > :order ")
     void decreaseOrderAfterPhaseWasDeleted(@Param("trainingDefinitionId") Long trainingDefinitionId,
                                            @Param("order") int order);
 
     @Modifying
     @Query("UPDATE AbstractPhase l SET l.order = l.order + 1 " +
-            "WHERE l.trainingDefinitionId = :trainingDefinitionId " +
+            "WHERE l.trainingDefinition.id = :trainingDefinitionId " +
             "AND l.order >= :lowerBound " +
             "AND l.order < :upperBound ")
     void increaseOrderOfPhasesOnInterval(@Param("trainingDefinitionId") Long trainingDefinitionId,
@@ -35,11 +36,23 @@ public interface AbstractPhaseRepository extends JpaRepository<AbstractPhase, Lo
 
     @Modifying
     @Query("UPDATE AbstractPhase l SET l.order = l.order - 1 " +
-            "WHERE l.trainingDefinitionId = :trainingDefinitionId " +
+            "WHERE l.trainingDefinition.id = :trainingDefinitionId " +
             "AND l.order > :lowerBound " +
             "AND l.order <= :upperBound ")
     void decreaseOrderOfPhasesOnInterval(@Param("trainingDefinitionId") Long trainingDefinitionId,
                                          @Param("lowerBound") int lowerBound,
                                          @Param("upperBound") int upperBound);
 
+    @Query("SELECT ap FROM AbstractPhase ap WHERE ap.trainingDefinition.id = :trainingDefinitionId AND ap.id = :phaseId")
+    Optional<AbstractPhase> findPhaseInDefinition(@Param("trainingDefinitionId") Long trainingDefinitionId,
+                                                  @Param("phaseId") Long phaseId);
+
+    @Query("SELECT ap FROM AbstractPhase ap " +
+            "JOIN FETCH ap.trainingDefinition td " +
+            "JOIN FETCH td.authors " +
+            "WHERE ap.id = :phaseId")
+    Optional<AbstractPhase> findByIdWithDefinition(@Param("phaseId") Long phaseId);
+
+    @Query("SELECT ap FROM AbstractPhase ap WHERE ap.trainingDefinition.id = :trainingDefinitionId AND ap.order = 0")
+    Optional<AbstractPhase> findFirstPhaseOfTrainingDefinition(@Param("trainingDefinitionId") Long trainingDefinitionId);
 }
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/InfoPhaseRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/InfoPhaseRepository.java
similarity index 61%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/InfoPhaseRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/InfoPhaseRepository.java
index bafec1c8f685565b1f6461b512551c42e49f99ea..fa0b5264c439e852f1e03525b33b693eb1d2b38d 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/InfoPhaseRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/InfoPhaseRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionPhaseRelationRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionPhaseRelationRepository.java
similarity index 60%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionPhaseRelationRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionPhaseRelationRepository.java
index a79d1229070bca9ac526af0b62e2af5476bdd917..e186d22fce5a3a1b569e9f3abe4eb304354a114e 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionPhaseRelationRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionPhaseRelationRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionRepository.java
similarity index 59%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionRepository.java
index f20e7080473e2ebcca5bfc68f8714ec557b1bc4f..2e2ceb2bb1ad1b4501b14b4966c87323a6fc30ed 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionnairePhaseRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionnairePhaseRepository.java
similarity index 62%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionnairePhaseRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionnairePhaseRepository.java
index 9a0362d4804a117142e94a044de8288a3b168e28..df0d6d902ef732a948392f902510a55388bd6126 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/QuestionnairePhaseRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/QuestionnairePhaseRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TaskRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TaskRepository.java
similarity index 93%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TaskRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TaskRepository.java
index a5bf4e245e944bef02c83be0d7a3cfbf2f6f2f7a..426f0fa15cb4eed73facb13ba41b1f70b3d07205 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TaskRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TaskRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.Task;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.Task;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TrainingPhaseRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TrainingPhaseRepository.java
similarity index 77%
rename from src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TrainingPhaseRepository.java
rename to src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TrainingPhaseRepository.java
index d09677540354b053fb632cd42e9b8bf3cf8d0a57..e0b2515508a3d00c673ef6b6efc30798424239a9 100644
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/TrainingPhaseRepository.java
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/phases/TrainingPhaseRepository.java
@@ -1,6 +1,6 @@
-package cz.muni.ics.kypo.training.adaptive.repository;
+package cz.muni.ics.kypo.training.adaptive.repository.phases;
 
-import cz.muni.ics.kypo.training.adaptive.domain.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
@@ -11,7 +11,7 @@ import java.util.List;
 @Repository
 public interface TrainingPhaseRepository extends JpaRepository<TrainingPhase, Long> {
 
-    @Query("SELECT COUNT(p.id) FROM TrainingPhase p WHERE p.trainingDefinitionId = :trainingDefinitionId")
+    @Query("SELECT COUNT(p.id) FROM TrainingPhase p WHERE p.trainingDefinition.id = :trainingDefinitionId")
     int getNumberOfExistingPhases(@Param("trainingDefinitionId") Long trainingDefinitionId);
 
     List<TrainingPhase> findAllByTrainingDefinitionIdOrderByOrder(Long trainingDefinitionId);
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c3ec2d82c1c4d205b361da0a5263aff96ccb51e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepository.java
@@ -0,0 +1,92 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.StringPath;
+import cz.muni.ics.kypo.training.adaptive.domain.training.QTrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
+import org.springframework.data.querydsl.binding.QuerydslBindings;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collection;
+import java.util.Optional;
+
+/**
+ * The JPA repository interface to manage {@link TrainingDefinition} instances.
+ */
+@Repository
+public interface TrainingDefinitionRepository
+        extends JpaRepository<TrainingDefinition, Long>, TrainingDefinitionRepositoryCustom, QuerydslPredicateExecutor<TrainingDefinition>, QuerydslBinderCustomizer<QTrainingDefinition> {
+
+    /**
+     * That method is used to make the query dsl string values case insensitive and also it supports partial matches in the database.
+     *
+     * @param querydslBindings
+     * @param qTrainingDefinition
+     */
+    @Override
+    default void customize(QuerydslBindings querydslBindings, QTrainingDefinition qTrainingDefinition) {
+        querydslBindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
+            BooleanBuilder predicate = new BooleanBuilder();
+            values.forEach(value -> predicate.and(path.containsIgnoreCase(value)));
+            return Optional.ofNullable(predicate);
+        });
+    }
+
+    /**
+     * Find all training definitions
+     *
+     * @param predicate the predicate
+     * @param pageable  the pageable
+     * @return page of all {@link TrainingDefinition}
+     */
+    Page<TrainingDefinition> findAll(Predicate predicate, Pageable pageable);
+
+    /**
+     * Find all training definitions
+     *
+     * @param pageable the pageable
+     * @return page of all {@link TrainingDefinition}
+     */
+    Page<TrainingDefinition> findAll(Pageable pageable);
+
+    /**
+     * Find all training definitions
+     *
+     * @param state    the state of training definition
+     * @param pageable the pageable
+     * @return page of all {@link TrainingDefinition}
+     */
+    Page<TrainingDefinition> findAllForOrganizers(@Param("state") TDState state, Pageable pageable);
+
+    /**
+     * Find all for designers and organizers unreleased page.
+     *
+     * @param userRefId the user ref id
+     * @param pageable  the pageable
+     * @return the page
+     */
+    Page<TrainingDefinition> findAllForDesigner(@Param("userRefId") Long userRefId, Pageable pageable);
+
+    /**
+     * Find training definition by id
+     *
+     * @param id the id of training definition
+     * @return {@link TrainingDefinition}
+     */
+    Optional<TrainingDefinition> findById(Long id);
+
+    @Query("SELECT td FROM AbstractPhase ap INNER JOIN ap.trainingDefinition td WHERE ap.id = :phaseId")
+    Optional<TrainingDefinition> findByPhaseId(@Param("phaseId") Long phaseId);
+
+    @Query("SELECT td FROM Task t INNER JOIN t.trainingPhase tp INNER JOIN tp.trainingDefinition td WHERE t.id = :taskId")
+    Optional<TrainingDefinition> findByTaskId(@Param("taskId") Long taskId);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryCustom.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryCustom.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb1f03dd206aebcce7395fa1f7d932e687daea73
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryCustom.java
@@ -0,0 +1,23 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * The interface Training definition repository custom.
+ */
+public interface TrainingDefinitionRepositoryCustom {
+
+    /**
+     * Find all training definitions.
+     *
+     * @param predicate      the predicate
+     * @param pageable       the pageable
+     * @param loggedInUserId the logged in user id
+     * @return the page of training definitions
+     */
+    Page<TrainingDefinition> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId);
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryImpl.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..f94d313b1e3e3f8f3f4a7c5e09fbc8ca9ef798eb
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingDefinitionRepositoryImpl.java
@@ -0,0 +1,63 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.types.Predicate;
+import com.querydsl.jpa.JPQLQuery;
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import cz.muni.ics.kypo.training.adaptive.domain.QUserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.training.QTrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.Objects;
+
+/**
+ * The type Training definition repository.
+ */
+@Repository
+public class TrainingDefinitionRepositoryImpl extends QuerydslRepositorySupport implements TrainingDefinitionRepositoryCustom {
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    /**
+     * Instantiates a new Training definition repository.
+     */
+    public TrainingDefinitionRepositoryImpl() {
+        super(TrainingDefinition.class);
+    }
+
+    @Override
+    @Transactional
+    public Page<TrainingDefinition> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId) {
+        Objects.requireNonNull(loggedInUserId, "Input logged in user ID must not be null.");
+        QTrainingDefinition trainingDefinition = QTrainingDefinition.trainingDefinition;
+        QUserRef authors = new QUserRef("authors");
+        QUserRef organizers = new QUserRef("organizers");
+
+        JPQLQuery<TrainingDefinition> query = new JPAQueryFactory(entityManager).selectFrom(trainingDefinition).distinct()
+                .leftJoin(trainingDefinition.authors, authors)
+                .where(authors.userRefId.eq(loggedInUserId).or(organizers.userRefId.eq(loggedInUserId)));
+
+        if (predicate != null) {
+            query.where(predicate);
+        }
+        return getPage(query, pageable);
+    }
+
+    private <T> Page getPage(JPQLQuery<T> query, Pageable pageable) {
+        if (pageable == null) {
+            pageable = PageRequest.of(0, 20);
+        }
+        query = getQuerydsl().applyPagination(pageable, query);
+        long count = query.fetchCount();
+        return new PageImpl<>(query.fetch(), pageable, count);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9f7073e9876efbafe71c8d1b6ef006b89e71d9e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepository.java
@@ -0,0 +1,113 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.StringPath;
+import cz.muni.ics.kypo.training.adaptive.domain.training.QTrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
+import org.springframework.data.querydsl.binding.QuerydslBindings;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * The JPA repository interface to manage {@link TrainingInstance} instances.
+ */
+@Repository
+public interface TrainingInstanceRepository extends JpaRepository<TrainingInstance, Long>, TrainingInstanceRepositoryCustom,
+        QuerydslPredicateExecutor<TrainingInstance>, QuerydslBinderCustomizer<QTrainingInstance> {
+
+    /**
+     * That method is used to make the query dsl string values case insensitive and also it supports partial matches in the database.
+     *
+     * @param querydslBindings
+     * @param qTrainingInstance
+     */
+    @Override
+    default void customize(QuerydslBindings querydslBindings, QTrainingInstance qTrainingInstance) {
+        querydslBindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
+            BooleanBuilder predicate = new BooleanBuilder();
+            values.forEach(value -> predicate.and(path.containsIgnoreCase(value)));
+            return Optional.ofNullable(predicate);
+        });
+    }
+
+    /**
+     * Find all training instances by id of associated training definition.
+     *
+     * @param trainingDefId the training def id
+     * @return the list of {@link TrainingInstance}s associated to {@link TrainingDefinition}
+     */
+    List<TrainingInstance> findAllByTrainingDefinitionId(@Param("trainingDefId") Long trainingDefId);
+
+    /**
+     * Find all training instances
+     *
+     * @param predicate the predicate
+     * @param pageable  the pageable
+     * @return page of all {@link TrainingInstance}
+     */
+    @EntityGraph(
+            value = "TrainingInstance.findAllAuthorsOrganizersBetaTestingGroupBetaTestingGroupOrganizers",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Page<TrainingInstance> findAll(Predicate predicate, Pageable pageable);
+
+    /**
+     * Find training instance by id
+     *
+     * @param id id of training instance
+     * @return {@link TrainingInstance}
+     */
+    @EntityGraph(
+            value = "TrainingInstance.findByIdAuthorsOrganizers",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Optional<TrainingInstance> findById(Long id);
+
+    /**
+     * Find training instance with start time in the past, end time in the future and by corresponding access token.
+     *
+     * @param datetime    the current time
+     * @param accessToken the access token
+     * @return {@link TrainingInstance} with start time in the past, end time in the future and by corresponding access token
+     */
+    Optional<TrainingInstance> findByStartTimeAfterAndEndTimeBeforeAndAccessToken(@Param("datetime") LocalDateTime datetime,
+                                                                                  @Param("accessToken") String accessToken);
+
+    /**
+     * Check if any training instances are associated with training definition
+     *
+     * @param trainingDefinitionId the training definition id
+     * @return True if there are any instances associated with training definition
+     */
+    boolean existsAnyForTrainingDefinition(@Param("trainingDefinitionId") Long trainingDefinitionId);
+
+    /**
+     * Find training instance by id including its associated training definition.
+     *
+     * @param instanceId the instance id
+     * @return {@link TrainingInstance} including its associated {@link TrainingDefinition}
+     */
+    Optional<TrainingInstance> findByIdIncludingDefinition(@Param("instanceId") Long instanceId);
+
+    /**
+     * Checks if training instance finished.
+     *
+     * @param instanceId  the instance id
+     * @param currentTime the current time
+     * @return true if instance is finished, false if not
+     */
+    boolean isFinished(@Param("instanceId") Long instanceId, @Param("currentTime") LocalDateTime currentTime);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryCustom.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryCustom.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ef561f392d996ed23e92d2da9f3e2b17539fd5b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryCustom.java
@@ -0,0 +1,22 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * The interface Training instance repository custom.
+ */
+public interface TrainingInstanceRepositoryCustom {
+
+    /**
+     * Find all training instances of logged in user.
+     *
+     * @param predicate      the predicate
+     * @param pageable       the pageable
+     * @param loggedInUserId the logged in user id
+     * @return the page of training instances
+     */
+    Page<TrainingInstance> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId);
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryImpl.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..eeb6fa7d8e0f72585b64e870462266acff6e3fe0
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingInstanceRepositoryImpl.java
@@ -0,0 +1,62 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.types.Predicate;
+import com.querydsl.jpa.JPQLQuery;
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import cz.muni.ics.kypo.training.adaptive.domain.QUserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.training.QTrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.Objects;
+
+/**
+ * The type Training instance repository.
+ */
+@Repository
+public class TrainingInstanceRepositoryImpl extends QuerydslRepositorySupport implements TrainingInstanceRepositoryCustom {
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    /**
+     * Instantiates a new Training instance repository.
+     */
+    public TrainingInstanceRepositoryImpl() {
+        super(TrainingInstance.class);
+    }
+
+    @Override
+    @Transactional
+    public Page<TrainingInstance> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId) {
+        Objects.requireNonNull(loggedInUserId, "Input logged in user ID must not be null.");
+        QTrainingInstance trainingInstance = QTrainingInstance.trainingInstance;
+        QUserRef organizers = new QUserRef("organizers");
+
+        JPQLQuery<TrainingInstance> query = new JPAQueryFactory(entityManager).selectFrom(trainingInstance).distinct()
+                .leftJoin(trainingInstance.organizers, organizers)
+                .where(organizers.userRefId.eq(loggedInUserId));
+
+        if (predicate != null) {
+            query.where(predicate);
+        }
+        return getPage(query, pageable);
+    }
+
+    private <T> Page getPage(JPQLQuery<T> query, Pageable pageable) {
+        if (pageable == null) {
+            pageable = PageRequest.of(0, 20);
+        }
+        query = getQuerydsl().applyPagination(pageable, query);
+        long count = query.fetchCount();
+        return new PageImpl<>(query.fetch(), pageable, count);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingRunRepository.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingRunRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..abdcc62bb4da0ff351069433d6998103cf402954
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/repository/training/TrainingRunRepository.java
@@ -0,0 +1,181 @@
+package cz.muni.ics.kypo.training.adaptive.repository.training;
+
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+import com.querydsl.core.types.dsl.StringPath;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.QTrainingRun;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
+import org.springframework.data.querydsl.binding.QuerydslBindings;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * The JPA repository interface to manage {@link TrainingRun} instances.
+ */
+@Repository
+public interface TrainingRunRepository extends JpaRepository<TrainingRun, Long>, QuerydslPredicateExecutor<TrainingRun>, QuerydslBinderCustomizer<QTrainingRun> {
+
+    /**
+     * That method is used to make the query dsl string values case insensitive and also it supports partial matches in the database.
+     *
+     * @param querydslBindings
+     * @param qTrainingRun
+     */
+    @Override
+    default void customize(QuerydslBindings querydslBindings, QTrainingRun qTrainingRun) {
+        querydslBindings.bind(String.class).all((StringPath path, Collection<? extends String> values) -> {
+            BooleanBuilder predicate = new BooleanBuilder();
+            values.forEach(value -> predicate.and(path.containsIgnoreCase(value)));
+            return Optional.ofNullable(predicate);
+        });
+    }
+
+    /**
+     * Find all training runs
+     *
+     * @param predicate the predicate
+     * @param pageable  the pageable
+     * @return page of all {@link TrainingRun}
+     */
+    @EntityGraph(
+            value = "TrainingRun.findAllParticipantRef",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Page<TrainingRun> findAll(Predicate predicate, Pageable pageable);
+
+    /**
+     * Find all training runs associated with training instance.
+     *
+     * @param trainingInstanceId the training instance id
+     * @param pageable           the pageable
+     * @return the page of all {@link TrainingRun}s associated with {@link TrainingInstance}
+     */
+    @EntityGraph(
+            value = "TrainingRun.findAllParticipantRef",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Page<TrainingRun> findAllByTrainingInstanceId(Long trainingInstanceId, Pageable pageable);
+
+    /**
+     * Find all training runs associated with training instance.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return the set of all {@link TrainingRun}s associated with {@link TrainingInstance}
+     */
+    @EntityGraph(
+            value = "TrainingRun.findAllParticipantRef",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Set<TrainingRun> findAllByTrainingInstanceId(Long trainingInstanceId);
+
+    /**
+     * Find training run by id
+     *
+     * @param id id of training run
+     * @return {@link TrainingRun}
+     */
+    @EntityGraph(
+            value = "TrainingRun.findByIdParticipantRefTrainingInstance",
+            type = EntityGraph.EntityGraphType.FETCH
+    )
+    Optional<TrainingRun> findById(Long id);
+
+    /**
+     * Find all training runs accessed by participant by their user ref id.
+     *
+     * @param userRefId the participant ref id
+     * @param pageable  the pageable
+     * @return the page of all {@link TrainingRun}s accessed by participant
+     */
+    Page<TrainingRun> findAllByParticipantRefId(@Param("userRefId") Long userRefId, Pageable pageable);
+
+    /**
+     * Find training run by id including current phase
+     *
+     * @param trainingRunId the training run id
+     * @return {@link TrainingRun} including {@link AbstractPhase}
+     */
+    Optional<TrainingRun> findByIdWithPhase(@Param("trainingRunId") Long trainingRunId);
+
+    /**
+     * Find all training runs by id of associated training definition that are accessible to participant by user ref id.
+     *
+     * @param trainingDefinitionId the training definition id
+     * @param userRefId            the participant user ref id
+     * @param pageable             the pageable
+     * @return the page of all {@link TrainingRun}s by id of associated {@link TrainingDefinition} that are accessible to participant
+     */
+    Page<TrainingRun> findAllByTrainingDefinitionIdAndParticipantUserRefId(@Param("trainingDefinitionId") Long trainingDefinitionId,
+                                                                           @Param("userRefId") Long userRefId,
+                                                                           Pageable pageable);
+
+    /**
+     * Find all active training runs by training instance id.
+     *
+     * @param trainingInstanceId the training instance id
+     * @param pageable           the pageable
+     * @return the page of all active {@link TrainingRun}s associated with given {@link TrainingInstance}
+     */
+    Page<TrainingRun> findAllActiveByTrainingInstanceId(@Param("trainingInstanceId") Long trainingInstanceId,
+                                                        Pageable pageable);
+
+    /**
+     * Find all inactive training runs by training instance id.
+     *
+     * @param trainingInstanceId the training instance id
+     * @param pageable           the pageable
+     * @return the page of all inactive {@link TrainingRun}s associated with given {@link TrainingInstance}
+     */
+    Page<TrainingRun> findAllInactiveByTrainingInstanceId(@Param("trainingInstanceId") Long trainingInstanceId,
+                                                          Pageable pageable);
+
+    /**
+     * Find all training runs associated with training definition.
+     *
+     * @param trainingDefinitionId the training definition id
+     * @param pageable             the pageable
+     * @return the page of all {@link TrainingRun}s associated with {@link TrainingDefinition}
+     */
+    Page<TrainingRun> findAllByTrainingDefinitionId(@Param("trainingDefinitionId") Long trainingDefinitionId,
+                                                    Pageable pageable);
+
+    /**
+     * Delete all training runs by training instance.
+     *
+     * @param trainingInstanceId the training instance id
+     */
+    @Modifying
+    void deleteTrainingRunsByTrainingInstance(@Param("trainingInstanceId") Long trainingInstanceId);
+
+    /**
+     * Find valid training run by user and access token.
+     *
+     * @param accessToken the access token
+     * @param userRefId   the user ref id
+     * @return the {@link TrainingRun} by user and access token
+     */
+    Optional<TrainingRun> findRunningTrainingRunOfUser(@Param("accessToken") String accessToken, @Param("userRefId") Long userRefId);
+
+    /**
+     * Exists any for training instance boolean.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return the boolean
+     */
+    boolean existsAnyForTrainingInstance(@Param("trainingInstanceId") Long trainingInstanceId);
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/ExportImportService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/ExportImportService.java
new file mode 100644
index 0000000000000000000000000000000000000000..fac9e0cb6775aff2fe6ebd76156c8b61a1042729
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/ExportImportService.java
@@ -0,0 +1,154 @@
+package cz.muni.ics.kypo.training.adaptive.service;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.*;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingRunRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The type Export import service.
+ */
+@Service
+public class ExportImportService {
+
+    private TrainingDefinitionRepository trainingDefinitionRepository;
+    private AbstractPhaseRepository abstractPhaseRepository;
+    private QuestionPhaseRelationRepository questionPhaseRelationRepository;
+    private QuestionRepository questionRepository;
+    private TrainingPhaseRepository trainingPhaseRepository;
+    private TrainingInstanceRepository trainingInstanceRepository;
+    private TrainingRunRepository trainingRunRepository;
+
+    /**
+     * Instantiates a new Export import service.
+     *
+     * @param trainingDefinitionRepository    the training definition repository
+     * @param abstractPhaseRepository         the abstract phase repository
+     * @param questionPhaseRelationRepository the questionnaire phase repository
+     * @param infoPhaseRepository             the info phase repository
+     * @param trainingPhaseRepository         the training phase repository
+     * @param trainingInstanceRepository      the training instance repository
+     * @param trainingRunRepository           the training run repository
+     */
+    @Autowired
+    public ExportImportService(TrainingDefinitionRepository trainingDefinitionRepository,
+                               AbstractPhaseRepository abstractPhaseRepository,
+                               QuestionPhaseRelationRepository questionPhaseRelationRepository,
+                               QuestionRepository questionRepository,
+                               InfoPhaseRepository infoPhaseRepository,
+                               TrainingPhaseRepository trainingPhaseRepository,
+                               TrainingInstanceRepository trainingInstanceRepository,
+                               TrainingRunRepository trainingRunRepository) {
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+        this.questionPhaseRelationRepository = questionPhaseRelationRepository;
+        this.questionRepository = questionRepository;
+        this.trainingPhaseRepository = trainingPhaseRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.trainingRunRepository = trainingRunRepository;
+    }
+
+    /**
+     * Finds training definition with given id.
+     *
+     * @param trainingDefinitionId the id of definition to be found.
+     * @return the {@link TrainingDefinition} with the given id.
+     * @throws EntityNotFoundException if training definition was not found.
+     */
+    public TrainingDefinition findById(Long trainingDefinitionId) {
+        return trainingDefinitionRepository.findById(trainingDefinitionId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinitionId.getClass(),
+                        trainingDefinitionId)));
+    }
+
+    /**
+     * Creates a phase and connects it with training definition.
+     *
+     * @param phase      the {@link AbstractPhase} to be created.
+     * @param definition the {@link TrainingDefinition} to associate phase with.
+     */
+    public TrainingPhase createTrainingPhase(TrainingPhase phase, TrainingDefinition definition) {
+        phase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(definition.getId()) + 1);
+        phase.setTrainingDefinition(definition);
+        phase.getTasks().forEach(task -> task.setTrainingPhase(phase));
+        phase.getDecisionMatrix().forEach(decisionMatrixRow -> decisionMatrixRow.setTrainingPhase(phase));
+        return abstractPhaseRepository.save(phase);
+    }
+
+    public QuestionnairePhase createQuestionnairePhase(QuestionnairePhase phase, TrainingDefinition definition) {
+        phase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(definition.getId()) + 1);
+        phase.setTrainingDefinition(definition);
+        phase.getQuestions().forEach(question -> {
+            question.setQuestionnairePhase(phase);
+            question.getChoices().forEach(questionChoice -> questionChoice.setQuestion(question));
+        });
+        return abstractPhaseRepository.save(phase);
+    }
+
+    public void createQuestionPhaseRelationPhase(QuestionPhaseRelation questionPhaseRelation,
+                                                 QuestionnairePhase questionnairePhase,
+                                                 Long relatedTrainingPhaseId,
+                                                 Set<Long> questionIds) {
+        questionPhaseRelation.setQuestionnairePhase(questionnairePhase);
+        questionPhaseRelation.setRelatedTrainingPhase(findTrainingPhaseById(relatedTrainingPhaseId));
+        questionPhaseRelation.setQuestions(new HashSet<>(questionRepository.findAllById(questionIds)));
+        questionPhaseRelationRepository.save(questionPhaseRelation);
+    }
+
+    public InfoPhase createInfoPhase(InfoPhase phase, TrainingDefinition definition) {
+        phase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(definition.getId()) + 1);
+        phase.setTrainingDefinition(definition);
+        return abstractPhaseRepository.save(phase);
+    }
+
+    /**
+     * Finds training instance with given id.
+     *
+     * @param trainingInstanceId the id of instance to be found.
+     * @return the {@link TrainingInstance} with the given id.
+     * @throws EntityNotFoundException if training instance was not found.
+     */
+    public TrainingInstance findInstanceById(Long trainingInstanceId) {
+        return trainingInstanceRepository.findById(trainingInstanceId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "id", trainingInstanceId.getClass(),
+                        trainingInstanceId)));
+    }
+
+    /**
+     * Finds training phase with given id.
+     *
+     * @param trainingPhaseId the id of the training phase to be found.
+     * @return the {@link TrainingPhase} with the given id.
+     * @throws EntityNotFoundException if training instance was not found.
+     */
+    public TrainingPhase findTrainingPhaseById(Long trainingPhaseId) {
+        return trainingPhaseRepository.findById(trainingPhaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingPhase.class, "id", trainingPhaseId.getClass(),
+                        trainingPhaseId)));
+    }
+
+    /**
+     * Finds training runs associated with training instance with given id.
+     *
+     * @param trainingInstanceId the id of instance which runs are to be found.
+     * @return the set off all {@link TrainingRun}
+     */
+    public Set<TrainingRun> findRunsByInstanceId(Long trainingInstanceId) {
+        return trainingRunRepository.findAllByTrainingInstanceId(trainingInstanceId);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/InfoPhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/InfoPhaseService.java
deleted file mode 100644
index 557d3466fb30108c1205b901dca5aac254267aa7..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/InfoPhaseService.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.service;
-
-import cz.muni.ics.kypo.training.adaptive.domain.InfoPhase;
-import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.info.InfoPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.mapper.BeanMapper;
-import cz.muni.ics.kypo.training.adaptive.repository.AbstractPhaseRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.InfoPhaseRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-@Service
-public class InfoPhaseService {
-
-    private static final Logger LOG = LoggerFactory.getLogger(InfoPhaseService.class);
-
-    private final InfoPhaseRepository infoPhaseRepository;
-    private final AbstractPhaseRepository abstractPhaseRepository;
-
-    @Autowired
-    public InfoPhaseService(InfoPhaseRepository infoPhaseRepository, AbstractPhaseRepository abstractPhaseRepository) {
-        this.infoPhaseRepository = infoPhaseRepository;
-        this.abstractPhaseRepository = abstractPhaseRepository;
-    }
-
-    public InfoPhaseDTO createDefaultInfoPhase(Long trainingDefinitionId) {
-        InfoPhase infoPhase = new InfoPhase();
-        infoPhase.setContent("Content of info phase");
-        infoPhase.setTitle("Title of info phase");
-        infoPhase.setTrainingDefinitionId(trainingDefinitionId);
-        infoPhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
-
-        InfoPhase persistedEntity = infoPhaseRepository.save(infoPhase);
-        return BeanMapper.INSTANCE.toDto(persistedEntity);
-    }
-
-    public InfoPhaseDTO updateInfoPhase(Long definitionId, Long phaseId, InfoPhaseUpdateDTO infoPhaseUpdateDto) {
-        InfoPhase infoPhaseUpdate = BeanMapper.INSTANCE.toEntity(infoPhaseUpdateDto);
-        infoPhaseUpdate.setId(phaseId);
-        InfoPhase persistedInfoPhase = infoPhaseRepository.findById(infoPhaseUpdate.getId())
-                .orElseThrow(() -> new RuntimeException("Info phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        infoPhaseUpdate.setTrainingDefinitionId(persistedInfoPhase.getTrainingDefinitionId());
-        infoPhaseUpdate.setOrder(persistedInfoPhase.getOrder());
-
-        InfoPhase savedEntity = infoPhaseRepository.save(infoPhaseUpdate);
-        return BeanMapper.INSTANCE.toDto(savedEntity);
-    }
-
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/PhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/PhaseService.java
deleted file mode 100644
index db837ce5901184f5bedffcc0b9b78d4e3124ac5a..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/PhaseService.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.service;
-
-import cz.muni.ics.kypo.training.adaptive.domain.AbstractPhase;
-import cz.muni.ics.kypo.training.adaptive.dto.AbstractPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.mapper.BeanMapper;
-import cz.muni.ics.kypo.training.adaptive.repository.AbstractPhaseRepository;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-
-@Service
-@Transactional
-public class PhaseService {
-
-    private final AbstractPhaseRepository abstractPhaseRepository;
-    private final TrainingPhaseService trainingPhaseService;
-
-    @Autowired
-    public PhaseService(AbstractPhaseRepository abstractPhaseRepository, TrainingPhaseService trainingPhaseService) {
-        this.abstractPhaseRepository = abstractPhaseRepository;
-        this.trainingPhaseService = trainingPhaseService;
-    }
-
-    public void deletePhase(Long definitionId, Long phaseId) {
-        AbstractPhase phase = abstractPhaseRepository.findById(phaseId)
-                .orElseThrow(() -> new RuntimeException("Phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        int phaseOrder = phase.getOrder();
-        abstractPhaseRepository.decreaseOrderAfterPhaseWasDeleted(definitionId, phaseOrder);
-
-        abstractPhaseRepository.delete(phase);
-    }
-
-    @Transactional(readOnly = true)
-    public AbstractPhaseDTO getPhase(Long definitionId, Long phaseId) {
-        AbstractPhase phase = abstractPhaseRepository.findById(phaseId)
-                .orElseThrow(() -> new RuntimeException("Phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        return BeanMapper.INSTANCE.toDto(phase);
-    }
-
-    @Transactional(readOnly = true)
-    public List<AbstractPhaseDTO> getPhases(Long trainingDefinitionId) {
-        List<AbstractPhase> phases = abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingDefinitionId);
-
-        return BeanMapper.INSTANCE.toDtoList(phases);
-    }
-
-    public void movePhaseToSpecifiedOrder(Long phaseIdFrom, int newPosition) {
-        AbstractPhase phaseFrom = abstractPhaseRepository.findById(phaseIdFrom)
-                .orElseThrow(() -> new RuntimeException("Phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        int fromOrder = phaseFrom.getOrder();
-        if (fromOrder < newPosition) {
-            abstractPhaseRepository.decreaseOrderOfPhasesOnInterval(phaseFrom.getTrainingDefinitionId(), fromOrder, newPosition);
-        } else if (fromOrder > newPosition) {
-            abstractPhaseRepository.increaseOrderOfPhasesOnInterval(phaseFrom.getTrainingDefinitionId(), newPosition, fromOrder);
-        } else {
-            // nothing should be changed, no further actions needed
-            return;
-        }
-
-        phaseFrom.setOrder(newPosition);
-        abstractPhaseRepository.save(phaseFrom);
-        trainingPhaseService.alignDecisionMatrixForPhasesInTrainingDefinition(phaseFrom.getTrainingDefinitionId());
-    }
-
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/QuestionnairePhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/QuestionnairePhaseService.java
deleted file mode 100644
index 04ed529a647a0ca54fcc63b4169d03aabd5e11b9..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/QuestionnairePhaseService.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.service;
-
-import cz.muni.ics.kypo.training.adaptive.domain.Question;
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionPhaseRelation;
-import cz.muni.ics.kypo.training.adaptive.domain.QuestionnairePhase;
-import cz.muni.ics.kypo.training.adaptive.domain.TrainingPhase;
-import cz.muni.ics.kypo.training.adaptive.dto.PhaseCreateDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionPhaseRelationDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnairePhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.questionnaire.QuestionnaireUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.enums.PhaseTypeCreate;
-import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
-import cz.muni.ics.kypo.training.adaptive.mapper.BeanMapper;
-import cz.muni.ics.kypo.training.adaptive.repository.AbstractPhaseRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.QuestionPhaseRelationRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.QuestionRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.QuestionnairePhaseRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.TrainingPhaseRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-@Service
-public class QuestionnairePhaseService {
-
-    private static final Logger LOG = LoggerFactory.getLogger(QuestionnairePhaseService.class);
-
-    private final QuestionnairePhaseRepository questionnairePhaseRepository;
-    private final AbstractPhaseRepository abstractPhaseRepository;
-    private final QuestionRepository questionRepository;
-    private final TrainingPhaseRepository trainingPhaseRepository;
-    private final QuestionPhaseRelationRepository questionPhaseRelationRepository;
-
-    @Autowired
-    public QuestionnairePhaseService(QuestionnairePhaseRepository questionnairePhaseRepository, AbstractPhaseRepository abstractPhaseRepository,
-                                     QuestionRepository questionRepository, TrainingPhaseRepository trainingPhaseRepository,
-                                     QuestionPhaseRelationRepository questionPhaseRelationRepository) {
-        this.questionnairePhaseRepository = questionnairePhaseRepository;
-        this.abstractPhaseRepository = abstractPhaseRepository;
-        this.questionRepository = questionRepository;
-        this.trainingPhaseRepository = trainingPhaseRepository;
-        this.questionPhaseRelationRepository = questionPhaseRelationRepository;
-    }
-
-    public QuestionnairePhaseDTO createDefaultQuestionnairePhase(Long trainingDefinitionId, PhaseCreateDTO phaseCreateDTO) {
-        QuestionnairePhase questionnairePhase = new QuestionnairePhase();
-        questionnairePhase.setTitle("Title of questionnaire phase");
-        questionnairePhase.setTrainingDefinitionId(trainingDefinitionId);
-        questionnairePhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
-
-        if (PhaseTypeCreate.QUESTIONNAIRE_ADAPTIVE.equals(phaseCreateDTO.getPhaseType())) {
-            questionnairePhase.setQuestionnaireType(QuestionnaireType.ADAPTIVE);
-        } else {
-            questionnairePhase.setQuestionnaireType(QuestionnaireType.GENERAL);
-        }
-
-        QuestionnairePhase persistedEntity = questionnairePhaseRepository.save(questionnairePhase);
-        return BeanMapper.INSTANCE.toDto(persistedEntity);
-    }
-
-    public QuestionnairePhaseDTO updateQuestionnairePhase(Long definitionId, Long phaseId, QuestionnaireUpdateDTO questionnaireUpdateDto) {
-        QuestionnairePhase questionnairePhase = BeanMapper.INSTANCE.toEntity(questionnaireUpdateDto);
-        questionnairePhase.setId(phaseId);
-
-        QuestionnairePhase persistedQuestionnairePhase = questionnairePhaseRepository.findById(questionnairePhase.getId())
-                .orElseThrow(() -> new RuntimeException("Questionnaire phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed);
-
-        questionnairePhase.setTrainingDefinitionId(persistedQuestionnairePhase.getTrainingDefinitionId());
-        questionnairePhase.setOrder(persistedQuestionnairePhase.getOrder());
-        questionnairePhase.setQuestionnaireType(persistedQuestionnairePhase.getQuestionnaireType());
-
-        questionnairePhase.getQuestionPhaseRelations().clear();
-        questionnairePhase.getQuestionPhaseRelations().addAll(resolveQuestionnairePhaseRelationsUpdate(questionnairePhase, questionnaireUpdateDto));
-
-        if (!CollectionUtils.isEmpty(questionnairePhase.getQuestions())) {
-            questionnairePhase.getQuestions().forEach(x -> x.setQuestionnairePhase(questionnairePhase));
-            for (Question question : questionnairePhase.getQuestions()) {
-                question.getChoices().forEach(x -> x.setQuestion(question));
-            }
-        }
-
-        QuestionnairePhase savedEntity = questionnairePhaseRepository.save(questionnairePhase);
-        return BeanMapper.INSTANCE.toDto(savedEntity);
-    }
-
-    private List<QuestionPhaseRelation> resolveQuestionnairePhaseRelationsUpdate(QuestionnairePhase questionnairePhase, QuestionnaireUpdateDTO questionnaireUpdateDto) {
-        List<QuestionPhaseRelation> questionnairePhaseRelations = new ArrayList<>();
-
-        if (!CollectionUtils.isEmpty(questionnaireUpdateDto.getPhaseRelations())) {
-            int order = 0;
-            for (QuestionPhaseRelationDTO phaseRelation : questionnaireUpdateDto.getPhaseRelations()) {
-                Set<Question> questionsInPhaseRelation = Set.copyOf(questionRepository.findAllById(phaseRelation.getQuestionIds()));
-
-                QuestionPhaseRelation questionPhaseRelation;
-                if (Objects.isNull(phaseRelation.getId())) {
-                    questionPhaseRelation = new QuestionPhaseRelation();
-                    questionPhaseRelation.setQuestions(questionsInPhaseRelation);
-                } else {
-                    questionPhaseRelation = questionPhaseRelationRepository.findById(phaseRelation.getId())
-                            .orElseThrow(() -> new RuntimeException("Question phase relation was not found"));
-                    // TODO throw proper exception once kypo2-training is migrated
-
-                    questionPhaseRelation.getQuestions().clear();
-                    questionPhaseRelation.getQuestions().addAll(questionsInPhaseRelation);
-                }
-
-                TrainingPhase trainingPhase = trainingPhaseRepository.findById(phaseRelation.getPhaseId())
-                        .orElseThrow(() -> new RuntimeException("Training phase was not found"));
-                // TODO throw proper exception once kypo2-training is migrated
-
-                questionPhaseRelation.setOrder(order);
-                questionPhaseRelation.setSuccessRate(phaseRelation.getSuccessRate());
-                questionPhaseRelation.setRelatedTrainingPhase(trainingPhase);
-                questionPhaseRelation.setQuestionnairePhase(questionnairePhase);
-
-                questionnairePhaseRelations.add(questionPhaseRelation);
-                order++;
-            }
-        }
-
-        return questionnairePhaseRelations;
-    }
-
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/SecurityService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/SecurityService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f66ba7a5be925b4fa9a2eccceed992c11ee7cb6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/SecurityService.java
@@ -0,0 +1,160 @@
+package cz.muni.ics.kypo.training.adaptive.service;
+
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalRO;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleTypeSecurity;
+import cz.muni.ics.kypo.training.adaptive.exceptions.*;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingRunRepository;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+
+
+/**
+ * The type Security service.
+ */
+@Service
+@TransactionalRO(propagation = Propagation.REQUIRES_NEW)
+public class SecurityService {
+
+    private UserManagementServiceApi userManagementServiceApi;
+    private TrainingRunRepository trainingRunRepository;
+    private TrainingDefinitionRepository trainingDefinitionRepository;
+    private TrainingInstanceRepository trainingInstanceRepository;
+
+    /**
+     * Instantiates a new Security service.
+     *
+     * @param trainingInstanceRepository   the training instance repository
+     * @param trainingDefinitionRepository the training definition repository
+     * @param trainingRunRepository        the training run repository
+     */
+    @Autowired
+    public SecurityService(UserManagementServiceApi userManagementServiceApi,
+                           TrainingInstanceRepository trainingInstanceRepository,
+                           TrainingDefinitionRepository trainingDefinitionRepository,
+                           TrainingRunRepository trainingRunRepository) {
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.trainingRunRepository = trainingRunRepository;
+    }
+
+    /**
+     * Is trainee of given training run boolean.
+     *
+     * @param trainingRunId the training run id
+     * @return the boolean
+     */
+    public boolean isTraineeOfGivenTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = trainingRunRepository.findById(trainingRunId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(),
+                        trainingRunId, "The necessary permissions are required for a resource.")));
+        return trainingRun.getParticipantRef().getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId());
+    }
+
+    /**
+     * Is organizer of given training instance boolean.
+     *
+     * @param instanceId the instance id
+     * @return the boolean
+     */
+    public boolean isOrganizerOfGivenTrainingInstance(Long instanceId) {
+        TrainingInstance trainingInstance = trainingInstanceRepository.findById(instanceId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "id", instanceId.getClass(),
+                        instanceId, "The necessary permissions are required for a resource.")));
+        return trainingInstance.getOrganizers().stream()
+                .anyMatch(o -> o.getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Is organizer of given training run.
+     *
+     * @param trainingRunId the run id
+     * @return the boolean
+     */
+    public boolean isOrganizerOfGivenTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = trainingRunRepository.findById(trainingRunId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(),
+                        trainingRunId, "The necessary permissions are required for a resource.")));
+        return trainingRun.getTrainingInstance().getOrganizers().stream()
+                .anyMatch(o -> o.getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Is designer of given training definition boolean.
+     *
+     * @param definitionId the definition id
+     * @return the boolean
+     */
+    public boolean isDesignerOfGivenTrainingDefinition(Long definitionId) {
+        TrainingDefinition trainingDefinition = trainingDefinitionRepository.findById(definitionId)
+                .orElseThrow(() -> new ForbiddenException("The necessary permissions are required for a resource."));
+        return trainingDefinition.getAuthors().stream()
+                .anyMatch(a -> a.getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Is designer of given phase boolean.
+     *
+     * @param phaseId the phase id
+     * @return the boolean
+     */
+    public boolean isDesignerOfGivenPhase(Long phaseId) {
+        TrainingDefinition trainingDefinition = trainingDefinitionRepository.findByPhaseId(phaseId)
+                .orElseThrow(() -> new ForbiddenException("The necessary permissions are required for a resource."));
+        return trainingDefinition.getAuthors().stream()
+                .anyMatch(a -> a.getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Is designer of given task boolean.
+     *
+     * @param taskId the task id
+     * @return the boolean
+     */
+    public boolean isDesignerOfGivenTask(Long taskId) {
+        TrainingDefinition trainingDefinition = trainingDefinitionRepository.findByTaskId(taskId)
+                .orElseThrow(() -> new ForbiddenException("The necessary permissions are required for a resource."));
+        return trainingDefinition.getAuthors().stream()
+                .anyMatch(a -> a.getUserRefId().equals(userManagementServiceApi.getLoggedInUserRefId()));
+    }
+
+    /**
+     * Has role boolean.
+     *
+     * @param roleTypeSecurity the role type security
+     * @return the boolean
+     */
+    public boolean hasRole(RoleTypeSecurity roleTypeSecurity) {
+        OAuth2Authentication authentication = (OAuth2Authentication) SecurityContextHolder.getContext().getAuthentication();
+        for (GrantedAuthority gA : authentication.getUserAuthentication().getAuthorities()) {
+            if (gA.getAuthority().equals(roleTypeSecurity.name())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if response from external API is not null.
+     *
+     * @param object  object to check
+     * @param message exception message if response is null
+     * @throws MicroserviceApiException if response is null
+     */
+    private void checkNonNull(Object object, String message) {
+        if (object == null) {
+            throw new MicroserviceApiException(message);
+        }
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TaskService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TaskService.java
deleted file mode 100644
index 67ccb756d62c6aa87d3ec87092b3dda88706b490..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TaskService.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.service;
-
-import cz.muni.ics.kypo.training.adaptive.domain.Task;
-import cz.muni.ics.kypo.training.adaptive.domain.TrainingPhase;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskCopyDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TaskUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.mapper.BeanMapper;
-import cz.muni.ics.kypo.training.adaptive.repository.TaskRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.TrainingPhaseRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-@Service
-@Transactional
-public class TaskService {
-
-    private static final Logger LOG = LoggerFactory.getLogger(TaskService.class);
-
-    private final TaskRepository taskRepository;
-    private final TrainingPhaseRepository trainingPhaseRepository;
-
-    @Autowired
-    public TaskService(TaskRepository taskRepository, TrainingPhaseRepository trainingPhaseRepository) {
-        this.taskRepository = taskRepository;
-        this.trainingPhaseRepository = trainingPhaseRepository;
-    }
-
-    public TaskDTO createDefaultTask(Long trainingDefinitionId, Long phaseId) {
-        TrainingPhase trainingPhase = trainingPhaseRepository.findById(phaseId)
-                .orElseThrow(() -> new RuntimeException("Game phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId (field structure will be probably changed)
-
-        Task task = new Task();
-        task.setTitle("Title of a new task");
-        task.setTrainingPhase(trainingPhase);
-        task.setOrder(taskRepository.getCurrentMaxOrder(phaseId) + 1);
-        task.setAnswer("Secret flag");
-        task.setContent("Task content ...");
-        task.setSolution("Task solution ...");
-        task.setIncorrectAnswerLimit(1);
-
-        Task persistedEntity = taskRepository.save(task);
-        return BeanMapper.INSTANCE.toDto(persistedEntity);
-    }
-
-    public TaskDTO cloneTask(Long trainingDefinitionId, Long phaseId, Long taskId, TaskCopyDTO taskCopyDTO) {
-        Task task = BeanMapper.INSTANCE.toEntity(taskCopyDTO);
-
-        TrainingPhase trainingPhase = trainingPhaseRepository.findById(phaseId)
-                .orElseThrow(() -> new RuntimeException("Game phase was not found"));
-
-        // TODO add check to trainingDefinitionId (field structure will be probably changed)
-
-        task.setId(null);
-        task.setTrainingPhase(trainingPhase);
-        task.setOrder(taskRepository.getCurrentMaxOrder(phaseId) + 1);
-
-        Task persistedEntity = taskRepository.save(task);
-        return BeanMapper.INSTANCE.toDto(persistedEntity);
-    }
-
-    @Transactional(readOnly = true)
-    public TaskDTO getTask(Long trainingDefinitionId, Long phaseId, Long taskId) {
-        Task task = taskRepository.findById(taskId)
-                .orElseThrow(() -> new RuntimeException("Task was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        return BeanMapper.INSTANCE.toDto(task);
-    }
-
-    public TaskDTO updateTask(Long trainingDefinitionId, Long phaseId, Long taskId, TaskUpdateDTO taskUpdateDto) {
-        Task taskUpdate = BeanMapper.INSTANCE.toEntity(taskUpdateDto);
-        taskUpdate.setId(taskId);
-
-        Task persistedTask = taskRepository.findById(taskUpdate.getId())
-                .orElseThrow(() -> new RuntimeException("Task was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        taskUpdate.setTrainingPhase(persistedTask.getTrainingPhase());
-        taskUpdate.setOrder(persistedTask.getOrder());
-
-        Task savedEntity = taskRepository.save(taskUpdate);
-        return BeanMapper.INSTANCE.toDto(savedEntity);
-    }
-
-    public void removeTask(Long trainingDefinitionId, Long phaseId, Long taskId) {
-        Task task = taskRepository.findById(taskId)
-                .orElseThrow(() -> new RuntimeException("Task was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed)
-
-        taskRepository.decreaseOrderAfterTaskWasDeleted(phaseId, task.getOrder());
-        taskRepository.delete(task);
-    }
-
-    public void moveTaskToSpecifiedOrder(Long taskIdFrom, int newPosition) {
-        Task task = taskRepository.findById(taskIdFrom)
-                .orElseThrow(() -> new RuntimeException("Task was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        int fromOrder = task.getOrder();
-
-        if (fromOrder < newPosition) {
-            taskRepository.decreaseOrderOfTasksOnInterval(task.getTrainingPhase().getId(), fromOrder, newPosition);
-        } else if (fromOrder > newPosition) {
-            taskRepository.increaseOrderOfTasksOnInterval(task.getTrainingPhase().getId(), newPosition, fromOrder);
-        } else {
-            // nothing should be changed, no further actions needed
-            return;
-        }
-
-        task.setOrder(newPosition);
-        taskRepository.save(task);
-    }
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TrainingPhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TrainingPhaseService.java
deleted file mode 100644
index eb966328881ed69d2ed02badf9162fec60e9dd8f..0000000000000000000000000000000000000000
--- a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/TrainingPhaseService.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package cz.muni.ics.kypo.training.adaptive.service;
-
-import cz.muni.ics.kypo.training.adaptive.domain.DecisionMatrixRow;
-import cz.muni.ics.kypo.training.adaptive.domain.TrainingPhase;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseDTO;
-import cz.muni.ics.kypo.training.adaptive.dto.training.TrainingPhaseUpdateDTO;
-import cz.muni.ics.kypo.training.adaptive.mapper.BeanMapper;
-import cz.muni.ics.kypo.training.adaptive.repository.AbstractPhaseRepository;
-import cz.muni.ics.kypo.training.adaptive.repository.TrainingPhaseRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.CollectionUtils;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-@Service
-@Transactional
-public class TrainingPhaseService {
-
-    private static final Logger LOG = LoggerFactory.getLogger(TrainingPhaseService.class);
-
-    private final TrainingPhaseRepository trainingPhaseRepository;
-    private final AbstractPhaseRepository abstractPhaseRepository;
-
-    @Autowired
-    public TrainingPhaseService(TrainingPhaseRepository trainingPhaseRepository, AbstractPhaseRepository abstractPhaseRepository) {
-        this.trainingPhaseRepository = trainingPhaseRepository;
-        this.abstractPhaseRepository = abstractPhaseRepository;
-    }
-
-    public TrainingPhaseDTO createDefaultTrainingPhase(Long trainingDefinitionId) {
-        TrainingPhase trainingPhase = new TrainingPhase();
-        trainingPhase.setTitle("Title of training phase");
-        trainingPhase.setTrainingDefinitionId(trainingDefinitionId);
-        trainingPhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
-
-        trainingPhase.setDecisionMatrix(prepareDefaultDecisionMatrix(trainingDefinitionId, trainingPhase));
-
-        TrainingPhase persistedEntity = trainingPhaseRepository.save(trainingPhase);
-        return BeanMapper.INSTANCE.toDto(persistedEntity);
-    }
-
-    public TrainingPhaseDTO updateTrainingPhase(Long definitionId, Long phaseId, TrainingPhaseUpdateDTO trainingPhaseUpdate) {
-        TrainingPhase trainingPhase = BeanMapper.INSTANCE.toEntity(trainingPhaseUpdate);
-        trainingPhase.setId(phaseId);
-
-        TrainingPhase persistedTrainingPhase = trainingPhaseRepository.findById(trainingPhase.getId())
-                .orElseThrow(() -> new RuntimeException("Training phase was not found"));
-        // TODO throw proper exception once kypo2-training is migrated
-
-        // TODO add check to trainingDefinitionId and phaseId (field structure will be probably changed);
-
-        trainingPhase.setTrainingDefinitionId(persistedTrainingPhase.getTrainingDefinitionId());
-        trainingPhase.setOrder(persistedTrainingPhase.getOrder());
-        trainingPhase.setTasks(persistedTrainingPhase.getTasks());
-
-        if (!CollectionUtils.isEmpty(trainingPhase.getDecisionMatrix())) {
-            trainingPhase.getDecisionMatrix().forEach(x -> x.setTrainingPhase(trainingPhase));
-        }
-
-        TrainingPhase savedEntity = trainingPhaseRepository.save(trainingPhase);
-        return BeanMapper.INSTANCE.toDto(savedEntity);
-    }
-
-    public void alignDecisionMatrixForPhasesInTrainingDefinition(Long trainingDefinitionId) {
-        List<TrainingPhase> trainingPhases = trainingPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingDefinitionId);
-
-        int currentPhaseOrder = 0;
-        for (TrainingPhase trainingPhase : trainingPhases) {
-            alignDecisionMatrixForPhase(trainingPhase, currentPhaseOrder);
-            currentPhaseOrder++;
-        }
-    }
-
-    private List<DecisionMatrixRow> prepareDefaultDecisionMatrix(Long trainingDefinitionId, TrainingPhase trainingPhase) {
-        List<DecisionMatrixRow> result = new ArrayList<>();
-
-        int numberOfExistingPhases = trainingPhaseRepository.getNumberOfExistingPhases(trainingDefinitionId);
-
-        // number of rows should be equal to number of existing phases + 1
-        for (int i = 0; i <= numberOfExistingPhases; i++) {
-            DecisionMatrixRow decisionMatrixRow = new DecisionMatrixRow();
-            decisionMatrixRow.setTrainingPhase(trainingPhase);
-            decisionMatrixRow.setOrder(i);
-
-            result.add(decisionMatrixRow);
-        }
-        return result;
-    }
-
-    private void alignDecisionMatrixForPhase(TrainingPhase trainingPhase, int currentPhaseOrder) {
-        if (Objects.isNull(trainingPhase)) {
-            return;
-        }
-
-        int numberOfRows = 0;
-        if (!CollectionUtils.isEmpty(trainingPhase.getDecisionMatrix())) {
-            numberOfRows = trainingPhase.getDecisionMatrix().size();
-        }
-
-        final int expectedNumberOfRows = currentPhaseOrder + 1;
-        if (numberOfRows == expectedNumberOfRows) {
-            return;
-        } else if (numberOfRows < expectedNumberOfRows) {
-            List<DecisionMatrixRow> newDecisionMatrixRows = getNewDecisionMatrixRows(numberOfRows, expectedNumberOfRows, trainingPhase);
-            trainingPhase.getDecisionMatrix().addAll(newDecisionMatrixRows);
-        } else {
-            List<DecisionMatrixRow> neededDecisionMatrixRows = getOnlyNeededDecisionMatrixRows(expectedNumberOfRows, trainingPhase);
-            trainingPhase.getDecisionMatrix().clear();
-            trainingPhase.getDecisionMatrix().addAll(neededDecisionMatrixRows);
-        }
-
-        trainingPhaseRepository.save(trainingPhase);
-    }
-
-    private List<DecisionMatrixRow> getNewDecisionMatrixRows(int currentNumberOfNewRows, int expectedNumberOfRows, TrainingPhase trainingPhase) {
-        List<DecisionMatrixRow> result = new ArrayList<>();
-        for (int i = currentNumberOfNewRows; i < expectedNumberOfRows; i++) {
-            DecisionMatrixRow decisionMatrixRow = new DecisionMatrixRow();
-            decisionMatrixRow.setTrainingPhase(trainingPhase);
-            decisionMatrixRow.setOrder(i);
-
-            result.add(decisionMatrixRow);
-        }
-        return result;
-    }
-
-    private List<DecisionMatrixRow> getOnlyNeededDecisionMatrixRows(int expectedNumberOfRows, TrainingPhase trainingPhase) {
-        List<DecisionMatrixRow> decisionMatrix = trainingPhase.getDecisionMatrix();
-        return decisionMatrix.stream()
-                .sorted(Comparator.comparingInt(DecisionMatrixRow::getOrder))
-                .limit(expectedNumberOfRows)
-                .collect(Collectors.toList());
-
-    }
-}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/UserService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/UserService.java
new file mode 100644
index 0000000000000000000000000000000000000000..23d48fd758adbbaee7f226095043820a6c0a5f44
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/UserService.java
@@ -0,0 +1,73 @@
+package cz.muni.ics.kypo.training.adaptive.service;
+
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleType;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import cz.muni.ics.kypo.training.adaptive.repository.UserRefRepository;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriBuilder;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * The type User service.
+ */
+@Service
+public class UserService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(UserService.class);
+
+    private final UserRefRepository userRefRepository;
+    private final UserManagementServiceApi userManagementServiceApi;
+
+    /**
+     * Instantiates a new User service.
+     *
+     * @param userRefRepository              the user ref repository
+     */
+    public UserService(UserRefRepository userRefRepository,
+                       UserManagementServiceApi userManagementServiceApi) {
+        this.userRefRepository = userRefRepository;
+        this.userManagementServiceApi = userManagementServiceApi;
+    }
+
+    /**
+     * Finds specific User reference by login
+     *
+     * @param userRefId of wanted User reference
+     * @return {@link UserRef} with corresponding login
+     * @throws EntityNotFoundException UserRef was not found
+     */
+    public UserRef getUserByUserRefId(Long userRefId) {
+        return userRefRepository.findUserByUserRefId(userRefId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(UserRef.class, "id", userRefId.getClass(), userRefId)));
+    }
+
+    /**
+     * Create new user reference
+     *
+     * @param userRefToCreate user reference to be created
+     * @return created {@link UserRef}
+     */
+    @TransactionalWO
+    public UserRef createUserRef(UserRef userRefToCreate) {
+        UserRef userRef = userRefRepository.save(userRefToCreate);
+        LOG.info("User ref with user_ref_id: {} created.", userRef.getUserRefId());
+        return userRef;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/ElasticsearchServiceApi.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/ElasticsearchServiceApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..f28c2a507e75c71d24309a30f45996a09c69f404
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/ElasticsearchServiceApi.java
@@ -0,0 +1,140 @@
+package cz.muni.ics.kypo.training.adaptive.service.api;
+
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The type User service.
+ */
+@Service
+public class ElasticsearchServiceApi {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchServiceApi.class);
+    private WebClient elasticsearchServiceWebClient;
+
+    /**
+     * Instantiates a new ElasticSearchService API.
+     *
+     * @param elasticsearchServiceWebClient the web client
+     */
+    public ElasticsearchServiceApi(@Qualifier("elasticsearchServiceWebClient") WebClient elasticsearchServiceWebClient) {
+        this.elasticsearchServiceWebClient = elasticsearchServiceWebClient;
+    }
+
+    /**
+     * Deletes events from elasticsearch for particular training instance
+     *
+     * @param trainingInstanceId id of the training instance whose events to delete.
+     * @throws MicroserviceApiException error with specific message when calling elasticsearch microservice.
+     */
+    public void deleteEventsByTrainingInstanceId(Long trainingInstanceId) {
+        try {
+            elasticsearchServiceWebClient
+                    .delete()
+                    .uri("/training-platform-events/training-instances/{instanceId}", trainingInstanceId)
+                    .retrieve()
+                    .bodyToMono(Void.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API to delete events for particular instance (ID: " + trainingInstanceId + ")", ex.getApiSubError());
+        }
+    }
+
+
+    /**
+     * Obtain events from elasticsearch for particular training run
+     *
+     * @param trainingRun thee training run whose events to obtain.
+     * @throws MicroserviceApiException error with specific message when calling elasticsearch microservice.
+     */
+    public List<Map<String, Object>> findAllEventsFromTrainingRun(TrainingRun trainingRun) {
+        try {
+            Long definitionId = trainingRun.getTrainingInstance().getTrainingDefinition().getId();
+            Long instanceId = trainingRun.getTrainingInstance().getId();
+            return elasticsearchServiceWebClient
+                    .get()
+                    .uri("/training-platform-events/training-definitions/{definitionId}/training-instances/{instanceId}/training-runs/{runId}", definitionId, instanceId, trainingRun.getId())
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<List<Map<String, Object>>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API for particular run (ID: " + trainingRun.getId() + ")", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Deletes events from elasticsearch for particular training run
+     *
+     * @param trainingInstanceId id of the training instance in which the training run is running.
+     * @param trainingRunId      id of the training run whose events to delete.
+     * @throws MicroserviceApiException error with specific message when calling elasticsearch microservice.
+     */
+    public void deleteEventsFromTrainingRun(Long trainingInstanceId, Long trainingRunId) {
+        try {
+            elasticsearchServiceWebClient
+                    .delete()
+                    .uri("/training-platform-events/training-instances/{instanceId}/training-runs/{runId}", trainingInstanceId, trainingRunId)
+                    .retrieve()
+                    .bodyToMono(Void.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API to delete events for particular training run (ID: " + trainingRunId + ")", ex.getApiSubError());
+        }
+    }
+
+    public List<Map<String, Object>> findAllConsoleCommandsFromSandbox(Long sandboxId) {
+        try {
+            return elasticsearchServiceWebClient
+                    .get()
+                    .uri("/training-platform-commands/sandboxes/{sandboxId}", sandboxId)
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<List<Map<String, Object>>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API for particular sandbox (ID: " + sandboxId + ")", ex.getApiSubError());
+        }
+    }
+
+    public List<Map<String, Object>> findAllConsoleCommandsFromSandboxAndTimeRange(Integer sandboxId, Long from, Long to) {
+        try {
+            return elasticsearchServiceWebClient
+                    .get()
+                    .uri(uriBuilder -> uriBuilder.path("/training-platform-commands/sandboxes/{sandboxId}/ranges")
+                            .queryParam("from", from)
+                            .queryParam("to", to)
+                            .build(sandboxId)
+                    )
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<List<Map<String, Object>>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API for particular commands of sandbox (ID: " + sandboxId + ")", ex.getApiSubError());
+        }
+    }
+
+    public void deleteBashCommandsFromPool(Long poolId) {
+        try {
+            elasticsearchServiceWebClient
+                    .delete()
+                    .uri("/training-platform-commands/pools/{poolId}", poolId)
+                    .retrieve()
+                    .bodyToMono(Void.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling Elasticsearch API to delete bash commands for particular pool (ID: " + poolId + ")", ex.getApiSubError());
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/SandboxServiceApi.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/SandboxServiceApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..8cb39e57783e61fe8fea3b319f4c0863c8033333
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/SandboxServiceApi.java
@@ -0,0 +1,128 @@
+package cz.muni.ics.kypo.training.adaptive.service.api;
+
+import cz.muni.ics.kypo.training.adaptive.dto.responses.LockedPoolInfo;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PoolInfoDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.SandboxDefinitionInfo;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.SandboxInfo;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.ForbiddenException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.errors.PythonApiError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+
+/**
+ * The type User service.
+ */
+@Service
+public class SandboxServiceApi {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SandboxServiceApi.class);
+    private WebClient sandboxServiceWebClient;
+
+
+    /**
+     * Instantiates a new SandboxService API.
+     *
+     * @param sandboxServiceWebClient the web client
+     */
+    public SandboxServiceApi(@Qualifier("sandboxServiceWebClient") WebClient sandboxServiceWebClient) {
+        this.sandboxServiceWebClient = sandboxServiceWebClient;
+    }
+
+
+    public Long getAndLockSandboxForTrainingRun(Long poolId) {
+        try {
+            SandboxInfo sandboxInfo = sandboxServiceWebClient
+                    .get()
+                    .uri("/pools/{poolId}/sandboxes/get-and-lock", poolId)
+                    .retrieve()
+                    .bodyToMono(SandboxInfo.class)
+                    .block();
+            return sandboxInfo.getId();
+        } catch (CustomWebClientException ex) {
+            if (ex.getStatusCode() == HttpStatus.CONFLICT) {
+                throw new ForbiddenException("There is no available sandbox, wait a minute and try again or ask organizer to allocate more sandboxes.");
+            }
+            throw new MicroserviceApiException("Error when calling OpenStack Sandbox Service API to get unlocked sandbox from pool (ID: " + poolId + ")", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Lock pool locked pool info.
+     *
+     * @param poolId the pool id
+     * @return the locked pool info
+     */
+    public LockedPoolInfo lockPool(Long poolId) {
+        try {
+            return sandboxServiceWebClient
+                    .post()
+                    .uri("/pools/{poolId}/locks", poolId)
+                    .body(Mono.just("{}"), String.class)
+                    .retrieve()
+                    .bodyToMono(LockedPoolInfo.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Currently, it is not possible to lock and assign pool with (ID: " + poolId + ").", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Unlock pool.
+     *
+     * @param poolId the pool id
+     */
+    public void unlockPool(Long poolId) {
+        try {
+            // get lock id from pool
+            PoolInfoDTO poolInfoDto = sandboxServiceWebClient
+                    .get()
+                    .uri("/pools/{poolId}", poolId)
+                    .retrieve()
+                    .bodyToMono(PoolInfoDTO.class)
+                    .block();
+            // unlock pool
+            if (poolInfoDto != null && poolInfoDto.getLockId() != null) {
+                sandboxServiceWebClient
+                        .delete()
+                        .uri("/pools/{poolId}/locks/{lockId}", poolId, poolInfoDto.getLockId())
+                        .retrieve()
+                        .bodyToMono(Void.class)
+                        .block();
+            }
+        } catch (CustomWebClientException ex) {
+            if (ex.getStatusCode() != HttpStatus.NOT_FOUND) {
+                throw new MicroserviceApiException("Currently, it is not possible to unlock a pool with (ID: " + poolId + ").", ex.getApiSubError());
+            }
+        }
+    }
+
+    /**
+     * Gets sandbox definition id.
+     *
+     * @param poolId the pool id
+     * @return the sandbox definition id
+     */
+    public SandboxDefinitionInfo getSandboxDefinitionId(Long poolId) {
+        try {
+            return sandboxServiceWebClient
+                    .get()
+                    .uri("/pools/{poolId}/definition", poolId)
+                    .retrieve()
+                    .bodyToMono(SandboxDefinitionInfo.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            if (ex.getStatusCode() == HttpStatus.CONFLICT) {
+                throw new ForbiddenException("There is no available sandbox definition for particular pool (ID: " + poolId + ").");
+            }
+            throw new MicroserviceApiException("Error when calling Python API to sandbox for particular pool (ID: " + poolId + ")", new PythonApiError(ex.getMessage()));
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/UserManagementServiceApi.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/UserManagementServiceApi.java
new file mode 100644
index 0000000000000000000000000000000000000000..b61b56d0d8dbbcbf5a00ade7ee1982b06f11c313
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/api/UserManagementServiceApi.java
@@ -0,0 +1,219 @@
+package cz.muni.ics.kypo.training.adaptive.service.api;
+
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.dto.UserRefDTO;
+import cz.muni.ics.kypo.training.adaptive.dto.responses.PageResultResource;
+import cz.muni.ics.kypo.training.adaptive.enums.RoleType;
+import cz.muni.ics.kypo.training.adaptive.exceptions.CustomWebClientException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.MicroserviceApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriBuilder;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * The type User Management Api.
+ */
+@Service
+public class UserManagementServiceApi {
+
+    private static final Logger LOG = LoggerFactory.getLogger(UserManagementServiceApi.class);
+    private WebClient userManagementServiceWebClient;
+
+    /**
+     * Instantiates a new User Management Api.
+     *
+     * @param userManagementServiceWebClient the rest template
+     */
+    public UserManagementServiceApi(@Qualifier("userManagementServiceWebClient") WebClient userManagementServiceWebClient) {
+        this.userManagementServiceWebClient = userManagementServiceWebClient;
+    }
+
+    /**
+     * Finds specific User reference by login
+     *
+     * @param id of wanted User reference
+     * @return {@link UserRef} with corresponding login
+     * @throws EntityNotFoundException UserRef was not found
+     */
+    public UserRefDTO getUserRefDTOByUserRefId(Long id) {
+        try {
+            return userManagementServiceWebClient
+                    .get()
+                    .uri("/users/{id}", id)
+                    .retrieve()
+                    .bodyToMono(UserRefDTO.class)
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to obtain info about user(ID: " + id + ").", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Gets users with given user ref ids.
+     *
+     * @param userRefIds the user ref ids
+     * @param pageable   pageable parameter with information about pagination.
+     * @param givenName  optional parameter used for filtration
+     * @param familyName optional parameter used for filtration
+     * @return the users with given user ref ids
+     */
+    public PageResultResource<UserRefDTO> getUserRefDTOsByUserIds(Set<Long> userRefIds, Pageable pageable, String givenName, String familyName) {
+        if (userRefIds.isEmpty()) {
+            return new PageResultResource<>(Collections.emptyList(), new PageResultResource.Pagination(0, 0, pageable.getPageSize(), 0, 0));
+        }
+        try {
+            return userManagementServiceWebClient
+                    .get()
+                    .uri(uriBuilder -> {
+                                uriBuilder
+                                        .path("/users/ids")
+                                        .queryParam("ids", StringUtils.collectionToDelimitedString(userRefIds, ","));
+                                this.setCommonParams(givenName, familyName, pageable, uriBuilder);
+                                return uriBuilder.build();
+                            }
+                    )
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<PageResultResource<UserRefDTO>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to obtain users by IDs: " + userRefIds + ".", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Finds all logins of users that have role of designer
+     *
+     * @param roleType   the role type
+     * @param pageable   pageable parameter with information about pagination.
+     * @param givenName  optional parameter used for filtration
+     * @param familyName optional parameter used for filtration
+     * @return list of users with given role
+     */
+    public PageResultResource<UserRefDTO> getUserRefsByRole(RoleType roleType, Pageable pageable, String givenName, String familyName) {
+        try {
+            return userManagementServiceWebClient
+                    .get()
+                    .uri(uriBuilder -> {
+                                uriBuilder
+                                        .path("/roles/users")
+                                        .queryParam("roleType", roleType.name());
+                                this.setCommonParams(givenName, familyName, pageable, uriBuilder);
+                                return uriBuilder.build();
+                            }
+                    )
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<PageResultResource<UserRefDTO>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to obtain users with role " + roleType.name() + ".", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Finds all logins of users that have role of designer
+     *
+     * @param roleType   the role type
+     * @param userRefIds ids of the users who should be excluded from the result set.
+     * @param pageable   the pageable
+     * @param givenName  optional parameter used for filtration
+     * @param familyName optional parameter used for filtration
+     * @return list of users with given role
+     */
+    public PageResultResource<UserRefDTO> getUserRefsByRoleAndNotWithIds(RoleType roleType, Set<Long> userRefIds, Pageable pageable, String givenName, String familyName) {
+        try {
+            return userManagementServiceWebClient
+                    .get()
+                    .uri(uriBuilder -> {
+                                uriBuilder
+                                        .path("/roles/users-not-with-ids")
+                                        .queryParam("roleType", roleType.name())
+                                        .queryParam("ids", StringUtils.collectionToDelimitedString(userRefIds, ","));
+                                this.setCommonParams(givenName, familyName, pageable, uriBuilder);
+                                return uriBuilder.build();
+                            }
+                    )
+                    .retrieve()
+                    .bodyToMono(new ParameterizedTypeReference<PageResultResource<UserRefDTO>>() {
+                    })
+                    .block();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to obtain users with role " + roleType.name() + " and IDs: " + userRefIds + ".", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Gets user ref id from user and group.
+     *
+     * @return the user ref id from user and group
+     */
+    public Long getLoggedInUserRefId() {
+        try {
+            UserRefDTO userRefDTO = userManagementServiceWebClient
+                    .get()
+                    .uri("/users/info")
+                    .retrieve()
+                    .bodyToMono(UserRefDTO.class)
+                    .block();
+            checkNonNull(userRefDTO, "Returned null response when calling user management service API to get info about logged in user.");
+            return userRefDTO.getUserRefId();
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to get info about logged in user.", ex.getApiSubError());
+        }
+    }
+
+    /**
+     * Gets user ref id from user and group.
+     *
+     * @return the user ref id from user and group
+     */
+    public UserRefDTO getUserRefDTO() {
+        try {
+            UserRefDTO userRefDTO = userManagementServiceWebClient
+                    .get()
+                    .uri("/users/info")
+                    .retrieve()
+                    .bodyToMono(UserRefDTO.class)
+                    .block();
+            checkNonNull(userRefDTO, "Returned null response when calling user management service API to get info about logged in user.");
+            return userRefDTO;
+        } catch (CustomWebClientException ex) {
+            throw new MicroserviceApiException("Error when calling user management service API to get info about logged in user.", ex.getApiSubError());
+        }
+    }
+
+    private void setCommonParams(String givenName, String familyName, Pageable pageable, UriBuilder builder) {
+        if (givenName != null) {
+            builder.queryParam("givenName", givenName);
+        }
+        if (familyName != null) {
+            builder.queryParam("familyName", familyName);
+        }
+        builder.queryParam("page", pageable.getPageNumber());
+        builder.queryParam("size", pageable.getPageSize());
+    }
+
+    /**
+     * Check if response from external API is not null.
+     *
+     * @param object  object to check
+     * @param message exception message if response is null
+     * @throws MicroserviceApiException if response is null
+     */
+    private void checkNonNull(Object object, String message) {
+        if (object == null) {
+            throw new MicroserviceApiException(message);
+        }
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditEventsService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditEventsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c15ecfef17180fa1707d484457cdc6681be6df1e
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditEventsService.java
@@ -0,0 +1,202 @@
+package cz.muni.ics.kypo.training.adaptive.service.audit;
+
+import cz.muni.csirt.kypo.events.adaptive.trainings.*;
+import cz.muni.csirt.kypo.events.adaptive.trainings.enums.PhaseType;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * The type Audit events service.
+ */
+@Service
+public class AuditEventsService {
+
+    private AuditService auditService;
+
+    /**
+     * Instantiates a new Audit events service.
+     *
+     * @param auditService the audit service
+     */
+    @Autowired
+    public AuditEventsService(AuditService auditService) {
+        this.auditService = auditService;
+    }
+
+
+    /**
+     * Audit training run started action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditTrainingRunStartedAction(TrainingRun trainingRun) {
+        TrainingRunStarted.TrainingRunStartedBuilder<?, ?> trainingRunStartedBuilder = (TrainingRunStarted.TrainingRunStartedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, TrainingRunStarted.builder());
+
+        TrainingRunStarted trainingRunStarted = trainingRunStartedBuilder
+                .trainingTime(0L)
+                .build();
+        auditService.saveTrainingRunEvent(trainingRunStarted, 0L);
+    }
+
+    /**
+     * Audit phase started action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditPhaseStartedAction(TrainingRun trainingRun) {
+        PhaseStarted.PhaseStartedBuilder<?, ?> phaseStartedBuilder = (PhaseStarted.PhaseStartedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, PhaseStarted.builder());
+
+        PhaseStarted phaseStarted = phaseStartedBuilder
+                .phaseType(getPhaseType(trainingRun.getCurrentPhase()))
+                .phaseTitle(trainingRun.getCurrentPhase().getTitle())
+                .build();
+        auditService.saveTrainingRunEvent(phaseStarted, 10L);
+    }
+
+    /**
+     * Audit phase completed action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditPhaseCompletedAction(TrainingRun trainingRun) {
+        PhaseCompleted.PhaseCompletedBuilder<?, ?> phaseCompletedBuilder = (PhaseCompleted.PhaseCompletedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, PhaseCompleted.builder());
+
+        PhaseCompleted phaseCompleted = phaseCompletedBuilder
+                .phaseType(getPhaseType(trainingRun.getCurrentPhase()))
+                .build();
+        auditService.saveTrainingRunEvent(phaseCompleted, 5L);
+    }
+
+    /**
+     * Audit solution displayed action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditSolutionDisplayedAction(TrainingRun trainingRun) {
+        SolutionDisplayed.SolutionDisplayedBuilder<?, ?> solutionDisplayedBuilder = (SolutionDisplayed.SolutionDisplayedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, SolutionDisplayed.builder());
+
+        SolutionDisplayed solutionDisplayed = solutionDisplayedBuilder
+                .build();
+        auditService.saveTrainingRunEvent(solutionDisplayed, 0L);
+    }
+
+    /**
+     * Audit correct answer submitted action.
+     *
+     * @param trainingRun the training run
+     * @param answer      the answer
+     */
+    public void auditCorrectAnswerSubmittedAction(TrainingRun trainingRun, String answer) {
+        CorrectAnswerSubmitted.CorrectAnswerSubmittedBuilder<?, ?> correctAnswerSubmittedBuilder = (CorrectAnswerSubmitted.CorrectAnswerSubmittedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, CorrectAnswerSubmitted.builder());
+
+        CorrectAnswerSubmitted correctAnswerSubmitted = correctAnswerSubmittedBuilder
+                .answerContent(answer)
+                .build();
+        auditService.saveTrainingRunEvent(correctAnswerSubmitted, 0L);
+    }
+
+    /**
+     * Audit wrong answer submitted action.
+     *
+     * @param trainingRun the training run
+     * @param answer      the answer
+     */
+    public void auditWrongAnswerSubmittedAction(TrainingRun trainingRun, String answer) {
+        WrongAnswerSubmitted.WrongAnswerSubmittedBuilder<?, ?> wrongAnswerSubmittedBuilder = (WrongAnswerSubmitted.WrongAnswerSubmittedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, WrongAnswerSubmitted.builder());
+
+        WrongAnswerSubmitted wrongAnswerSubmitted = wrongAnswerSubmittedBuilder
+                .answerContent(answer)
+                .count(trainingRun.getIncorrectAnswerCount())
+                .build();
+        auditService.saveTrainingRunEvent(wrongAnswerSubmitted, 0L);
+    }
+
+    /**
+     * Audit questionnaire answers action.
+     *
+     * @param trainingRun the training run
+     * @param answers     the answers
+     */
+    public void auditQuestionnaireAnswersAction(TrainingRun trainingRun, String answers) {
+        QuestionnaireAnswers.QuestionnaireAnswersBuilder<?, ?> questionnaireAnswersBuilder = (QuestionnaireAnswers.QuestionnaireAnswersBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, QuestionnaireAnswers.builder());
+
+        QuestionnaireAnswers questionnaireAnswers = questionnaireAnswersBuilder
+                .answers(answers)
+                .build();
+        auditService.saveTrainingRunEvent(questionnaireAnswers, 0L);
+    }
+
+    /**
+     * Audit training run ended action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditTrainingRunEndedAction(TrainingRun trainingRun) {
+        TrainingRunEnded.TrainingRunEndedBuilder<?, ?> trainingRunEndedBuilder = (TrainingRunEnded.TrainingRunEndedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, TrainingRunEnded.builder());
+
+        TrainingRunEnded trainingRunEnded = trainingRunEndedBuilder
+                .startTime(trainingRun.getStartTime().atOffset(ZoneOffset.UTC).toInstant().toEpochMilli())
+                .endTime(System.currentTimeMillis())
+                .build();
+        auditService.saveTrainingRunEvent(trainingRunEnded, 10L);
+    }
+
+    /**
+     * Audit training run resumed action.
+     *
+     * @param trainingRun the training run
+     */
+    public void auditTrainingRunResumedAction(TrainingRun trainingRun) {
+        TrainingRunResumed.TrainingRunResumedBuilder<?, ?> trainingRunResumedBuilder = (TrainingRunResumed.TrainingRunResumedBuilder<?, ?>)
+                fillInCommonBuilderFields(trainingRun, TrainingRunResumed.builder());
+        TrainingRunResumed trainingRunResumed = trainingRunResumedBuilder.build();
+        auditService.saveTrainingRunEvent(trainingRunResumed, 0L);
+    }
+
+    private AbstractAuditAdaptivePOJO.AbstractAuditAdaptivePOJOBuilder<?, ?> fillInCommonBuilderFields(TrainingRun trainingRun, AbstractAuditAdaptivePOJO.AbstractAuditAdaptivePOJOBuilder<?, ?> builder) {
+        TrainingInstance trainingInstance = trainingRun.getTrainingInstance();
+        builder.sandboxId(trainingRun.getSandboxInstanceRefId())
+                .poolId(trainingInstance.getPoolId())
+                .trainingRunId(trainingRun.getId())
+                .trainingInstanceId(trainingInstance.getId())
+                .trainingDefinitionId(trainingInstance.getTrainingDefinition().getId())
+                .trainingTime(computeTrainingTime(trainingRun.getStartTime()))
+                .userRefId(trainingRun.getParticipantRef().getUserRefId())
+                .phaseId(trainingRun.getCurrentPhase().getId());
+        return builder;
+    }
+
+    private long computeTrainingTime(LocalDateTime trainingStartedTime) {
+        return ChronoUnit.MILLIS.between(trainingStartedTime, LocalDateTime.now(Clock.systemUTC()));
+    }
+
+    private PhaseType getPhaseType(AbstractPhase abstractPhase) {
+        if (abstractPhase instanceof TrainingPhase) {
+            return PhaseType.TRAINING;
+        } else if (abstractPhase instanceof InfoPhase) {
+            return PhaseType.INFO;
+        } else if (abstractPhase instanceof QuestionnairePhase) {
+            return PhaseType.QUESTIONNAIRE;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e82937138ed8254452a9742fa2f5b95aff7252b
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/audit/AuditService.java
@@ -0,0 +1,54 @@
+package cz.muni.ics.kypo.training.adaptive.service.audit;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import cz.muni.csirt.kypo.events.adaptive.trainings.AbstractAuditAdaptivePOJO;
+import cz.muni.ics.kypo.training.adaptive.exceptions.ElasticsearchTrainingServiceLayerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import java.io.IOException;
+
+/**
+ * The type Audit service.
+ */
+@Service
+public class AuditService {
+
+    private static Logger logger = LoggerFactory.getLogger(AuditService.class);
+
+    private ObjectMapper objectMapper;
+
+    /**
+     * Instantiates a new Audit service.
+     *
+     * @param objectMapper the object mapper
+     */
+    @Autowired
+    public AuditService(@Qualifier("objMapperForElasticsearch") ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Method for saving general class into Elasticsearch under specific index and type.
+     *
+     * @param <T>       the type parameter
+     * @param pojoClass class saving to Elasticsearch
+     * @throws ElasticsearchTrainingServiceLayerException the elasticsearch training service layer exception
+     */
+    public <T extends AbstractAuditAdaptivePOJO> void saveTrainingRunEvent(T pojoClass, long timestampDelay) {
+        Assert.notNull(pojoClass, "Null class could not be saved via audit method.");
+        try {
+            pojoClass.setTimestamp(System.currentTimeMillis() + timestampDelay);
+            pojoClass.setType(pojoClass.getClass().getName());
+
+            logger.info(objectMapper.writeValueAsString(pojoClass));
+        } catch (IOException ex) {
+            throw new ElasticsearchTrainingServiceLayerException(ex);
+        }
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/InfoPhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/InfoPhaseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..59f4f948107a8ad24ea9c55fe3c814e36a218db6
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/InfoPhaseService.java
@@ -0,0 +1,98 @@
+package cz.muni.ics.kypo.training.adaptive.service.phases;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.InfoPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.AbstractPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.InfoPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+
+import static cz.muni.ics.kypo.training.adaptive.service.phases.PhaseService.PHASE_NOT_FOUND;
+import static cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService.ARCHIVED_OR_RELEASED;
+
+@Service
+public class InfoPhaseService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(InfoPhaseService.class);
+
+    private final TrainingInstanceRepository trainingInstanceRepository;
+    private final TrainingDefinitionRepository trainingDefinitionRepository;
+    private final InfoPhaseRepository infoPhaseRepository;
+    private final AbstractPhaseRepository abstractPhaseRepository;
+
+    @Autowired
+    public InfoPhaseService(TrainingDefinitionRepository trainingDefinitionRepository,
+                            TrainingInstanceRepository trainingInstanceRepository,
+                            InfoPhaseRepository infoPhaseRepository,
+                            AbstractPhaseRepository abstractPhaseRepository) {
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.infoPhaseRepository = infoPhaseRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+    }
+
+    public InfoPhase createDefaultInfoPhase(Long trainingDefinitionId) {
+        TrainingDefinition trainingDefinition = findDefinitionById(trainingDefinitionId);
+        checkIfCanBeUpdated(trainingDefinition);
+
+        InfoPhase infoPhase = new InfoPhase();
+        infoPhase.setContent("Content of info phase");
+        infoPhase.setTitle("Title of info phase");
+        infoPhase.setTrainingDefinition(trainingDefinition);
+        infoPhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
+
+        InfoPhase persistedEntity = infoPhaseRepository.save(infoPhase);
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+        return persistedEntity;
+    }
+
+    public InfoPhase updateInfoPhase(Long phaseId, InfoPhase infoPhaseToUpdate) {
+        InfoPhase persistedInfoPhase = findInfoPhaseById(phaseId);
+        TrainingDefinition trainingDefinition = persistedInfoPhase.getTrainingDefinition();
+        checkIfCanBeUpdated(trainingDefinition);
+        infoPhaseToUpdate.setId(phaseId);
+        infoPhaseToUpdate.setTrainingDefinition(trainingDefinition);
+        infoPhaseToUpdate.setOrder(persistedInfoPhase.getOrder());
+
+        return infoPhaseRepository.save(infoPhaseToUpdate);
+    }
+
+    private InfoPhase findInfoPhaseById(Long phaseId) {
+        return infoPhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    private TrainingDefinition findDefinitionById(Long id) {
+        return trainingDefinitionRepository.findById(id)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, id)));
+    }
+
+    private void checkIfCanBeUpdated(TrainingDefinition trainingDefinition) {
+        if (!trainingDefinition.getState().equals(TDState.UNRELEASED)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    ARCHIVED_OR_RELEASED));
+        }
+        if (trainingInstanceRepository.existsAnyForTrainingDefinition(trainingDefinition.getId())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    "Cannot update training definition with already created training instance. " +
+                            "Remove training instance/s before updating training definition."));
+        }
+    }
+
+    private LocalDateTime getCurrentTimeInUTC() {
+        return LocalDateTime.now(Clock.systemUTC());
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/PhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/PhaseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4a733e3b204376d09810a00af25c51775deed7f
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/PhaseService.java
@@ -0,0 +1,110 @@
+package cz.muni.ics.kypo.training.adaptive.service.phases;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.AbstractPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+@Transactional
+public class PhaseService {
+
+    public static final String PHASE_NOT_FOUND = "Phase not found.";
+
+
+    private final AbstractPhaseRepository abstractPhaseRepository;
+    private final TrainingPhaseService trainingPhaseService;
+    private final TrainingDefinitionRepository trainingDefinitionRepository;
+
+    @Autowired
+    public PhaseService(AbstractPhaseRepository abstractPhaseRepository,
+                        TrainingPhaseService trainingPhaseService,
+                        TrainingDefinitionRepository trainingDefinitionRepository) {
+        this.abstractPhaseRepository = abstractPhaseRepository;
+        this.trainingPhaseService = trainingPhaseService;
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+    }
+
+    private TrainingDefinition findDefinitionById(Long id) {
+        return trainingDefinitionRepository.findById(id)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, id)));
+    }
+
+    /**
+     * Deletes specific phase based on id
+     *
+     * @param phaseId - id of phase to be deleted
+     * @return ID of the training definition from which the phase has been deleted.
+     * @throws EntityNotFoundException training definition or phase is not found.
+     * @throws EntityConflictException phase cannot be deleted in released or archived training definition.
+     */
+    public Long deletePhase(Long phaseId) {
+        AbstractPhase phaseToDelete = this.getPhase(phaseId);
+        TrainingDefinition trainingDefinition = phaseToDelete.getTrainingDefinition();
+        if (!trainingDefinition.getState().equals(TDState.UNRELEASED)) {
+            throw new EntityConflictException(new EntityErrorDetail(AbstractPhase.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(), TrainingDefinitionService.ARCHIVED_OR_RELEASED));
+        }
+
+        abstractPhaseRepository.delete(phaseToDelete);
+        int phaseOrder = phaseToDelete.getOrder();
+        abstractPhaseRepository.decreaseOrderAfterPhaseWasDeleted(trainingDefinition.getId(), phaseOrder);
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+        return trainingDefinition.getId();
+    }
+
+    public AbstractPhase getPhase(Long phaseId) {
+        return abstractPhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    public List<AbstractPhase> getPhases(Long trainingDefinitionId) {
+        return abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingDefinitionId);
+    }
+
+    /**
+     * Move phase to the different position and modify orders of phase between moved phase and new position.
+     *
+     * @param phaseIdFrom - id of the phase to be moved to the new position
+     * @param newPosition - position where phase will be moved
+     * @throws EntityNotFoundException training definition or one of the phase is not found.
+     * @throws EntityConflictException released or archived training definition cannot be modified.
+     */
+    public void movePhaseToSpecifiedOrder(Long phaseIdFrom, int newPosition) {
+        AbstractPhase phaseFrom = abstractPhaseRepository.findById(phaseIdFrom)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseIdFrom.getClass(), phaseIdFrom, PHASE_NOT_FOUND)));
+        Integer maxOrderOfPhase = abstractPhaseRepository.getCurrentMaxOrder(phaseFrom.getTrainingDefinition().getId());
+        if (newPosition < 0) {
+            newPosition = 0;
+        } else if (newPosition > maxOrderOfPhase) {
+            newPosition = maxOrderOfPhase;
+        }
+        int fromOrder = phaseFrom.getOrder();
+        if (fromOrder == newPosition) {
+            return;
+        } else if (fromOrder > newPosition) {
+            abstractPhaseRepository.increaseOrderOfPhasesOnInterval(phaseFrom.getTrainingDefinition().getId(), newPosition, fromOrder);
+        } else {
+            abstractPhaseRepository.decreaseOrderOfPhasesOnInterval(phaseFrom.getTrainingDefinition().getId(), fromOrder, newPosition);
+        }
+        phaseFrom.setOrder(newPosition);
+        abstractPhaseRepository.save(phaseFrom);
+        trainingPhaseService.alignDecisionMatrixForPhasesInTrainingDefinition(phaseFrom.getTrainingDefinition().getId());
+    }
+
+    private LocalDateTime getCurrentTimeInUTC() {
+        return LocalDateTime.now(Clock.systemUTC());
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/QuestionnairePhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/QuestionnairePhaseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d6b57a2a5713f09776a98cb8bd6c0b2c7643fc4
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/QuestionnairePhaseService.java
@@ -0,0 +1,156 @@
+package cz.muni.ics.kypo.training.adaptive.service.phases;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.QuestionnairePhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.enums.QuestionnaireType;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.BadRequestException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.*;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import static cz.muni.ics.kypo.training.adaptive.service.phases.PhaseService.PHASE_NOT_FOUND;
+import static cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService.ARCHIVED_OR_RELEASED;
+
+@Service
+public class QuestionnairePhaseService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(QuestionnairePhaseService.class);
+
+    private final TrainingDefinitionRepository trainingDefinitionRepository;
+    private final TrainingInstanceRepository trainingInstanceRepository;
+    private final QuestionnairePhaseRepository questionnairePhaseRepository;
+    private final AbstractPhaseRepository abstractPhaseRepository;
+    private final QuestionRepository questionRepository;
+    private final TrainingPhaseRepository trainingPhaseRepository;
+    private final QuestionPhaseRelationRepository questionPhaseRelationRepository;
+
+    @Autowired
+    public QuestionnairePhaseService(TrainingDefinitionRepository trainingDefinitionRepository,
+                                     TrainingInstanceRepository trainingInstanceRepository,
+                                     QuestionnairePhaseRepository questionnairePhaseRepository,
+                                     AbstractPhaseRepository abstractPhaseRepository,
+                                     QuestionRepository questionRepository,
+                                     TrainingPhaseRepository trainingPhaseRepository,
+                                     QuestionPhaseRelationRepository questionPhaseRelationRepository) {
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.questionnairePhaseRepository = questionnairePhaseRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+        this.questionRepository = questionRepository;
+        this.trainingPhaseRepository = trainingPhaseRepository;
+        this.questionPhaseRelationRepository = questionPhaseRelationRepository;
+    }
+
+    public QuestionnairePhase createDefaultQuestionnairePhase(Long trainingDefinitionId, QuestionnaireType questionnaireType) {
+        TrainingDefinition trainingDefinition = findDefinitionById(trainingDefinitionId);
+        checkIfCanBeUpdated(trainingDefinition);
+
+        QuestionnairePhase questionnairePhase = new QuestionnairePhase();
+        questionnairePhase.setTitle("Title of questionnaire phase");
+        questionnairePhase.setTrainingDefinition(trainingDefinition);
+        questionnairePhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
+        questionnairePhase.setQuestionnaireType(questionnaireType);
+        QuestionnairePhase persistedEntity = questionnairePhaseRepository.save(questionnairePhase);
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+        return persistedEntity;
+    }
+
+    public QuestionnairePhase updateQuestionnairePhase(Long phaseId, QuestionnairePhase questionnairePhaseToUpdate) {
+        QuestionnairePhase persistedQuestionnairePhase = findQuestionnairePhaseById(phaseId);
+        checkIfCanBeUpdated(persistedQuestionnairePhase.getTrainingDefinition());
+
+        questionnairePhaseToUpdate.setId(phaseId);
+        questionnairePhaseToUpdate.setTrainingDefinition(persistedQuestionnairePhase.getTrainingDefinition());
+        questionnairePhaseToUpdate.setOrder(persistedQuestionnairePhase.getOrder());
+        questionnairePhaseToUpdate.setQuestionnaireType(persistedQuestionnairePhase.getQuestionnaireType());
+        questionnairePhaseToUpdate.setQuestionPhaseRelations(persistedQuestionnairePhase.getQuestionPhaseRelations());
+
+        if (!CollectionUtils.isEmpty(questionnairePhaseToUpdate.getQuestions())) {
+            questionnairePhaseToUpdate.getQuestions().forEach(question -> {
+                question.setQuestionnairePhase(questionnairePhaseToUpdate);
+                question.getChoices().forEach(questionChoice -> questionChoice.setQuestion(question));
+            });
+        }
+        return questionnairePhaseRepository.save(questionnairePhaseToUpdate);
+    }
+
+    public QuestionPhaseRelation updateQuestionPhaseRelation(QuestionnairePhase questionnairePhase,
+                                                             QuestionPhaseRelation questionPhaseRelationToUpdate,
+                                                             Set<Long> questionIds,
+                                                             Long relatedTrainingPhaseId) {
+        Set<Question> questionsInPhaseRelation = new HashSet<>(Set.copyOf(questionRepository.findAllById(questionIds)));
+
+        QuestionPhaseRelation questionPhaseRelation;
+        if (Objects.isNull(questionPhaseRelationToUpdate.getId())) {
+            questionPhaseRelation = new QuestionPhaseRelation();
+        } else {
+            questionPhaseRelation = this.findQuestionPhaseRelationById(questionPhaseRelationToUpdate.getId());
+            if (!questionPhaseRelation.getQuestionnairePhase().getId().equals(questionnairePhase.getId())) {
+                throw new BadRequestException("The question phase relation (ID: " + questionPhaseRelationToUpdate.getId() + ") is not associated with the" +
+                        " questionnaire phase (ID: " + questionnairePhase.getId() + ".");
+            }
+        }
+        TrainingPhase trainingPhase = this.findTrainingPhaseById(relatedTrainingPhaseId);
+        questionPhaseRelation.setQuestions(questionsInPhaseRelation);
+        questionPhaseRelation.setOrder(questionPhaseRelationToUpdate.getOrder());
+        questionPhaseRelation.setSuccessRate(questionPhaseRelationToUpdate.getSuccessRate());
+        questionPhaseRelation.setRelatedTrainingPhase(trainingPhase);
+        questionPhaseRelation.setQuestionnairePhase(questionnairePhase);
+        return questionPhaseRelationRepository.save(questionPhaseRelation);
+    }
+
+    private QuestionPhaseRelation findQuestionPhaseRelationById(Long questionPhaseRelation) {
+        return questionPhaseRelationRepository.findById(questionPhaseRelation)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(QuestionPhaseRelation.class, "id", questionPhaseRelation.getClass(), questionPhaseRelation, "Question phase relation not found.")));
+    }
+
+    private QuestionnairePhase findQuestionnairePhaseById(Long phaseId) {
+        return questionnairePhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(QuestionnairePhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    private TrainingPhase findTrainingPhaseById(Long phaseId) {
+        return trainingPhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    private TrainingDefinition findDefinitionById(Long id) {
+        return trainingDefinitionRepository.findById(id)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, id)));
+    }
+
+    private void checkIfCanBeUpdated(TrainingDefinition trainingDefinition) {
+        if (!trainingDefinition.getState().equals(TDState.UNRELEASED)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    ARCHIVED_OR_RELEASED));
+        }
+        if (trainingInstanceRepository.existsAnyForTrainingDefinition(trainingDefinition.getId())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    "Cannot update training definition with already created training instance. " +
+                            "Remove training instance/s before updating training definition."));
+        }
+    }
+
+    private LocalDateTime getCurrentTimeInUTC() {
+        return LocalDateTime.now(Clock.systemUTC());
+    }
+
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TaskService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TaskService.java
new file mode 100644
index 0000000000000000000000000000000000000000..344f6792b4700f19dc27bfae5cbfa758694a19f8
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TaskService.java
@@ -0,0 +1,77 @@
+package cz.muni.ics.kypo.training.adaptive.service.phases;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.Task;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.TaskRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TaskService {
+
+    public static final String TASK_NOT_FOUND = "Task not found.";
+    private static final Logger LOG = LoggerFactory.getLogger(TaskService.class);
+    private final TaskRepository taskRepository;
+
+    @Autowired
+    public TaskService(TaskRepository taskRepository) {
+        this.taskRepository = taskRepository;
+    }
+
+    public Task createDefaultTask(TrainingPhase trainingPhase) {
+        Task task = new Task();
+        task.setTitle("Title of a new task");
+        task.setTrainingPhase(trainingPhase);
+        task.setOrder(taskRepository.getCurrentMaxOrder(trainingPhase.getId()) + 1);
+        task.setAnswer("Secret flag");
+        task.setContent("Task content ...");
+        task.setSolution("Task solution ...");
+        task.setIncorrectAnswerLimit(1);
+        return taskRepository.save(task);
+    }
+
+    public Task createTask(Task task) {
+        task.setOrder(taskRepository.getCurrentMaxOrder(task.getTrainingPhase().getId()) + 1);
+        return taskRepository.save(task);
+    }
+
+    public Task getTask(Long taskId) {
+        return taskRepository.findById(taskId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", taskId.getClass(), taskId, TASK_NOT_FOUND)));
+    }
+
+    public Task updateTask(Long taskId, Task updatedTask) {
+        Task persistedTask = this.getTask(taskId);
+        updatedTask.setId(taskId);
+        updatedTask.setTrainingPhase(persistedTask.getTrainingPhase());
+        updatedTask.setOrder(persistedTask.getOrder());
+        return taskRepository.save(updatedTask);
+    }
+
+    public void removeTask(Task task) {
+        taskRepository.decreaseOrderAfterTaskWasDeleted(task.getTrainingPhase().getId(), task.getOrder());
+        taskRepository.delete(task);
+    }
+
+    public void moveTaskToSpecifiedOrder(Long taskIdFrom, int newPosition) {
+        Task task = this.getTask(taskIdFrom);
+        int fromOrder = task.getOrder();
+
+        if (fromOrder < newPosition) {
+            taskRepository.decreaseOrderOfTasksOnInterval(task.getTrainingPhase().getId(), fromOrder, newPosition);
+        } else if (fromOrder > newPosition) {
+            taskRepository.increaseOrderOfTasksOnInterval(task.getTrainingPhase().getId(), newPosition, fromOrder);
+        } else {
+            // nothing should be changed, no further actions needed
+            return;
+        }
+
+        task.setOrder(newPosition);
+        taskRepository.save(task);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TrainingPhaseService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TrainingPhaseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..bccfe06a8018740d6db12c388a1e21e668c08e38
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/phases/TrainingPhaseService.java
@@ -0,0 +1,193 @@
+package cz.muni.ics.kypo.training.adaptive.service.phases;
+
+import cz.muni.ics.kypo.training.adaptive.domain.phase.AbstractPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.DecisionMatrixRow;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.TrainingPhase;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.AbstractPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.TrainingPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cz.muni.ics.kypo.training.adaptive.service.phases.PhaseService.PHASE_NOT_FOUND;
+import static cz.muni.ics.kypo.training.adaptive.service.training.TrainingDefinitionService.ARCHIVED_OR_RELEASED;
+
+@Service
+@Transactional
+public class TrainingPhaseService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrainingPhaseService.class);
+
+    private final TrainingPhaseRepository trainingPhaseRepository;
+    private final TrainingInstanceRepository trainingInstanceRepository;
+    private final AbstractPhaseRepository abstractPhaseRepository;
+    private final TrainingDefinitionRepository trainingDefinitionRepository;
+
+    @Autowired
+    public TrainingPhaseService(TrainingDefinitionRepository trainingDefinitionRepository,
+                                TrainingInstanceRepository trainingInstanceRepository,
+                                TrainingPhaseRepository trainingPhaseRepository,
+                                AbstractPhaseRepository abstractPhaseRepository) {
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.trainingPhaseRepository = trainingPhaseRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+    }
+
+    public TrainingPhase createDefaultTrainingPhase(Long trainingDefinitionId) {
+        TrainingDefinition trainingDefinition = findDefinitionById(trainingDefinitionId);
+        checkIfCanBeUpdated(trainingDefinition);
+
+        TrainingPhase trainingPhase = new TrainingPhase();
+        trainingPhase.setTitle("Title of training phase");
+        trainingPhase.setTrainingDefinition(trainingDefinition);
+        trainingPhase.setOrder(abstractPhaseRepository.getCurrentMaxOrder(trainingDefinitionId) + 1);
+        trainingPhase.setDecisionMatrix(prepareDefaultDecisionMatrix(trainingDefinitionId, trainingPhase));
+        TrainingPhase persistedEntity = trainingPhaseRepository.save(trainingPhase);
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+        return persistedEntity;
+    }
+
+
+    /**
+     * Updates training phase in training definition
+     *
+     * @param phaseId               - id of the phase to be updated
+     * @param trainingPhaseToUpdate phase to be updated
+     * @throws EntityNotFoundException training definition is not found.
+     * @throws EntityConflictException phase cannot be updated in released or archived training definition.
+     */
+    public TrainingPhase updateTrainingPhase(Long phaseId, TrainingPhase trainingPhaseToUpdate) {
+        TrainingPhase persistedTrainingPhase = findPhaseById(phaseId);
+        TrainingDefinition trainingDefinition = persistedTrainingPhase.getTrainingDefinition();
+        checkIfCanBeUpdated(trainingDefinition);
+        trainingPhaseToUpdate.setId(phaseId);
+        trainingPhaseToUpdate.setTrainingDefinition(persistedTrainingPhase.getTrainingDefinition());
+        trainingPhaseToUpdate.setOrder(persistedTrainingPhase.getOrder());
+        trainingPhaseToUpdate.setTasks(persistedTrainingPhase.getTasks());
+        if (!CollectionUtils.isEmpty(trainingPhaseToUpdate.getDecisionMatrix())) {
+            Set<Long> persistedMatrixRows = persistedTrainingPhase.getDecisionMatrix()
+                    .stream()
+                    .map(DecisionMatrixRow::getId)
+                    .collect(Collectors.toSet());
+            trainingPhaseToUpdate.getDecisionMatrix()
+                    .stream()
+                    .filter(matrixRow -> matrixRow.getId() == null || persistedMatrixRows.contains(matrixRow.getId()))
+                    .forEach(matrixRow -> matrixRow.setTrainingPhase(trainingPhaseToUpdate));
+        }
+        return trainingPhaseRepository.save(trainingPhaseToUpdate);
+    }
+
+    public TrainingPhase findPhaseById(Long phaseId) {
+        return trainingPhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    public void alignDecisionMatrixForPhasesInTrainingDefinition(Long trainingDefinitionId) {
+        List<TrainingPhase> trainingPhases = trainingPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingDefinitionId);
+
+        int currentPhaseOrder = 0;
+        for (TrainingPhase trainingPhase : trainingPhases) {
+            alignDecisionMatrixForPhase(trainingPhase, currentPhaseOrder);
+            currentPhaseOrder++;
+        }
+    }
+
+    private List<DecisionMatrixRow> prepareDefaultDecisionMatrix(Long trainingDefinitionId, TrainingPhase trainingPhase) {
+        List<DecisionMatrixRow> result = new ArrayList<>();
+
+        int numberOfExistingPhases = trainingPhaseRepository.getNumberOfExistingPhases(trainingDefinitionId);
+
+        // number of rows should be equal to number of existing phase + 1
+        for (int i = 0; i <= numberOfExistingPhases; i++) {
+            DecisionMatrixRow decisionMatrixRow = new DecisionMatrixRow();
+            decisionMatrixRow.setTrainingPhase(trainingPhase);
+            decisionMatrixRow.setOrder(i);
+
+            result.add(decisionMatrixRow);
+        }
+        return result;
+    }
+
+    private void alignDecisionMatrixForPhase(TrainingPhase trainingPhase, int currentPhaseOrder) {
+        if (Objects.isNull(trainingPhase)) {
+            return;
+        }
+
+        int numberOfRows = 0;
+        if (!CollectionUtils.isEmpty(trainingPhase.getDecisionMatrix())) {
+            numberOfRows = trainingPhase.getDecisionMatrix().size();
+        }
+
+        final int expectedNumberOfRows = currentPhaseOrder + 1;
+        if (numberOfRows == expectedNumberOfRows) {
+            return;
+        } else if (numberOfRows < expectedNumberOfRows) {
+            List<DecisionMatrixRow> newDecisionMatrixRows = getNewDecisionMatrixRows(numberOfRows, expectedNumberOfRows, trainingPhase);
+            trainingPhase.getDecisionMatrix().addAll(newDecisionMatrixRows);
+        } else {
+            List<DecisionMatrixRow> neededDecisionMatrixRows = getOnlyNeededDecisionMatrixRows(expectedNumberOfRows, trainingPhase);
+            trainingPhase.getDecisionMatrix().clear();
+            trainingPhase.getDecisionMatrix().addAll(neededDecisionMatrixRows);
+        }
+
+        trainingPhaseRepository.save(trainingPhase);
+    }
+
+    private List<DecisionMatrixRow> getNewDecisionMatrixRows(int currentNumberOfNewRows, int expectedNumberOfRows, TrainingPhase trainingPhase) {
+        List<DecisionMatrixRow> result = new ArrayList<>();
+        for (int i = currentNumberOfNewRows; i < expectedNumberOfRows; i++) {
+            DecisionMatrixRow decisionMatrixRow = new DecisionMatrixRow();
+            decisionMatrixRow.setTrainingPhase(trainingPhase);
+            decisionMatrixRow.setOrder(i);
+
+            result.add(decisionMatrixRow);
+        }
+        return result;
+    }
+
+    private List<DecisionMatrixRow> getOnlyNeededDecisionMatrixRows(int expectedNumberOfRows, TrainingPhase trainingPhase) {
+        List<DecisionMatrixRow> decisionMatrix = trainingPhase.getDecisionMatrix();
+        return decisionMatrix.stream()
+                .sorted(Comparator.comparingInt(DecisionMatrixRow::getOrder))
+                .limit(expectedNumberOfRows)
+                .collect(Collectors.toList());
+
+    }
+
+    private TrainingDefinition findDefinitionById(Long id) {
+        return trainingDefinitionRepository.findById(id)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, id)));
+    }
+
+    private void checkIfCanBeUpdated(TrainingDefinition trainingDefinition) {
+        if (!trainingDefinition.getState().equals(TDState.UNRELEASED)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    ARCHIVED_OR_RELEASED));
+        }
+        if (trainingInstanceRepository.existsAnyForTrainingDefinition(trainingDefinition.getId())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    "Cannot update training definition with already created training instance. " +
+                            "Remove training instance/s before updating training definition."));
+        }
+    }
+
+    private LocalDateTime getCurrentTimeInUTC() {
+        return LocalDateTime.now(Clock.systemUTC());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingDefinitionService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingDefinitionService.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ab5b96c7cc4095f68de642993eaf6b2c72d4eb7
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingDefinitionService.java
@@ -0,0 +1,433 @@
+package cz.muni.ics.kypo.training.adaptive.service.training;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.*;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.Question;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionChoice;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.questions.QuestionPhaseRelation;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.enums.TDState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.mapping.mapstruct.CloneMapper;
+import cz.muni.ics.kypo.training.adaptive.repository.UserRefRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.*;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingDefinitionRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * The type Training definition service.
+ */
+@Service
+public class TrainingDefinitionService {
+
+    public static final String ARCHIVED_OR_RELEASED = "Cannot edit released or archived training definition.";
+    private static final Logger LOG = LoggerFactory.getLogger(TrainingDefinitionService.class);
+    private static final String PHASE_NOT_FOUND = "Phase not found.";
+    private TrainingDefinitionRepository trainingDefinitionRepository;
+    private TrainingInstanceRepository trainingInstanceRepository;
+    private AbstractPhaseRepository abstractPhaseRepository;
+    private TrainingPhaseRepository trainingPhaseRepository;
+    private InfoPhaseRepository infoPhaseRepository;
+    private QuestionnairePhaseRepository questionnairePhaseRepository;
+    private UserRefRepository userRefRepository;
+    private UserManagementServiceApi userManagementServiceApi;
+    private CloneMapper cloneMapper;
+
+    /**
+     * Instantiates a new Training definition service.
+     *
+     * @param trainingDefinitionRepository the training definition repository
+     * @param abstractPhaseRepository      the abstract phase repository
+     * @param infoPhaseRepository          the info phase repository
+     * @param trainingPhaseRepository      the training phase repository
+     * @param questionnairePhaseRepository the questionnaire phase repository
+     * @param trainingInstanceRepository   the training instance repository
+     * @param userRefRepository            the user ref repository
+     * @param userManagementServiceApi              the security service
+     */
+    @Autowired
+    public TrainingDefinitionService(TrainingDefinitionRepository trainingDefinitionRepository,
+                                     AbstractPhaseRepository abstractPhaseRepository,
+                                     InfoPhaseRepository infoPhaseRepository,
+                                     TrainingPhaseRepository trainingPhaseRepository,
+                                     QuestionnairePhaseRepository questionnairePhaseRepository,
+                                     TrainingInstanceRepository trainingInstanceRepository,
+                                     UserRefRepository userRefRepository,
+                                     UserManagementServiceApi userManagementServiceApi,
+                                     CloneMapper cloneMapper) {
+        this.trainingDefinitionRepository = trainingDefinitionRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+        this.trainingPhaseRepository = trainingPhaseRepository;
+        this.infoPhaseRepository = infoPhaseRepository;
+        this.questionnairePhaseRepository = questionnairePhaseRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.userRefRepository = userRefRepository;
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.cloneMapper = cloneMapper;
+    }
+
+    /**
+     * Finds specific Training Definition by id
+     *
+     * @param id of a Training Definition that would be returned
+     * @return specific {@link TrainingDefinition} by id
+     * @throws EntityNotFoundException training definition cannot be found
+     */
+    public TrainingDefinition findById(Long id) {
+        return trainingDefinitionRepository.findById(id)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, id)));
+    }
+
+    /**
+     * Find all Training Definitions by author if user is designer or all Training Definitions if user is admin.
+     *
+     * @param predicate represents a predicate (boolean-valued function) of one argument.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return all {@link TrainingDefinition}s
+     */
+    public Page<TrainingDefinition> findAll(Predicate predicate, Pageable pageable) {
+        return trainingDefinitionRepository.findAll(predicate, pageable);
+    }
+
+    /**
+     * Find all page.
+     *
+     * @param predicate      the predicate
+     * @param pageable       the pageable
+     * @param loggedInUserId the logged in user id
+     * @return the page
+     */
+    public Page<TrainingDefinition> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId) {
+        return trainingDefinitionRepository.findAll(predicate, pageable, loggedInUserId);
+    }
+
+    /**
+     * Finds all Training Definitions accessible to users with the role of organizer.
+     *
+     * @param state    represents a state of training definition if it is released or unreleased.
+     * @param pageable pageable parameter with information about pagination.
+     * @return all Training Definitions for organizers
+     */
+    public Page<TrainingDefinition> findAllForOrganizers(TDState state, Pageable pageable) {
+        return trainingDefinitionRepository.findAllForOrganizers(state, pageable);
+    }
+
+    /**
+     * Find all for designers and organizers unreleased page.
+     *
+     * @param loggedInUserId the logged in user id
+     * @param pageable       the pageable
+     * @return the page
+     */
+    public Page<TrainingDefinition> findAllForDesigner(Long loggedInUserId, Pageable pageable) {
+        return trainingDefinitionRepository.findAllForDesigner(loggedInUserId, pageable);
+    }
+
+    /**
+     * creates new training definition
+     *
+     * @param trainingDefinition to be created
+     * @return new {@link TrainingDefinition}
+     */
+    public TrainingDefinition create(TrainingDefinition trainingDefinition) {
+        addLoggedInUserToTrainingDefinitionAsAuthor(trainingDefinition);
+        LOG.info("Training definition with id: {} created.", trainingDefinition.getId());
+        return trainingDefinitionRepository.save(trainingDefinition);
+    }
+
+    /**
+     * Updates given Training Definition
+     *
+     * @param trainingDefinitionToUpdate to be updated
+     * @throws EntityNotFoundException training definition or one of the phase is not found.
+     * @throws EntityConflictException released or archived training definition cannot be modified.
+     */
+    public void update(TrainingDefinition trainingDefinitionToUpdate) {
+        TrainingDefinition trainingDefinition = findById(trainingDefinitionToUpdate.getId());
+        checkIfCanBeUpdated(trainingDefinition);
+        addLoggedInUserToTrainingDefinitionAsAuthor(trainingDefinitionToUpdate);
+        trainingDefinitionToUpdate.setEstimatedDuration(trainingDefinition.getEstimatedDuration());
+        trainingDefinitionRepository.save(trainingDefinitionToUpdate);
+        LOG.info("Training definition with id: {} updated.", trainingDefinitionToUpdate.getId());
+    }
+
+    /**
+     * Creates new training definition by cloning existing one
+     *
+     * @param id    of definition to be cloned
+     * @param title the title of the new cloned definition
+     * @return cloned {@link TrainingDefinition}
+     * @throws EntityNotFoundException training definition not found.
+     * @throws EntityConflictException cannot clone unreleased training definition.
+     */
+    public TrainingDefinition clone(Long id, String title) {
+        TrainingDefinition trainingDefinition = findById(id);
+        TrainingDefinition clonedTrainingDefinition = cloneMapper.clone(trainingDefinition);
+        clonedTrainingDefinition.setTitle(title);
+        addLoggedInUserToTrainingDefinitionAsAuthor(clonedTrainingDefinition);
+        clonedTrainingDefinition = trainingDefinitionRepository.save(clonedTrainingDefinition);
+
+        clonePhasesFromTrainingDefinition(trainingDefinition.getId(), clonedTrainingDefinition);
+
+        LOG.info("Training definition with id: {} cloned.", trainingDefinition.getId());
+        return clonedTrainingDefinition;
+    }
+
+    /**
+     * Deletes specific training definition based on id
+     *
+     * @param definitionId of definition to be deleted
+     * @throws EntityNotFoundException training definition or phase is not found.
+     * @throws EntityConflictException released training definition cannot be deleted.
+     */
+    public void delete(Long definitionId) {
+        TrainingDefinition definition = findById(definitionId);
+        if (definition.getState().equals(TDState.RELEASED))
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", definitionId.getClass(), definitionId,
+                    "Cannot delete released training definition."));
+        if (trainingInstanceRepository.existsAnyForTrainingDefinition(definitionId)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", definitionId.getClass(), definitionId,
+                    "Cannot delete training definition with already created training instance. " +
+                            "Remove training instance/s before deleting training definition."));
+        }
+        List<AbstractPhase> abstractPhases = abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(definitionId);
+        abstractPhases.forEach(this::deletePhase);
+        trainingDefinitionRepository.delete(definition);
+    }
+
+
+    /**
+     * Finds all phase from single definition
+     *
+     * @param definitionId of definition
+     * @return list of {@link AbstractPhase} associated with training definition
+     */
+    public List<AbstractPhase> findAllPhasesFromDefinition(Long definitionId) {
+        return abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(definitionId);
+    }
+
+    /**
+     * Finds specific phase by id with associated training definition
+     *
+     * @param phaseId - id of wanted phase
+     * @return wanted {@link AbstractPhase}
+     * @throws EntityNotFoundException phase is not found.
+     */
+    public AbstractPhase findPhaseByIdWithDefinition(Long phaseId) {
+        return abstractPhaseRepository.findByIdWithDefinition(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    /**
+     * Finds specific phase by id
+     *
+     * @param phaseId - id of wanted phase
+     * @return wanted {@link AbstractPhase}
+     * @throws EntityNotFoundException phase is not found.
+     */
+    private AbstractPhase findPhaseById(Long phaseId) {
+        return abstractPhaseRepository.findById(phaseId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "id", phaseId.getClass(), phaseId, PHASE_NOT_FOUND)));
+    }
+
+    /**
+     * Find all training instances associated with training definition by id.
+     *
+     * @param id the id of training definition
+     * @return the list of all {@link TrainingInstance}s associated with wanted {@link TrainingDefinition}
+     */
+    public List<TrainingInstance> findAllTrainingInstancesByTrainingDefinitionId(Long id) {
+        return trainingInstanceRepository.findAllByTrainingDefinitionId(id);
+    }
+
+    /**
+     * Switch development state of definition from unreleased to released, or from released to archived or back to unreleased.
+     *
+     * @param definitionId - id of training definition
+     * @param state        - new state of training definition
+     */
+    public void switchState(Long definitionId, TDState state) {
+        TrainingDefinition trainingDefinition = findById(definitionId);
+        if (trainingDefinition.getState().name().equals(state.name())) {
+            return;
+        }
+        switch (trainingDefinition.getState()) {
+            case UNRELEASED:
+                if (state.equals(TDState.RELEASED))
+                    trainingDefinition.setState(TDState.RELEASED);
+                else
+                    throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", definitionId.getClass(), definitionId,
+                            "Cannot switch from" + trainingDefinition.getState() + " to " + state));
+                break;
+            case RELEASED:
+                if (state.equals(TDState.ARCHIVED))
+                    trainingDefinition.setState(TDState.ARCHIVED);
+                else if (state.equals(TDState.UNRELEASED)) {
+                    if (trainingInstanceRepository.existsAnyForTrainingDefinition(definitionId)) {
+                        throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", definitionId.getClass(), definitionId,
+                                "Cannot update training definition with already created training instance(s). " +
+                                        "Remove training instance(s) before changing the state from released to unreleased training definition."));
+                    }
+                    trainingDefinition.setState((TDState.UNRELEASED));
+                }
+                break;
+            default:
+                throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", definitionId.getClass(), definitionId,
+                        "Cannot switch from " + trainingDefinition.getState() + " to " + state));
+        }
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+    }
+
+    private void clonePhasesFromTrainingDefinition(Long trainingDefinitionId, TrainingDefinition clonedTrainingDefinition) {
+        List<AbstractPhase> phases = abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingDefinitionId);
+        if (phases == null || phases.isEmpty()) {
+            return;
+        }
+        Map<Long, TrainingPhase> clonedTrainingPhases = new HashMap<>();
+        phases.forEach(phase -> {
+            if (phase instanceof InfoPhase) {
+                cloneInfoPhase((InfoPhase) phase, clonedTrainingDefinition);
+            }
+            if (phase instanceof TrainingPhase) {
+                TrainingPhase clonedTrainingPhase = cloneTrainingPhase((TrainingPhase) phase, clonedTrainingDefinition);
+                clonedTrainingPhases.put(phase.getId(), clonedTrainingPhase);
+            }
+        });
+        phases.forEach(phase -> {
+            if (phase instanceof QuestionnairePhase) {
+                cloneQuestionnairePhase((QuestionnairePhase) phase, clonedTrainingDefinition, clonedTrainingPhases);
+            }
+        });
+    }
+
+    private void cloneInfoPhase(InfoPhase infoPhase, TrainingDefinition trainingDefinition) {
+        InfoPhase clonedInfoPhase = cloneMapper.clone(infoPhase);
+        clonedInfoPhase.setTrainingDefinition(trainingDefinition);
+        infoPhaseRepository.save(clonedInfoPhase);
+    }
+
+    private TrainingPhase cloneTrainingPhase(TrainingPhase trainingPhase, TrainingDefinition trainingDefinition) {
+        TrainingPhase clonedTrainingPhase = cloneMapper.clone(trainingPhase);
+        clonedTrainingPhase.setDecisionMatrix(trainingPhase.getDecisionMatrix()
+                .stream()
+                .map(matrix -> cloneDecisionMatrixRow(matrix, clonedTrainingPhase))
+                .collect(Collectors.toList()));
+        clonedTrainingPhase.setTasks(trainingPhase.getTasks()
+                .stream()
+                .map(task -> cloneTask(task, clonedTrainingPhase))
+                .collect(Collectors.toList()));
+        clonedTrainingPhase.setTrainingDefinition(trainingDefinition);
+        return trainingPhaseRepository.save(clonedTrainingPhase);
+    }
+
+    private DecisionMatrixRow cloneDecisionMatrixRow(DecisionMatrixRow decisionMatrixRow, TrainingPhase trainingPhase) {
+        DecisionMatrixRow clonedDecisionMatrixRow = cloneMapper.clone(decisionMatrixRow);
+        clonedDecisionMatrixRow.setTrainingPhase(trainingPhase);
+        return clonedDecisionMatrixRow;
+    }
+
+    private Task cloneTask(Task originalTask, TrainingPhase trainingPhase) {
+        Task clonedTask = cloneMapper.clone(originalTask);
+        clonedTask.setTrainingPhase(trainingPhase);
+        return clonedTask;
+    }
+
+    private void cloneQuestionnairePhase(QuestionnairePhase questionnairePhase,
+                                         TrainingDefinition trainingDefinition,
+                                         Map<Long, TrainingPhase> clonedTrainingPhases) {
+        QuestionnairePhase clonedQuestionnairePhase = cloneMapper.clone(questionnairePhase);
+        clonedQuestionnairePhase.setTrainingDefinition(trainingDefinition);
+        Map<Long, Question> clonedQuestions = questionnairePhase.getQuestions()
+                .stream()
+                .collect(Collectors.toMap(Question::getId, question -> this.cloneQuestion(question, clonedQuestionnairePhase)));
+        clonedQuestionnairePhase.setQuestions(new ArrayList<>(clonedQuestions.values()));
+        clonedQuestionnairePhase.setQuestionPhaseRelations(questionnairePhase.getQuestionPhaseRelations()
+                .stream()
+                .map(questionPhaseRelation -> this.cloneQuestionPhaseRelation(questionPhaseRelation, clonedQuestionnairePhase, clonedTrainingPhases, clonedQuestions))
+                .collect(Collectors.toList()));
+        questionnairePhaseRepository.save(clonedQuestionnairePhase);
+    }
+
+    private Question cloneQuestion(Question question, QuestionnairePhase clonedQuestionnairePhase) {
+        Question clonedQuestion = cloneMapper.clone(question);
+        clonedQuestion.setQuestionnairePhase(clonedQuestionnairePhase);
+        clonedQuestion.setChoices(question.getChoices()
+                .stream()
+                .map(choice -> this.cloneQuestionChoice(choice, clonedQuestion))
+                .collect(Collectors.toList()));
+        return clonedQuestion;
+    }
+
+    private QuestionChoice cloneQuestionChoice(QuestionChoice choice, Question clonedQuestion) {
+        QuestionChoice clonedChoice = cloneMapper.clone(choice);
+        clonedChoice.setQuestion(clonedQuestion);
+        return clonedChoice;
+    }
+
+    private QuestionPhaseRelation cloneQuestionPhaseRelation(QuestionPhaseRelation questionPhaseRelation,
+                                                             QuestionnairePhase clonedQuestionnairePhase,
+                                                             Map<Long, TrainingPhase> clonedTrainingPhases,
+                                                             Map<Long, Question> clonedQuestions) {
+        QuestionPhaseRelation clonedQuestionPhaseRelation = cloneMapper.clone(questionPhaseRelation);
+        clonedQuestionPhaseRelation.setQuestionnairePhase(clonedQuestionnairePhase);
+        clonedQuestionPhaseRelation.setRelatedTrainingPhase(clonedTrainingPhases.get(questionPhaseRelation.getRelatedTrainingPhase().getId()));
+        clonedQuestionPhaseRelation.setQuestions(questionPhaseRelation.getQuestions()
+                .stream()
+                .map(question -> clonedQuestions.get(question.getId()))
+                .collect(Collectors.toSet()));
+        return clonedQuestionPhaseRelation;
+    }
+
+    private void checkIfCanBeUpdated(TrainingDefinition trainingDefinition) {
+        if (!trainingDefinition.getState().equals(TDState.UNRELEASED)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    ARCHIVED_OR_RELEASED));
+        }
+        if (trainingInstanceRepository.existsAnyForTrainingDefinition(trainingDefinition.getId())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingDefinition.class, "id", trainingDefinition.getId().getClass(), trainingDefinition.getId(),
+                    "Cannot update training definition with already created training instance. " +
+                            "Remove training instance/s before updating training definition."));
+        }
+    }
+
+    private void deletePhase(AbstractPhase abstractPhase) {
+        if (abstractPhase instanceof QuestionnairePhase) {
+            questionnairePhaseRepository.delete((QuestionnairePhase) abstractPhase);
+        } else if (abstractPhase instanceof InfoPhase) {
+            infoPhaseRepository.delete((InfoPhase) abstractPhase);
+        } else {
+            trainingPhaseRepository.delete((TrainingPhase) abstractPhase);
+        }
+    }
+
+    private LocalDateTime getCurrentTimeInUTC() {
+        return LocalDateTime.now(Clock.systemUTC());
+    }
+
+    private void addLoggedInUserToTrainingDefinitionAsAuthor(TrainingDefinition trainingDefinition) {
+        Optional<UserRef> user = userRefRepository.findUserByUserRefId(userManagementServiceApi.getLoggedInUserRefId());
+        if (user.isPresent()) {
+            trainingDefinition.addAuthor(user.get());
+        } else {
+            UserRef newUser = new UserRef(userManagementServiceApi.getLoggedInUserRefId());
+            trainingDefinition.addAuthor(newUser);
+        }
+        trainingDefinition.setLastEdited(getCurrentTimeInUTC());
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingInstanceService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingInstanceService.java
new file mode 100644
index 0000000000000000000000000000000000000000..18c0ac6f85d44553620c84a4c5a35c846b402971
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingInstanceService.java
@@ -0,0 +1,300 @@
+package cz.muni.ics.kypo.training.adaptive.service.training;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.domain.AccessToken;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityConflictException;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityErrorDetail;
+import cz.muni.ics.kypo.training.adaptive.exceptions.EntityNotFoundException;
+import cz.muni.ics.kypo.training.adaptive.repository.AccessTokenRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.UserRefRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingRunRepository;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * The type Training instance service.
+ */
+@Service
+public class TrainingInstanceService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrainingInstanceService.class);
+    private final Random random = new Random();
+    private TrainingInstanceRepository trainingInstanceRepository;
+    private TrainingRunRepository trainingRunRepository;
+    private AccessTokenRepository accessTokenRepository;
+    private UserRefRepository organizerRefRepository;
+    private UserManagementServiceApi userManagementServiceApi;
+
+    /**
+     * Instantiates a new Training instance service.
+     *
+     * @param trainingInstanceRepository the training instance repository
+     * @param accessTokenRepository      the access token repository
+     * @param trainingRunRepository      the training run repository
+     * @param organizerRefRepository     the organizer ref repository
+     * @param userManagementServiceApi   the user management service
+     */
+    @Autowired
+
+    public TrainingInstanceService(TrainingInstanceRepository trainingInstanceRepository,
+                                   AccessTokenRepository accessTokenRepository,
+                                   TrainingRunRepository trainingRunRepository,
+                                   UserRefRepository organizerRefRepository,
+                                   UserManagementServiceApi userManagementServiceApi) {
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.trainingRunRepository = trainingRunRepository;
+        this.accessTokenRepository = accessTokenRepository;
+        this.organizerRefRepository = organizerRefRepository;
+        this.userManagementServiceApi = userManagementServiceApi;
+    }
+
+    /**
+     * Finds basic info about Training Instance by id
+     *
+     * @param instanceId of a Training Instance that would be returned
+     * @return specific {@link TrainingInstance} by id
+     * @throws EntityNotFoundException training instance is not found.
+     */
+    public TrainingInstance findById(Long instanceId) {
+        return trainingInstanceRepository.findById(instanceId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "id", instanceId.getClass(), instanceId)));
+    }
+
+    /**
+     * Find specific Training instance by id including its associated Training definition.
+     *
+     * @param instanceId the instance id
+     * @return the {@link TrainingInstance}
+     */
+    public TrainingInstance findByIdIncludingDefinition(Long instanceId) {
+        return trainingInstanceRepository.findByIdIncludingDefinition(instanceId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "id", instanceId.getClass(), instanceId)));
+    }
+
+    /**
+     * Find all Training Instances.
+     *
+     * @param predicate represents a predicate (boolean-valued function) of one argument.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return all {@link TrainingInstance}s
+     */
+    public Page<TrainingInstance> findAll(Predicate predicate, Pageable pageable) {
+        return trainingInstanceRepository.findAll(predicate, pageable);
+    }
+
+    /**
+     * Find all training instances based on the logged in user.
+     *
+     * @param predicate      the predicate
+     * @param pageable       the pageable
+     * @param loggedInUserId the logged in user id
+     * @return the page
+     */
+    public Page<TrainingInstance> findAll(Predicate predicate, Pageable pageable, Long loggedInUserId) {
+        return trainingInstanceRepository.findAll(predicate, pageable, loggedInUserId);
+    }
+
+    /**
+     * Creates new training instance
+     *
+     * @param trainingInstance to be created
+     * @return created {@link TrainingInstance}
+     */
+    public TrainingInstance create(TrainingInstance trainingInstance) {
+        trainingInstance.setAccessToken(generateAccessToken(trainingInstance.getAccessToken()));
+        if (trainingInstance.getStartTime().isAfter(trainingInstance.getEndTime())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", trainingInstance.getId().getClass(), trainingInstance.getId(),
+                    "End time must be later than start time."));
+        }
+        addLoggedInUserAsOrganizerToTrainingInstance(trainingInstance);
+        return trainingInstanceRepository.save(trainingInstance);
+    }
+
+    /**
+     * updates training instance
+     *
+     * @param trainingInstanceToUpdate to be updated
+     * @return new access token if it was changed
+     * @throws EntityNotFoundException training instance is not found.
+     * @throws EntityConflictException cannot be updated for some reason.
+     */
+    public String update(TrainingInstance trainingInstanceToUpdate) {
+        validateStartAndEndTime(trainingInstanceToUpdate);
+        TrainingInstance trainingInstance = findById(trainingInstanceToUpdate.getId());
+        //add original organizers and poolId to update
+        trainingInstanceToUpdate.setOrganizers(new HashSet<>(trainingInstance.getOrganizers()));
+        addLoggedInUserAsOrganizerToTrainingInstance(trainingInstanceToUpdate);
+        trainingInstanceToUpdate.setPoolId(trainingInstance.getPoolId());
+        //check if TI is running, true - only title can be changed, false - any field can be changed
+        if (LocalDateTime.now(Clock.systemUTC()).isAfter(trainingInstance.getStartTime())) {
+            this.checkChangedFieldsOfRunningTrainingInstance(trainingInstanceToUpdate, trainingInstance);
+        } else {
+            //check if new access token should be generated, if not original is kept
+            if (shouldGenerateNewToken(trainingInstance.getAccessToken(), trainingInstanceToUpdate.getAccessToken())) {
+                trainingInstanceToUpdate.setAccessToken(generateAccessToken(trainingInstanceToUpdate.getAccessToken()));
+            } else {
+                trainingInstanceToUpdate.setAccessToken(trainingInstance.getAccessToken());
+            }
+        }
+        trainingInstanceRepository.save(trainingInstanceToUpdate);
+        return trainingInstanceToUpdate.getAccessToken();
+    }
+
+    private void validateStartAndEndTime(TrainingInstance trainingInstance) {
+        if (trainingInstance.getStartTime().isAfter(trainingInstance.getEndTime())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id",
+                    trainingInstance.getId().getClass(), trainingInstance.getId(),
+                    "End time must be later than start time."));
+        }
+    }
+
+    private void checkChangedFieldsOfRunningTrainingInstance(TrainingInstance trainingInstanceToUpdate, TrainingInstance currentTrainingInstance) {
+        if (!currentTrainingInstance.getStartTime().equals(trainingInstanceToUpdate.getStartTime())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstanceToUpdate.getId(),
+                    "The start time of the running training instance cannot be changed. Only title can be updated."));
+        } else if (!currentTrainingInstance.getEndTime().equals(trainingInstanceToUpdate.getEndTime())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstanceToUpdate.getId(),
+                    "The end time of the running training instance cannot be changed. Only title can be updated."));
+        } else if (!currentTrainingInstance.getAccessToken().equals(trainingInstanceToUpdate.getAccessToken())) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", Long.class, trainingInstanceToUpdate.getId(),
+                    "The access token of the running training instance cannot be changed. Only title can be updated."));
+        }
+    }
+
+    private boolean shouldGenerateNewToken(String originalToken, String newToken) {
+        //new token should not be generated if token in update equals original token or if token in update equals original token without PIN
+        String tokenWithoutPin = originalToken.substring(0, originalToken.length() - 5);
+        return !(newToken.equals(tokenWithoutPin) || originalToken.equals(newToken));
+    }
+
+    private String generateAccessToken(String accessToken) {
+        String newPass = "";
+        boolean generated = false;
+        while (!generated) {
+            int firstNumber = this.random.nextInt(5) + 5;
+            String pin = firstNumber + RandomStringUtils.random(3, false, true);
+            newPass = accessToken + "-" + pin;
+            Optional<AccessToken> pW = accessTokenRepository.findOneByAccessToken(newPass);
+            if (!pW.isPresent()) {
+                generated = true;
+            }
+        }
+        AccessToken newTokenInstance = new AccessToken();
+        newTokenInstance.setAccessToken(newPass);
+        accessTokenRepository.saveAndFlush(newTokenInstance);
+        return newPass;
+    }
+
+    private void addLoggedInUserAsOrganizerToTrainingInstance(TrainingInstance trainingInstance) {
+        Optional<UserRef> authorOfTrainingInstance = organizerRefRepository.findUserByUserRefId(userManagementServiceApi.getLoggedInUserRefId());
+        if (authorOfTrainingInstance.isPresent()) {
+            trainingInstance.addOrganizer(authorOfTrainingInstance.get());
+        } else {
+            UserRef userRef = new UserRef(userManagementServiceApi.getLoggedInUserRefId());
+            trainingInstance.addOrganizer(organizerRefRepository.save(userRef));
+        }
+    }
+
+    /**
+     * deletes training instance
+     *
+     * @param trainingInstance the training instance to be deleted.
+     * @throws EntityNotFoundException training instance is not found.
+     * @throws EntityConflictException cannot be deleted for some reason.
+     */
+    public void delete(TrainingInstance trainingInstance) {
+        trainingInstanceRepository.delete(trainingInstance);
+        LOG.debug("Training instance with id: {} deleted.", trainingInstance.getId());
+    }
+
+    /**
+     * deletes training instance
+     *
+     * @param id the training instance to be deleted.
+     * @throws EntityNotFoundException training instance is not found.
+     * @throws EntityConflictException cannot be deleted for some reason.
+     */
+    public void deleteById(Long id) {
+        trainingInstanceRepository.deleteById(id);
+        LOG.debug("Training instance with id: {} deleted.", id);
+    }
+
+    /**
+     * Update training instance pool training instance.
+     *
+     * @param trainingInstance the training instance
+     * @return the training instance
+     */
+    public TrainingInstance updateTrainingInstancePool(TrainingInstance trainingInstance) {
+        return trainingInstanceRepository.saveAndFlush(trainingInstance);
+    }
+
+    /**
+     * Finds all Training Runs of specific Training Instance.
+     *
+     * @param instanceId id of Training Instance whose Training Runs would be returned.
+     * @param isActive   if isActive attribute is True, only active runs are returned
+     * @param pageable   pageable parameter with information about pagination.
+     * @return {@link TrainingRun}s of specific {@link TrainingInstance}
+     */
+    public Page<TrainingRun> findTrainingRunsByTrainingInstance(Long instanceId, Boolean isActive, Pageable pageable) {
+        // check if instance exists
+        this.findById(instanceId);
+        if (isActive == null) {
+            return trainingRunRepository.findAllByTrainingInstanceId(instanceId, pageable);
+        } else if (isActive) {
+            return trainingRunRepository.findAllActiveByTrainingInstanceId(instanceId, pageable);
+        } else {
+            return trainingRunRepository.findAllInactiveByTrainingInstanceId(instanceId, pageable);
+        }
+    }
+
+    /**
+     * Find UserRefs by userRefId
+     *
+     * @param usersRefId of wanted UserRefs
+     * @return {@link UserRef}s with corresponding userRefIds
+     */
+    public Set<UserRef> findUserRefsByUserRefIds(Set<Long> usersRefId) {
+        return organizerRefRepository.findUsers(usersRefId);
+    }
+
+    /**
+     * Check if instance is finished.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return true if instance is finished, false if not
+     */
+    public boolean checkIfInstanceIsFinished(Long trainingInstanceId) {
+        return trainingInstanceRepository.isFinished(trainingInstanceId, LocalDateTime.now(Clock.systemUTC()));
+    }
+
+    /**
+     * Find specific Training instance by its access token and with start time before current time and ending time after current time
+     *
+     * @param accessToken of Training instance
+     * @return Training instance
+     */
+    public TrainingInstance findByStartTimeAfterAndEndTimeBeforeAndAccessToken(String accessToken) {
+        return trainingInstanceRepository.findByStartTimeAfterAndEndTimeBeforeAndAccessToken(LocalDateTime.now(Clock.systemUTC()), accessToken)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "accessToken", accessToken.getClass(), accessToken,
+                        "There is no active training session matching access token.")));
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingRunService.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingRunService.java
new file mode 100644
index 0000000000000000000000000000000000000000..f19241709c6c7bd01ebc9a4f683e557de1c71a94
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/service/training/TrainingRunService.java
@@ -0,0 +1,523 @@
+package cz.muni.ics.kypo.training.adaptive.service.training;
+
+import com.querydsl.core.types.Predicate;
+import cz.muni.ics.kypo.training.adaptive.annotations.transactions.TransactionalWO;
+import cz.muni.ics.kypo.training.adaptive.domain.TRAcquisitionLock;
+import cz.muni.ics.kypo.training.adaptive.domain.UserRef;
+import cz.muni.ics.kypo.training.adaptive.domain.phase.*;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingDefinition;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingInstance;
+import cz.muni.ics.kypo.training.adaptive.domain.training.TrainingRun;
+import cz.muni.ics.kypo.training.adaptive.enums.TRState;
+import cz.muni.ics.kypo.training.adaptive.exceptions.*;
+import cz.muni.ics.kypo.training.adaptive.repository.TRAcquisitionLockRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.UserRefRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.phases.AbstractPhaseRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingInstanceRepository;
+import cz.muni.ics.kypo.training.adaptive.repository.training.TrainingRunRepository;
+import cz.muni.ics.kypo.training.adaptive.service.api.ElasticsearchServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.api.SandboxServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.api.UserManagementServiceApi;
+import cz.muni.ics.kypo.training.adaptive.service.audit.AuditEventsService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
+
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * The type Training run service.
+ */
+@Service
+public class TrainingRunService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrainingRunService.class);
+
+    private SandboxServiceApi sandboxServiceApi;
+    private TrainingRunRepository trainingRunRepository;
+    private AbstractPhaseRepository abstractPhaseRepository;
+    private TrainingInstanceRepository trainingInstanceRepository;
+    private UserRefRepository participantRefRepository;
+    private AuditEventsService auditEventsService;
+    private ElasticsearchServiceApi elasticsearchServiceApi;
+    private UserManagementServiceApi userManagementServiceApi;
+    private TRAcquisitionLockRepository trAcquisitionLockRepository;
+
+    /**
+     * Instantiates a new Training run service.
+     *
+     * @param trainingRunRepository       the training run repository
+     * @param abstractPhaseRepository     the abstract phase repository
+     * @param trainingInstanceRepository  the training instance repository
+     * @param participantRefRepository    the participant ref repository
+     * @param auditEventsService          the audit events service
+     * @param securityService             the security service
+     * @param trAcquisitionLockRepository the tr acquisition lock repository
+     */
+    @Autowired
+    public TrainingRunService(SandboxServiceApi sandboxServiceApi,
+                              TrainingRunRepository trainingRunRepository,
+                              AbstractPhaseRepository abstractPhaseRepository,
+                              TrainingInstanceRepository trainingInstanceRepository,
+                              UserRefRepository participantRefRepository,
+                              AuditEventsService auditEventsService,
+                              ElasticsearchServiceApi elasticsearchServiceApi,
+                              UserManagementServiceApi userManagementServiceApi,
+                              TRAcquisitionLockRepository trAcquisitionLockRepository) {
+        this.sandboxServiceApi = sandboxServiceApi;
+        this.trainingRunRepository = trainingRunRepository;
+        this.abstractPhaseRepository = abstractPhaseRepository;
+        this.trainingInstanceRepository = trainingInstanceRepository;
+        this.participantRefRepository = participantRefRepository;
+        this.auditEventsService = auditEventsService;
+        this.elasticsearchServiceApi = elasticsearchServiceApi;
+        this.userManagementServiceApi = userManagementServiceApi;
+        this.trAcquisitionLockRepository = trAcquisitionLockRepository;
+    }
+
+    /**
+     * Finds specific Training Run by id.
+     *
+     * @param runId of a Training Run that would be returned
+     * @return specific {@link TrainingRun} by id
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public TrainingRun findById(Long runId) {
+        return trainingRunRepository.findById(runId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingRun.class, "id", runId.getClass(), runId)));
+    }
+
+    /**
+     * /**
+     * Finds specific Training Run by id including current phase.
+     *
+     * @param runId of a Training Run with phase that would be returned
+     * @return specific {@link TrainingRun} by id
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public TrainingRun findByIdWithPhase(Long runId) {
+        return trainingRunRepository.findByIdWithPhase(runId).orElseThrow(() -> new EntityNotFoundException(
+                new EntityErrorDetail(TrainingRun.class, "id", runId.getClass(), runId)));
+    }
+
+    /**
+     * Find all Training Runs.
+     *
+     * @param predicate specifies query to the database.
+     * @param pageable  pageable parameter with information about pagination.
+     * @return all {@link TrainingRun}s
+     */
+    public Page<TrainingRun> findAll(Predicate predicate, Pageable pageable) {
+        return trainingRunRepository.findAll(predicate, pageable);
+    }
+
+    /**
+     * Delete selected training run.
+     *
+     * @param trainingRunId training run to delete
+     * @param forceDelete   delete training run in a force manner
+     */
+    public void deleteTrainingRun(Long trainingRunId, boolean forceDelete) {
+        TrainingRun trainingRun = findById(trainingRunId);
+        if (!forceDelete && trainingRun.getState().equals(TRState.RUNNING)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRun.getId().getClass(), trainingRun.getId(),
+                    "Cannot delete training run that is running. Consider force delete."));
+        }
+        elasticsearchServiceApi.deleteEventsFromTrainingRun(trainingRun.getTrainingInstance().getId(), trainingRunId);
+        trAcquisitionLockRepository.deleteByParticipantRefIdAndTrainingInstanceId(trainingRun.getParticipantRef().getUserRefId(), trainingRun.getTrainingInstance().getId());
+        trainingRunRepository.delete(trainingRun);
+    }
+
+    /**
+     * Checks whether any trainin runs exists for particular training instance
+     *
+     * @param trainingInstanceId the training instance id
+     * @return boolean boolean
+     */
+    public boolean existsAnyForTrainingInstance(Long trainingInstanceId) {
+        return trainingRunRepository.existsAnyForTrainingInstance(trainingInstanceId);
+    }
+
+
+    /**
+     * Finds all Training Runs of logged in user.
+     *
+     * @param pageable pageable parameter with information about pagination.
+     * @return {@link TrainingRun}s of logged in user.
+     */
+    public Page<TrainingRun> findAllByParticipantRefUserRefId(Pageable pageable) {
+        return trainingRunRepository.findAllByParticipantRefId(userManagementServiceApi.getLoggedInUserRefId(), pageable);
+    }
+
+    /**
+     * Finds all Training Runs of particular training instance.
+     *
+     * @param trainingInstanceId the training instance id
+     * @return the set
+     */
+    public Set<TrainingRun> findAllByTrainingInstanceId(Long trainingInstanceId) {
+        return trainingRunRepository.findAllByTrainingInstanceId(trainingInstanceId);
+    }
+
+    /**
+     * Gets next phase of given Training Run and set new current phase.
+     *
+     * @param runId id of Training Run whose next phase should be returned.
+     * @return {@link AbstractPhase}
+     * @throws EntityNotFoundException training run or phase is not found.
+     */
+    public AbstractPhase getNextPhase(Long runId) {
+        TrainingRun trainingRun = findByIdWithPhase(runId);
+        int currentPhaseOrder = trainingRun.getCurrentPhase().getOrder();
+        int maxPhaseOrder = abstractPhaseRepository.getCurrentMaxOrder(trainingRun.getCurrentPhase().getTrainingDefinition().getId());
+        if (!trainingRun.isPhaseAnswered()) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", runId.getClass(), runId,
+                    "You need to answer the phase to move to the next phase."));
+        }
+        if (currentPhaseOrder == maxPhaseOrder) {
+            throw new EntityNotFoundException(new EntityErrorDetail(AbstractPhase.class, "There is no next phase for current training run (ID: " + runId + ")."));
+        }
+        List<AbstractPhase> phases = abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(trainingRun.getCurrentPhase().getTrainingDefinition().getId());
+        int nextPhaseIndex = phases.indexOf(trainingRun.getCurrentPhase()) + 1;
+        AbstractPhase nextPhase = phases.get(nextPhaseIndex);
+        if (trainingRun.getCurrentPhase() instanceof InfoPhase) {
+            auditEventsService.auditPhaseCompletedAction(trainingRun);
+        }
+        if (nextPhase instanceof TrainingPhase) {
+            trainingRun.setCurrentTask(((TrainingPhase) nextPhase).getTasks().get(0));
+        } else {
+            trainingRun.setCurrentTask(null);
+        }
+        trainingRun.setCurrentPhase(nextPhase);
+        trainingRun.setIncorrectAnswerCount(0);
+        trainingRunRepository.save(trainingRun);
+        auditEventsService.auditPhaseStartedAction(trainingRun);
+
+        return nextPhase;
+    }
+
+    /**
+     * Finds all Training Runs of specific Training Definition of logged in user.
+     *
+     * @param definitionId id of Training Definition
+     * @param pageable     pageable parameter with information about pagination.
+     * @return {@link TrainingRun}s of specific Training Definition of logged in user
+     */
+    public Page<TrainingRun> findAllByTrainingDefinitionAndParticipant(Long definitionId, Pageable pageable) {
+        return trainingRunRepository.findAllByTrainingDefinitionIdAndParticipantUserRefId(definitionId, userManagementServiceApi.getLoggedInUserRefId(), pageable);
+    }
+
+    /**
+     * Finds all Training Runs of specific training definition.
+     *
+     * @param definitionId id of Training Definition whose Training Runs would be returned.
+     * @param pageable     pageable parameter with information about pagination.
+     * @return {@link TrainingRun}s of specific Training Definition
+     */
+    public Page<TrainingRun> findAllByTrainingDefinition(Long definitionId, Pageable pageable) {
+        return trainingRunRepository.findAllByTrainingDefinitionId(definitionId, pageable);
+    }
+
+    /**
+     * Gets list of all phase in Training Definition.
+     *
+     * @param definitionId must be id of first phase of some Training Definition.
+     * @return List of {@link AbstractPhase}s
+     * @throws EntityNotFoundException one of the phase is not found.
+     */
+    public List<AbstractPhase> getPhases(Long definitionId) {
+        return abstractPhaseRepository.findAllByTrainingDefinitionIdOrderByOrder(definitionId);
+    }
+
+    /**
+     * Access training run based on given accessToken.
+     *
+     * @param trainingInstance the training instance
+     * @param participantRefId the participant ref id
+     * @return accessed {@link TrainingRun}
+     * @throws EntityNotFoundException no active training instance for given access token, no starting phase in training definition.
+     * @throws EntityConflictException pool of sandboxes is not created for training instance.
+     */
+    public TrainingRun createTrainingRun(TrainingInstance trainingInstance, Long participantRefId) {
+        AbstractPhase initialPhase = findFirstPhaseForTrainingRun(trainingInstance.getTrainingDefinition().getId());
+        TrainingRun trainingRun = getNewTrainingRun(initialPhase, trainingInstance, LocalDateTime.now(Clock.systemUTC()), trainingInstance.getEndTime(), participantRefId);
+        if (initialPhase instanceof TrainingPhase) {
+            trainingRun.setCurrentTask(((TrainingPhase) initialPhase).getTasks().get(0));
+        }
+        return trainingRunRepository.save(trainingRun);
+    }
+
+    /**
+     * Find running training run of user optional.
+     *
+     * @param accessToken      the access token
+     * @param participantRefId the participant ref id
+     * @return the optional
+     */
+    public Optional<TrainingRun> findRunningTrainingRunOfUser(String accessToken, Long participantRefId) {
+        return trainingRunRepository.findRunningTrainingRunOfUser(accessToken, participantRefId);
+    }
+
+    /**
+     * Gets training instance for particular access token.
+     *
+     * @param accessToken the access token
+     * @return the training instance for particular access token
+     */
+    public TrainingInstance getTrainingInstanceForParticularAccessToken(String accessToken) {
+        TrainingInstance trainingInstance = trainingInstanceRepository.findByStartTimeAfterAndEndTimeBeforeAndAccessToken(LocalDateTime.now(Clock.systemUTC()), accessToken)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingInstance.class, "accessToken", accessToken.getClass(), accessToken,
+                        "There is no active training session matching access token.")));
+        if (trainingInstance.getPoolId() == null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingInstance.class, "id", trainingInstance.getId().getClass(), trainingInstance.getId(),
+                    "At first organizer must allocate sandboxes for training instance."));
+        }
+        return trainingInstance;
+    }
+
+    /**
+     * Tr acquisition lock to prevent many requests from the same user. This method is called in a new transaction that means that the existing one is suspended.
+     *
+     * @param participantRefId   the participant ref id
+     * @param trainingInstanceId the training instance id
+     * @param accessToken        the access token
+     */
+    @TransactionalWO(propagation = Propagation.REQUIRES_NEW)
+    public void trAcquisitionLockToPreventManyRequestsFromSameUser(Long participantRefId, Long trainingInstanceId, String accessToken) {
+        try {
+            trAcquisitionLockRepository.saveAndFlush(new TRAcquisitionLock(participantRefId, trainingInstanceId, LocalDateTime.now(Clock.systemUTC())));
+        } catch (DataIntegrityViolationException ex) {
+            throw new TooManyRequestsException(new EntityErrorDetail(TrainingInstance.class, "accessToken", accessToken.getClass(), accessToken,
+                    "Training run has been already accessed and cannot be created again. Please resume Training Run"));
+        }
+    }
+
+    @TransactionalWO(propagation = Propagation.REQUIRES_NEW)
+    public void deleteTrAcquisitionLockToPreventManyRequestsFromSameUser(Long participantRefId, Long trainingInstanceId) {
+        trAcquisitionLockRepository.deleteByParticipantRefIdAndTrainingInstanceId(participantRefId, trainingInstanceId);
+    }
+
+    private AbstractPhase findFirstPhaseForTrainingRun(Long trainingDefinitionId) {
+        return abstractPhaseRepository.findFirstPhaseOfTrainingDefinition(trainingDefinitionId)
+                .orElseThrow(() -> new EntityNotFoundException(new EntityErrorDetail(TrainingDefinition.class, "id", Long.class, trainingDefinitionId,
+                        "No starting phase available for this training definition.")));
+    }
+
+    private TrainingRun getNewTrainingRun(AbstractPhase currentPhase, TrainingInstance trainingInstance, LocalDateTime startTime, LocalDateTime endTime, Long participantRefId) {
+        TrainingRun newTrainingRun = new TrainingRun();
+        newTrainingRun.setCurrentPhase(currentPhase);
+
+        Optional<UserRef> userRefOpt = participantRefRepository.findUserByUserRefId(participantRefId);
+        if (userRefOpt.isPresent()) {
+            newTrainingRun.setParticipantRef(userRefOpt.get());
+        } else {
+            newTrainingRun.setParticipantRef(participantRefRepository.save(new UserRef(userManagementServiceApi.getLoggedInUserRefId())));
+        }
+        newTrainingRun.setQuestionnaireResponses("[]");
+        newTrainingRun.setState(TRState.RUNNING);
+        newTrainingRun.setTrainingInstance(trainingInstance);
+        newTrainingRun.setStartTime(startTime);
+        newTrainingRun.setEndTime(endTime);
+        return newTrainingRun;
+    }
+
+    /**
+     * Connects available sandbox with given Training run.
+     *
+     * @param trainingRun that will be connected with sandbox
+     * @param poolId      the pool id
+     * @return Training run with assigned sandbox
+     * @throws ForbiddenException       no available sandbox.
+     * @throws MicroserviceApiException error calling OpenStack Sandbox Service API
+     */
+    public TrainingRun assignSandbox(TrainingRun trainingRun, long poolId) {
+        Long sandboxInstanceRef = this.sandboxServiceApi.getAndLockSandboxForTrainingRun(poolId);
+        trainingRun.setSandboxInstanceRefId(sandboxInstanceRef);
+        auditEventsService.auditTrainingRunStartedAction(trainingRun);
+        auditEventsService.auditPhaseStartedAction(trainingRun);
+        return trainingRunRepository.save(trainingRun);
+    }
+
+    /**
+     * Resume previously closed training run.
+     *
+     * @param trainingRunId id of training run to be resumed.
+     * @return {@link TrainingRun}
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public TrainingRun resumeTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = findByIdWithPhase(trainingRunId);
+        if (trainingRun.getState().equals(TRState.FINISHED) || trainingRun.getState().equals(TRState.ARCHIVED)) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Cannot resume finished training run."));
+        }
+        if (trainingRun.getTrainingInstance().getEndTime().isBefore(LocalDateTime.now(Clock.systemUTC()))) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Cannot resume training run after end of training instance."));
+        }
+        if (trainingRun.getTrainingInstance().getPoolId() == null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "The pool assignment of the appropriate training instance has been probably canceled. Please contact the organizer."));
+        }
+
+        if (trainingRun.getSandboxInstanceRefId() == null) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Sandbox of this training run was already deleted, you have to start new training."));
+        }
+        auditEventsService.auditTrainingRunResumedAction(trainingRun);
+        return trainingRun;
+    }
+
+    /**
+     * Check given answer of given Training Run.
+     *
+     * @param runId  id of Training Run to check answer.
+     * @param answer string which player submit.
+     * @return true if answer is correct, false if answer is wrong.
+     * @throws EntityNotFoundException training run is not found.
+     * @throws BadRequestException     the current phase of training run is not training phase.
+     */
+    public boolean isCorrectAnswer(Long runId, String answer) {
+        TrainingRun trainingRun = findByIdWithPhase(runId);
+        AbstractPhase currentPhase = trainingRun.getCurrentPhase();
+        if (currentPhase instanceof TrainingPhase) {
+            if (trainingRun.isPhaseAnswered()) {
+                throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", Long.class, runId, "The answer of the current phase of training run has been already corrected."));
+            }
+            Task currentTask = trainingRun.getCurrentTask();
+            if (currentTask.getAnswer().equals(answer)) {
+                trainingRun.setPhaseAnswered(true);
+                auditEventsService.auditCorrectAnswerSubmittedAction(trainingRun, answer);
+                auditEventsService.auditPhaseCompletedAction(trainingRun);
+                return true;
+            } else if (currentTask.getIncorrectAnswerLimit() != trainingRun.getIncorrectAnswerCount()) {
+                trainingRun.setIncorrectAnswerCount(trainingRun.getIncorrectAnswerCount() + 1);
+            }
+            auditEventsService.auditWrongAnswerSubmittedAction(trainingRun, answer);
+            return false;
+        } else {
+            throw new BadRequestException("Current phase is not training phase and does not have answer.");
+        }
+    }
+
+    /**
+     * Gets remaining attempts to solve current phase of training run.
+     *
+     * @param trainingRunId the training run id
+     * @return the remaining attempts
+     */
+    public int getRemainingAttempts(Long trainingRunId) {
+        TrainingRun trainingRun = findByIdWithPhase(trainingRunId);
+        AbstractPhase phase = trainingRun.getCurrentPhase();
+        if (phase instanceof TrainingPhase) {
+            if (trainingRun.isSolutionTaken()) {
+                return 0;
+            }
+            return trainingRun.getCurrentTask().getIncorrectAnswerLimit() - trainingRun.getIncorrectAnswerCount();
+        }
+        throw new BadRequestException("Current phase is not training phase and does not have an answer.");
+    }
+
+    /**
+     * Gets solution of current phase of given Training Run.
+     *
+     * @param trainingRunId id of Training Run which current phase gets solution for.
+     * @return solution of current phase.
+     * @throws EntityNotFoundException training run is not found.
+     * @throws BadRequestException     the current phase of training run is not training phase.
+     */
+    public String getSolution(Long trainingRunId) {
+        TrainingRun trainingRun = findByIdWithPhase(trainingRunId);
+        AbstractPhase currentPhase = trainingRun.getCurrentPhase();
+        if (currentPhase instanceof TrainingPhase) {
+            if (!trainingRun.isSolutionTaken()) {
+                trainingRun.setSolutionTaken(true);
+                trainingRunRepository.save(trainingRun);
+                auditEventsService.auditSolutionDisplayedAction(trainingRun);
+            }
+            return trainingRun.getCurrentTask().getSolution();
+        } else {
+            throw new BadRequestException("Current phase is not training phase and does not have solution.");
+        }
+    }
+
+    /**
+     * Gets max phase order of phase from definition.
+     *
+     * @param definitionId id of training definition.
+     * @return max order of phase.
+     */
+    public int getMaxPhaseOrder(Long definitionId) {
+        return abstractPhaseRepository.getCurrentMaxOrder(definitionId);
+    }
+
+    /**
+     * Finish training run.
+     *
+     * @param trainingRunId id of training run to be finished.
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public void finishTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = findById(trainingRunId);
+        int maxOrder = abstractPhaseRepository.getCurrentMaxOrder(trainingRun.getCurrentPhase().getTrainingDefinition().getId());
+        if (trainingRun.getCurrentPhase().getOrder() != maxOrder) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Cannot finish training run because current phase is not last."));
+        }
+        if (!trainingRun.isPhaseAnswered()) {
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Cannot finish training run because current phase is not answered."));
+        }
+        trainingRun.setState(TRState.FINISHED);
+        trainingRun.setEndTime(LocalDateTime.now(Clock.systemUTC()));
+        trAcquisitionLockRepository.deleteByParticipantRefIdAndTrainingInstanceId(trainingRun.getParticipantRef().getUserRefId(), trainingRun.getTrainingInstance().getId());
+        if (trainingRun.getCurrentPhase() instanceof InfoPhase) {
+            auditEventsService.auditPhaseCompletedAction(trainingRun);
+        }
+        auditEventsService.auditTrainingRunEndedAction(trainingRun);
+    }
+
+    /**
+     * Archive training run.
+     *
+     * @param trainingRunId id of training run to be archived.
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public void archiveTrainingRun(Long trainingRunId) {
+        TrainingRun trainingRun = findById(trainingRunId);
+        trainingRun.setState(TRState.ARCHIVED);
+        trainingRun.setPreviousSandboxInstanceRefId(trainingRun.getSandboxInstanceRefId());
+        trainingRun.setSandboxInstanceRefId(null);
+        trAcquisitionLockRepository.deleteByParticipantRefIdAndTrainingInstanceId(trainingRun.getParticipantRef().getUserRefId(), trainingRun.getTrainingInstance().getId());
+        trainingRunRepository.save(trainingRun);
+    }
+
+    /**
+     * Evaluate and store responses to questionnaire.
+     *
+     * @param trainingRunId     id of training run to be finished.
+     * @param responsesAsString response to questionnaire to be evaluated
+     * @throws EntityNotFoundException training run is not found.
+     */
+    public void evaluateResponsesToQuestionnaire(Long trainingRunId, String responsesAsString) {
+        TrainingRun trainingRun = findByIdWithPhase(trainingRunId);
+        if (!(trainingRun.getCurrentPhase() instanceof QuestionnairePhase)) {
+            throw new BadRequestException("Current phase is not questionnaire phase and cannot be evaluated.");
+        }
+        if (trainingRun.isPhaseAnswered())
+            throw new EntityConflictException(new EntityErrorDetail(TrainingRun.class, "id", trainingRunId.getClass(), trainingRunId,
+                    "Current phase of the training run has been already answered."));
+        //TODO complete the evaluation
+        auditEventsService.auditQuestionnaireAnswersAction(trainingRun, responsesAsString);
+        auditEventsService.auditPhaseCompletedAction(trainingRun);
+    }
+}
diff --git a/src/main/java/cz/muni/ics/kypo/training/adaptive/utils/AbstractFileExtensions.java b/src/main/java/cz/muni/ics/kypo/training/adaptive/utils/AbstractFileExtensions.java
new file mode 100644
index 0000000000000000000000000000000000000000..c53e176d2700243c908d32ff6e3653cb5aae1504
--- /dev/null
+++ b/src/main/java/cz/muni/ics/kypo/training/adaptive/utils/AbstractFileExtensions.java
@@ -0,0 +1,15 @@
+package cz.muni.ics.kypo.training.adaptive.utils;
+
+/**
+ * The type Abstract file extensions.
+ */
+public abstract class AbstractFileExtensions {
+    /**
+     * The constant JSON_FILE_EXTENSION.
+     */
+    public static final String JSON_FILE_EXTENSION = ".json";
+    /**
+     * The constant ZIP_FILE_EXTENSION.
+     */
+    public static final String ZIP_FILE_EXTENSION = ".zip";
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index a31816f8448efd57f0aab12ecfcdfa5c167a584c..708cd874d1bfbdbf45f26d662aa5b703e6e02d2f 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,5 +1,5 @@
 server.servlet.context-path=/kypo-adaptive-training-rest/api/v1/
-
 spring.h2.console.enabled=true
 spring.h2.console.path=/h2-console
-
+spring.flyway.enabled=false
+user-and-group-server.uri=https://localhost:8084/kypo2-user-and-group-rest/api/v1/
\ No newline at end of file
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ab28ad276b7257f149b5dd0b4ecb2218bcedfe8b
--- /dev/null
+++ b/src/main/resources/banner.txt
@@ -0,0 +1,6 @@
+/\ \/\ \ /\ \  /\ \/\  _`\ /\  __`\    
+\ \ \/'/'\ `\`\\/'/\ \ \L\ \ \ \/\ \   
+ \ \ , <  `\ `\ /'  \ \ ,__/\ \ \ \ \  
+  \ \ \\`\  `\ \ \   \ \ \/  \ \ \_\ \ 
+   \ \_\ \_\  \ \_\   \ \_\   \ \_____\
+    \/_/\/_/   \/_/    \/_/    \/_____/
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V1__db_adaptive_trainings_schema.sql b/src/main/resources/db/migration/V1__db_adaptive_trainings_schema.sql
new file mode 100644
index 0000000000000000000000000000000000000000..43bbe615f9acb9e3a7bd6aecfee6ccaaf533655f
--- /dev/null
+++ b/src/main/resources/db/migration/V1__db_adaptive_trainings_schema.sql
@@ -0,0 +1,196 @@
+-- USER_REF
+create table user_ref (
+    id  bigserial not null,
+    user_ref_id int8 not null,
+    primary key (id)
+);
+
+-- TRAINING
+create table training_definition (
+    id  bigserial not null,
+    description text,
+    last_edited timestamp not null,
+    estimated_duration int8,
+    outcomes bytea,
+    prerequisites bytea,
+    show_stepper_bar boolean not null,
+    state varchar(128) not null,
+    title varchar(255) not null,
+    primary key (id)
+);
+
+create table training_definition_user_ref (
+    training_definition_id int8 not null,
+    user_ref_id int8 not null,
+    primary key (training_definition_id, user_ref_id),
+    foreign key (training_definition_id) references training_definition,
+    foreign key (user_ref_id) references user_ref
+);
+
+create table training_instance (
+    id  bigserial not null,
+    access_token varchar(255) not null,
+    end_time timestamp not null,
+    pool_id int8,
+    start_time timestamp not null,
+    title varchar(255) not null,
+    training_definition_id int8,
+    primary key (id),
+    foreign key (training_definition_id) references training_definition
+
+);
+
+create table training_instance_user_ref (
+    training_instance_id int8 not null,
+    user_ref_id int8 not null,
+    primary key (training_instance_id, user_ref_id),
+    foreign key (training_instance_id) references training_instance,
+    foreign key (user_ref_id) references user_ref
+
+);
+
+create table training_run (
+    id  bigserial not null,
+    questionnaire_responses text,
+    end_time timestamp not null,
+    incorrect_answer_count int4 not null,
+    phase_answered boolean,
+    solution_taken boolean not null,
+    start_time timestamp not null,
+    state varchar(128) not null,
+    current_phase_id int8 not null,
+    current_task_id int8 null,
+    user_ref_id int8 not null,
+    sandbox_instance_ref_id int8 null,
+    training_instance_id int8 not null,
+    previous_sandbox_instance_ref_id int8 null,
+    primary key (id),
+    foreign key (training_instance_id) references training_instance,
+    foreign key (user_ref_id) references user_ref
+);
+
+-- PHASES
+create table abstract_phase (
+    phase_id  bigserial not null,
+    title varchar(255) not null,
+    order_in_training_definition int4 not null,
+    training_definition_id int8,
+    primary key (phase_id),
+    foreign key (training_definition_id) references training_definition
+);
+
+-- INFO PHASE
+create table info_phase (
+    phase_id  bigserial not null,
+    content text not null,
+    primary key (phase_id),
+    foreign key (phase_id) references abstract_phase
+);
+
+-- TRAINING PHASE
+create table training_phase (
+    phase_id  bigserial not null,
+    estimated_duration int4 not null,
+    allowed_commands int4 ,
+    allowed_wrong_answers int4 not null,
+    primary key (phase_id),
+    foreign key (phase_id) references abstract_phase
+);
+
+create table task (
+    task_id bigserial not null,
+    title varchar(255) not null,
+    content text not null,
+    answer varchar(255) not null,
+    solution varchar(1048) not null,
+    incorrect_answer_limit int4,
+    modify_sandbox boolean not null,
+    sandbox_change_expected_duration int4 not null,
+    order_in_training_phase int4 not null,
+    training_phase_id int8 not null,
+    primary key (task_id),
+    foreign key (training_phase_id) references training_phase
+);
+
+create table decision_matrix_row (
+    decision_matrix_row_id bigserial not null,
+    order_in_training_phase int4 not null,
+    assessment_answered double precision not null,
+    keyword_used double precision not null,
+    completed_in_time double precision not null,
+    solution_displayed double precision not null,
+    wrong_answers double precision not null,
+    training_phase_id int8 not null,
+    primary key (decision_matrix_row_id),
+    foreign key (training_phase_id) references training_phase
+);
+
+-- QUESTIONNAIRE PHASE
+create table questionnaire_phase (
+    phase_id  bigserial not null,
+    questionnaire_type varchar(32),
+    primary key (phase_id),
+    foreign key (phase_id) references abstract_phase
+);
+
+create table question (
+    question_id  bigserial not null,
+    question_type varchar(64) not null,
+    order_in_questionnaire int4 not null,
+    text text not null,
+    questionnaire_phase_id  int8 not null,
+    primary key (question_id),
+    foreign key (questionnaire_phase_id) references questionnaire_phase
+);
+
+create table question_choice (
+    question_choice_id  bigserial not null,
+    correct boolean not null,
+    text text not null,
+    order_in_question int4 not null,
+    question_id  int8 not null,
+    primary key (question_choice_id),
+    foreign key (question_id) references question
+);
+
+create table question_phase_relation (
+    question_phase_relation_id bigserial not null,
+    success_rate int4 not null,
+    related_training_phase_id int8 not null,
+    order_in_questionnaire int4 not null,
+    questionnaire_phase_id int8 not null,
+    primary key (question_phase_relation_id),
+    foreign key (related_training_phase_id) references training_phase,
+    foreign key (questionnaire_phase_id) references questionnaire_phase
+);
+
+create table question_phase_relation_question (
+    question_phase_relation_id int8 not null,
+    question_id int8 not null,
+    primary key (question_phase_relation_id, question_id),
+    foreign key (question_phase_relation_id) references question_phase_relation,
+    foreign key (question_id) references question
+);
+
+-- ACCESS TOKEN
+create table access_token (
+   id  bigserial not null,
+    access_token varchar(255) not null,
+    primary key (id)
+);
+
+-- ACQUISITION LOCK
+create table training_run_acquisition_lock (
+    id bigserial not null,
+    participant_ref_id int8 not null,
+    training_instance_id int8 not null,
+    creation_time timestamp not null,
+    primary key (id),
+    foreign key (participant_ref_id) references user_ref,
+    foreign key (training_instance_id) references training_instance
+);
+
+alter table training_run
+   add constraint FKi9smgl25av8pb1yv3fl4ycby0
+   foreign key (current_task_id)
+   references task;
diff --git a/src/main/resources/db/migration/V2__db_adaptive_trainings_sequences.sql b/src/main/resources/db/migration/V2__db_adaptive_trainings_sequences.sql
new file mode 100644
index 0000000000000000000000000000000000000000..5f55c926c77fe2ef7849167902e332e517d74637
--- /dev/null
+++ b/src/main/resources/db/migration/V2__db_adaptive_trainings_sequences.sql
@@ -0,0 +1,6 @@
+CREATE SEQUENCE phase_seq AS bigint INCREMENT 50 MINVALUE 1;
+CREATE SEQUENCE task_seq AS bigint INCREMENT 50 MINVALUE 1;
+CREATE SEQUENCE question_seq AS bigint INCREMENT 50 MINVALUE 1;
+CREATE SEQUENCE question_choice_seq AS bigint INCREMENT 50 MINVALUE 1;
+CREATE SEQUENCE question_phase_seq AS bigint INCREMENT 50 MINVALUE 1;
+CREATE SEQUENCE decision_matrix_row_seq AS bigint INCREMENT 50 MINVALUE 1;
diff --git a/src/main/resources/db/migration/V3__db_adaptive_trainings_indexes.sql b/src/main/resources/db/migration/V3__db_adaptive_trainings_indexes.sql
new file mode 100644
index 0000000000000000000000000000000000000000..46eb474fc7ae895d2cf46b17a880089806903765
--- /dev/null
+++ b/src/main/resources/db/migration/V3__db_adaptive_trainings_indexes.sql
@@ -0,0 +1,17 @@
+CREATE INDEX abstract_phase_order_in_training_definition_index
+ON abstract_phase (order_in_training_definition);
+
+CREATE UNIQUE INDEX access_token_access_token_index
+ON access_token (access_token);
+
+CREATE UNIQUE INDEX training_instance_access_token_index
+ON training_instance (access_token);
+
+CREATE INDEX training_instance_start_time_and_end_time_index
+ON training_instance (start_time, end_time DESC);
+
+CREATE INDEX training_definition_state_index
+ON training_definition (state);
+
+CREATE INDEX training_run_start_time_and_end_time_index
+ON training_run (start_time, end_time DESC);
diff --git a/src/main/resources/locale/ValidationMessages.properties b/src/main/resources/locale/ValidationMessages.properties
new file mode 100644
index 0000000000000000000000000000000000000000..3f765b4e5b520c18923a50a15abab574b144c9b6
--- /dev/null
+++ b/src/main/resources/locale/ValidationMessages.properties
@@ -0,0 +1,81 @@
+#PHASE
+phase.id.NotNull.message=Phase field 'id' cannot be null.
+phase.title.NotEmpty.message=Phase field 'title' cannot be empty.
+phase.phaseType.NotNull.message=Phase field 'phaseType' cannot be null.
+phase.order.NotNull.message=Phase field 'order' cannot be null.
+phase.order.Min.message=Phase field 'order' cannot be lower than 0.
+#QUESTIONNAIRE PHASE
+questionnairePhase.questionnaireType.NotNull.message=Questionnaire Phase field 'questionnaireType' cannot be null.
+questionnairePhaseRelation.order.NotNull.message=Questionnaire Phase Relation field 'order' cannot be null.
+questionnairePhaseRelation.order.Min.message=Questionnaire Phase Relation field 'order' cannot be lower than 0.
+questionnairePhaseRelation.questionIds.NotNull.message=Questionnaire Phase Relation field 'questionIds' cannot be null.
+questionnairePhaseRelation.questionIds.Size.message=Questionnaire Phase Relation field 'questionIds' must contain at least one value.
+questionnairePhaseRelation.phaseId.NotNull.message=Questionnaire Phase Relation field 'phaseId' cannot be null.
+questionnairePhaseRelation.phaseId.Min.message=Questionnaire Phase Relation field 'phaseId' cannot be lower than 0.
+questionnairePhaseRelation.successRate.Min.message=Questionnaire Phase Relation field 'successRate' cannot be lower than 0.
+questionnairePhaseRelation.successRate.Max.message=Questionnaire Phase Relation field 'successRate' cannot be higher than 100.
+#QUESTION
+question.order.NotNull.message=Question Level field 'order' cannot be null.
+question.order.Min.message=Question field 'order' cannot be lower than 0.
+question.text.NotEmpty.message=Question field 'text' cannot be empty.
+question.questionType.NotNull.message=Question field 'questionType' cannot be null.
+#QUESTION CHOICES
+questionChoices.text.NotEmpty.message=Question field 'text' cannot be empty.
+questionChoices.correct.NotNull.message=Question field 'correct' cannot be null.
+questionChoices.order.NotNull.message=Question field 'order' cannot be null.
+questionChoices.order.Min.message=Question field 'order' cannot be lower than 0.
+#TRAINING PHASE
+trainingPhase.id.NotNull.message=Training Phase field 'id' cannot be null.
+trainingPhase.title.NotEmpty.message=Training Phase field 'title' cannot be empty.
+trainingPhase.estimatedDuration.NotNull.message=Training Phase field 'maxScore' cannot be null.
+trainingPhase.estimatedDuration.Min.message=Training Phase field 'maxScore' cannot be lower than 0.
+trainingPhase.allowedCommands.NotNull.message=Training Phase field 'allowedCommands' cannot be null.
+trainingPhase.allowedCommands.Min.message=Training Phase field 'allowedCommands' cannot be lower than 0.
+trainingPhase.allowedWrongAnswers.NotNull.message=Training Phase field 'allowedWrongAnswers' cannot be null.
+trainingPhase.allowedWrongAnswers.Min.message=Training Phase field 'allowedWrongAnswers' cannot be lower than 0.
+#TASK
+task.title.NotEmpty.message=Task field 'title' cannot be empty.
+task.answer.NotEmpty.message=Task field 'answer' cannot be empty.
+task.answer.Size.message=Task field 'answer' cannot have more than 50 characters.
+task.content.NotEmpty.message=Task field 'content' cannot be empty.
+task.solution.NotEmpty.message=Task field 'solution' cannot be empty.
+task.incorrectAnswerLimit.NotEmpty.message=Task field 'incorrectAnswerLimit' cannot be empty.
+task.incorrectAnswerLimit.Min.message=Task field 'incorrectAnswerLimit' cannot be lower than 0.
+task.incorrectAnswerLimit.Max.message=Task field 'incorrectAnswerLimit' cannot be higher than 100.
+task.sandboxChangeExpectedDuration.Min.message=Task field 'sandboxChangeExpectedDuration' cannot be lower than 0.
+task.order.NotNull.message=Task field 'order' cannot be null.
+task.order.Min.message=Task field 'order' cannot be lower than 0.
+#INFO PHASE
+info.content.NotEmpty.message=Info Level field 'content' cannot be empty.
+#VALIDATE ANSWER
+validateAnswer.answer.NotEmpty.message=Validate answer field 'answer' cannot be empty.
+#TRAINING DEFINITION
+trainingDefinition.title.NotEmpty.message=Training Definition field 'title' cannot be empty.
+trainingDefinition.state.NotNull.message=Training Definition field 'state' cannot be null.
+trainingDefinition.showStepperBar.NotNull.message=Training Definition field 'showStepperBar' cannot be null.
+trainingDefinition.id.NotNull.message=Training Definition field 'id' cannot be null.
+#TRAINING INSTANCE
+assignPool.poolId.NotNull.message=Training Instance Assign Pool field  'poolId' cannot be null.
+assignPool.poolId.Min.message=Training Instance Assign Pool field  'poolId' cannot be lower than 0.
+trainingInstance.id.NotNull.message=Training Instance field 'id' cannot be null.
+trainingInstance.startTime.NotNull.message=Training Instance field 'startTime' cannot be null.
+trainingInstance.endTime.NotNull.message=Training Instance field 'endTime' cannot be null.
+trainingInstance.title.NotEmpty.message=Training Instance field 'title' cannot be empty.
+trainingInstance.accessToken.NotEmpty.message=Training Instance field 'accessToken' cannot be empty.
+trainingInstance.trainingDefinitionId.NotNull.message=Training Instance field 'trainingDefinition' cannot be null.
+trainingInstance.trainingDefinitionId.Min.message=Training Instance field 'trainingDefinitionId' cannot be lower than 0.
+#SANDBOX DEFINITION
+sandboxDefinition.id.NotNull.message=Sandbox Definition Info field 'id' cannot be null.
+sandboxDefinition.name.NotNull.message=Sandbox Definition Info field 'name' cannot be null.
+sandboxDefinition.url.NotNull.message=Sandbox Definition Info field 'url' cannot be null.
+sandboxDefinition.rev.NotNull.message=Sandbox Definition Info field 'rev' cannot be null.
+#SANDBOX
+sandbox.id.NotNull.message=Sandbox Info field 'id' cannot be null.
+#POOL INFO
+poolInfo.id.NotNull.message=Pool Info field 'id' cannot be null.
+poolInfo.maxSize.NotNull.message=Pool Info field 'maxSize' cannot be null.
+poolInfo.size.NotNull.message=Pool Info field 'size' cannot be null.
+poolInfo.poolId.NotNull.message=Pool Id Info field 'poolId' cannot be null.
+poolInfo.definitionId.NotNull.message=Pool Info field 'definitionId' cannot be null.
+poolInfo.lockId.NotNull.message=Pool Info field 'lockId' cannot be null.
+
diff --git a/src/main/resources/locale/ValidationMessages_cs_CZ.properties b/src/main/resources/locale/ValidationMessages_cs_CZ.properties
new file mode 100644
index 0000000000000000000000000000000000000000..d50e2d451bdfa4933dbcffe7e40798dabdbde8bf
--- /dev/null
+++ b/src/main/resources/locale/ValidationMessages_cs_CZ.properties
@@ -0,0 +1,81 @@
+#PHASE
+phase.id.NotNull.message=Phase pole 'id' nemůže být null.
+phase.title.NotEmpty.message=Phase pole 'title' nemůže být prázdné.
+phase.phaseType.NotNull.message=Phase pole 'phaseType' nemůže být null.
+phase.order.NotNull.message=Phase pole 'order' nemůže být null.
+phase.order.Min.message=Phase pole 'order' nemůže být menší než 0.
+#QUESTIONNAIRE PHASE
+questionnairePhase.questionnaireType.NotNull.message=Questionnaire Phase pole 'questionnaireType' nemůže být null.
+questionnairePhaseRelation.order.NotNull.message=Questionnaire Phase Relation pole 'order' nemůže být null.
+questionnairePhaseRelation.order.Min.message=Questionnaire Phase Relation pole 'order' nemůže být menší než 0.
+questionnairePhaseRelation.questionIds.NotNull.message=Questionnaire Phase Relation pole 'questionIds' nemůže být null.
+questionnairePhaseRelation.questionIds.Size.message=Questionnaire Phase Relation pole 'questionIds' must contain at least one value.
+questionnairePhaseRelation.phaseId.NotNull.message=Questionnaire Phase Relation pole 'phaseId' nemůže být null.
+questionnairePhaseRelation.phaseId.Min.message=Questionnaire Phase Relation pole 'phaseId' nemůže být menší než 0.
+questionnairePhaseRelation.successRate.Min.message=Questionnaire Phase Relation pole 'successRate' nemůže být menší než 0.
+questionnairePhaseRelation.successRate.Max.message=Questionnaire Phase Relation pole 'successRate' nemůže být vyšší než 100.
+#QUESTION
+question.order.NotNull.message=Question Level pole 'order' nemůže být null.
+question.order.Min.message=Question pole 'order' nemůže být menší než 0.
+question.text.NotEmpty.message=Question pole 'text' nemůže být prázdné.
+question.questionType.NotNull.message=Question pole 'questionType' nemůže být null.
+#QUESTION CHOICES
+questionChoices.text.NotEmpty.message=Question pole 'text' nemůže být prázdné.
+questionChoices.correct.NotNull.message=Question pole 'correct' nemůže být null.
+questionChoices.order.NotNull.message=Question pole 'order' nemůže být null.
+questionChoices.order.Min.message=Question pole 'order' nemůže být menší než 0.
+#TRAINING PHASE
+trainingPhase.id.NotNull.message=Training Phase pole 'id' nemůže být null.
+trainingPhase.title.NotEmpty.message=Training Phase pole 'title' nemůže být prázdné.
+trainingPhase.estimatedDuration.NotNull.message=Training Phase pole 'maxScore' nemůže být null.
+trainingPhase.estimatedDuration.Min.message=Training Phase pole 'maxScore' nemůže být menší než 0.
+trainingPhase.allowedCommands.NotNull.message=Training Phase pole 'allowedCommands' nemůže být null.
+trainingPhase.allowedCommands.Min.message=Training Phase pole 'allowedCommands' nemůže být menší než 0.
+trainingPhase.allowedWrongAnswers.NotNull.message=Training Phase pole 'allowedWrongAnswers' nemůže být null.
+trainingPhase.allowedWrongAnswers.Min.message=Training Phase pole 'allowedWrongAnswers' nemůže být menší než 0.
+#TASK
+task.title.NotEmpty.message=Task pole 'title' nemůže být prázdné.
+task.answer.NotEmpty.message=Task pole 'answer' nemůže být prázdné.
+task.answer.Size.message=Task pole 'answer' nemĹŻĹľe mĂ­t vĂ­c neĹľ 50 znakĹŻ.
+task.content.NotEmpty.message=Task pole 'content' nemůže být prázdné.
+task.solution.NotEmpty.message=Task pole 'solution' nemůže být prázdné.
+task.incorrectAnswerLimit.NotEmpty.message=Task pole 'incorrectAnswerLimit' nemůže být prázdné.
+task.incorrectAnswerLimit.Min.message=Task pole 'incorrectAnswerLimit' nemůže být menší než 0.
+task.incorrectAnswerLimit.Max.message=Task pole 'incorrectAnswerLimit' nemůže být vyšší než 100.
+task.sandboxChangeExpectedDuration.Min.message=Task pole 'sandboxChangeExpectedDuration' nemůže být menší než 0.
+task.order.NotNull.message=Task pole 'order' nemůže být null.
+task.order.Min.message=Task pole 'order' nemůže být menší než 0.
+#INFO PHASE
+info.content.NotEmpty.message=Info Level pole 'content' nemůže být prázdné.
+#VALIDATE ANSWER
+validateAnswer.answer.NotEmpty.message=Validate answer pole 'answer' nemůže být prázdné.
+#TRAINING DEFINITION
+trainingDefinition.title.NotEmpty.message=Training Definition pole 'title' nemůže být prázdné.
+trainingDefinition.state.NotNull.message=Training Definition pole 'state' nemůže být null.
+trainingDefinition.showStepperBar.NotNull.message=Training Definition pole 'showStepperBar' nemůže být null.
+trainingDefinition.id.NotNull.message=Training Definition pole 'id' nemůže být null.
+#TRAINING INSTANCE
+assignPool.poolId.NotNull.message=Training Instance Assign Pool pole 'poolId' nemůže být null.
+assignPool.poolId.Min.message=Training Instance Assign Pool pole 'poolId' nemůže být menší než 0.
+trainingInstance.id.NotNull.message=Training Instance pole 'id' nemůže být null.
+trainingInstance.startTime.NotNull.message=Training Instance pole 'startTime' nemůže být null.
+trainingInstance.endTime.NotNull.message=Training Instance pole 'endTime' nemůže být null.
+trainingInstance.title.NotEmpty.message=Training Instance pole 'title' nemůže být prázdné.
+trainingInstance.accessToken.NotEmpty.message=Training Instance pole 'accessToken' nemůže být prázdné.
+trainingInstance.trainingDefinitionId.NotNull.message=Training Instance pole 'trainingDefinition' nemůže být null.
+trainingInstance.trainingDefinitionId.Min.message=Training Instance pole 'trainingDefinitionId' nemůže být menší než 0.
+#SANDBOX DEFINITION
+sandboxDefinition.id.NotNull.message=Sandbox Definition Info field 'id' nemůže být null.
+sandboxDefinition.name.NotNull.message=Sandbox Definition Info field 'name' nemůže být null.
+sandboxDefinition.url.NotNull.message=Sandbox Definition Info field 'url' nemůže být null.
+sandboxDefinition.rev.NotNull.message=Sandbox Definition Info field 'rev' nemůže být null.
+#SANDBOX
+sandbox.id.NotNull.message=Sandbox Info field 'id' nemůže být null.
+#POOL INFO
+poolInfo.id.NotNull.message=Pool Info field 'id' nemůže být null.
+poolInfo.maxSize.NotNull.message=Pool Info field 'maxSize' nemůže být null.
+poolInfo.size.NotNull.message=Pool Info field 'size' nemůže být null.
+poolInfo.poolId.NotNull.message=Pool Id Info field 'poolId' nemůže být null.
+poolInfo.definitionId.NotNull.message=Pool Info field 'definitionId' nemůže být null.
+poolInfo.lockId.NotNull.message=Pool Info field 'lockId' nemůže být null.
+
diff --git a/src/main/resources/locale/ValidationMessages_sk_SK.properties b/src/main/resources/locale/ValidationMessages_sk_SK.properties
new file mode 100644
index 0000000000000000000000000000000000000000..a00fe9ec794eef3168b51b141b3e8767ed040318
--- /dev/null
+++ b/src/main/resources/locale/ValidationMessages_sk_SK.properties
@@ -0,0 +1,80 @@
+#PHASE
+phase.id.NotNull.message=Phase pole 'id' nemĂ´Ĺľe byĹĄ null.
+phase.title.NotEmpty.message=Phase pole 'title' nemôže byť prázdne.
+phase.phaseType.NotNull.message=Phase pole 'phaseType' nemĂ´Ĺľe byĹĄ null.
+phase.order.NotNull.message=Phase pole 'order' nemĂ´Ĺľe byĹĄ null.
+phase.order.Min.message=Phase pole 'order' nemôže byť nižšie než 0.
+#QUESTIONNAIRE PHASE
+questionnairePhase.questionnaireType.NotNull.message=Questionnaire Phase pole 'questionnaireType' nemĂ´Ĺľe byĹĄ null.
+questionnairePhaseRelation.order.NotNull.message=Questionnaire Phase Relation pole 'order' nemĂ´Ĺľe byĹĄ null.
+questionnairePhaseRelation.order.Min.message=Questionnaire Phase Relation pole 'order' nemôže byť nižšie než 0.
+questionnairePhaseRelation.questionIds.NotNull.message=Questionnaire Phase Relation pole 'questionIds' nemĂ´Ĺľe byĹĄ null.
+questionnairePhaseRelation.questionIds.Size.message=Questionnaire Phase Relation pole 'questionIds' must contain at least one value.
+questionnairePhaseRelation.phaseId.NotNull.message=Questionnaire Phase Relation pole 'phaseId' nemĂ´Ĺľe byĹĄ null.
+questionnairePhaseRelation.phaseId.Min.message=Questionnaire Phase Relation pole 'phaseId' nemôže byť nižšie než 0.
+questionnairePhaseRelation.successRate.Min.message=Questionnaire Phase Relation pole 'successRate' nemôže byť nižšie než 0.
+questionnairePhaseRelation.successRate.Max.message=Questionnaire Phase Relation pole 'successRate' nemôže byť vyššie než 100.
+#QUESTION
+question.order.NotNull.message=Question Level pole 'order' nemĂ´Ĺľe byĹĄ null.
+question.order.Min.message=Question pole 'order' nemôže byť nižšie než 0.
+question.text.NotEmpty.message=Question pole 'text' nemôže byť prázdne.
+question.questionType.NotNull.message=Question pole 'questionType' nemĂ´Ĺľe byĹĄ null.
+#QUESTION CHOICES
+questionChoices.text.NotEmpty.message=Question pole 'text' nemôže byť prázdne.
+questionChoices.correct.NotNull.message=Question pole 'correct' nemĂ´Ĺľe byĹĄ null.
+questionChoices.order.NotNull.message=Question pole 'order' nemĂ´Ĺľe byĹĄ null.
+questionChoices.order.Min.message=Question pole 'order' nemôže byť nižšie než 0.
+#TRAINING PHASE
+trainingPhase.id.NotNull.message=Training Phase pole 'id' nemĂ´Ĺľe byĹĄ null.
+trainingPhase.title.NotEmpty.message=Training Phase pole 'title' nemôže byť prázdne.
+trainingPhase.estimatedDuration.NotNull.message=Training Phase pole 'maxScore' nemĂ´Ĺľe byĹĄ null.
+trainingPhase.estimatedDuration.Min.message=Training Phase pole 'maxScore' nemôže byť nižšie než 0.
+trainingPhase.allowedCommands.NotNull.message=Training Phase pole 'allowedCommands' nemĂ´Ĺľe byĹĄ null.
+trainingPhase.allowedCommands.Min.message=Training Phase pole 'allowedCommands' nemôže byť nižšie než 0.
+trainingPhase.allowedWrongAnswers.NotNull.message=Training Phase pole 'allowedWrongAnswers' nemĂ´Ĺľe byĹĄ null.
+trainingPhase.allowedWrongAnswers.Min.message=Training Phase pole 'allowedWrongAnswers' nemôže byť nižšie než 0.
+#TASK
+task.title.NotEmpty.message=Task pole 'title' nemôže byť prázdne.
+task.answer.NotEmpty.message=Task pole 'answer' nemôže byť prázdne.
+task.answer.Size.message=Task pole 'answer' nemĂ´Ĺľe maĹĄ viac neĹľ 50 znakov.
+task.content.NotEmpty.message=Task pole 'content' nemôže byť prázdne.
+task.solution.NotEmpty.message=Task pole 'solution' nemôže byť prázdne.
+task.incorrectAnswerLimit.NotEmpty.message=Task pole 'incorrectAnswerLimit' nemôže byť prázdne.
+task.incorrectAnswerLimit.Min.message=Task pole 'incorrectAnswerLimit' nemôže byť nižšie než 0.
+task.incorrectAnswerLimit.Max.message=Task pole 'incorrectAnswerLimit' nemôže byť vyššie než 100.
+task.sandboxChangeExpectedDuration.Min.message=Task pole 'sandboxChangeExpectedDuration' nemôže byť nižšie než 0.
+task.order.NotNull.message=Task pole 'order' nemĂ´Ĺľe byĹĄ null.
+task.order.Min.message=Task pole 'order' nemôže byť nižšie než 0.
+#INFO PHASE
+info.content.NotEmpty.message=Info Level pole 'content' nemôže byť prázdne.
+#VALIDATE ANSWER
+validateAnswer.answer.NotEmpty.message=Validate answer pole 'answer' nemôže byť prázdne.
+#TRAINING DEFINITION
+trainingDefinition.title.NotEmpty.message=Training Definition pole 'title' nemôže byť prázdne.
+trainingDefinition.state.NotNull.message=Training Definition pole 'state' nemĂ´Ĺľe byĹĄ null.
+trainingDefinition.showStepperBar.NotNull.message=Training Definition pole 'showStepperBar' nemĂ´Ĺľe byĹĄ null.
+trainingDefinition.id.NotNull.message=Training Definition pole 'id' nemĂ´Ĺľe byĹĄ null.
+#TRAINING INSTANCE
+assignPool.poolId.NotNull.message=Training Instance Assign Pool pole 'poolId' nemĂ´Ĺľe byĹĄ null.
+assignPool.poolId.Min.message=Training Instance Assign Pool pole 'poolId' nemôže byť nižšie než 0.
+trainingInstance.id.NotNull.message=Training Instance pole 'id' nemĂ´Ĺľe byĹĄ null.
+trainingInstance.startTime.NotNull.message=Training Instance pole 'startTime' nemĂ´Ĺľe byĹĄ null.
+trainingInstance.endTime.NotNull.message=Training Instance pole 'endTime' nemĂ´Ĺľe byĹĄ null.
+trainingInstance.title.NotEmpty.message=Training Instance pole 'title' nemôže byť prázdne.
+trainingInstance.accessToken.NotEmpty.message=Training Instance pole 'accessToken' nemôže byť prázdne.
+trainingInstance.trainingDefinitionId.NotNull.message=Training Instance pole 'trainingDefinition' nemĂ´Ĺľe byĹĄ null.
+trainingInstance.trainingDefinitionId.Min.message=Training Instance pole 'trainingDefinitionId' nemôže byť nižšie než 0.
+#SANDBOX DEFINITION
+sandboxDefinition.id.NotNull.message=Sandbox Definition Info field 'id' nemĂ´Ĺľe byĹĄ null.
+sandboxDefinition.name.NotNull.message=Sandbox Definition Info field 'name' nemĂ´Ĺľe byĹĄ null.
+sandboxDefinition.url.NotNull.message=Sandbox Definition Info field 'url' nemĂ´Ĺľe byĹĄ null.
+sandboxDefinition.rev.NotNull.message=Sandbox Definition Info field 'rev' nemĂ´Ĺľe byĹĄ null.
+#SANDBOX
+sandbox.id.NotNull.message=Sandbox Info field 'id' nemĂ´Ĺľe byĹĄ null.
+#POOL INFO
+poolInfo.id.NotNull.message=Pool Info field 'id' nemĂ´Ĺľe byĹĄ null.
+poolInfo.maxSize.NotNull.message=Pool Info field 'maxSize' nemĂ´Ĺľe byĹĄ null.
+poolInfo.size.NotNull.message=Pool Info field 'size' nemĂ´Ĺľe byĹĄ null.
+poolInfo.poolId.NotNull.message=Pool Id Info field 'poolId' nemĂ´Ĺľe byĹĄ null.
+poolInfo.definitionId.NotNull.message=Pool Info field 'definitionId' nemĂ´Ĺľe byĹĄ null.
+poolInfo.lockId.NotNull.message=Pool Info field 'lockId' nemĂ´Ĺľe byĹĄ null.
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44f8d9fda50e08e6b91c94ddcbb2078982e5e44d
--- /dev/null
+++ b/src/main/resources/logback-spring.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml"/>
+
+    <property file="${spring.config.location}"/>
+
+    <logger name="org.springframework.boot" level="ERROR" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="guru.springframework.controllers" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="guru.springframework.helpers" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="org.springframework.boot" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+    <logger name="cz.muni.ics" level="WARN" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%-5level --- [%thread] %logger{5} --- %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AUDIT_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <charset>UTF-8</charset>
+            <pattern>${kypo.audit.messages.format:-kypo-training --- KYPO_PORTAL_EVENTS_AUDIT [%thread] %logger{5} ---
+                %msg%n}
+            </pattern>
+        </encoder>
+    </appender>
+
+    <appender name="KYPO_SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
+        <syslogHost>${kypo.audit.syslog.host:-localhost}</syslogHost>
+        <port>${kypo.audit.syslog.port:-514}</port>
+        <facility>AUDIT</facility>
+        <suffixPattern>${kypo.audit.messages.format:-kypo-training --- KYPO_PORTAL_EVENTS_AUDIT [%thread] %logger{5} ---
+            %msg%n}
+        </suffixPattern>
+    </appender>
+
+    <logger name="cz.muni.ics.kypo.training.adaptive.service.audit" level="INFO" additivity="false">
+        <appender-ref ref="AUDIT_CONSOLE"/>
+        <appender-ref ref="KYPO_SYSLOG"/>
+    </logger>
+
+
+</configuration>
+
+
+
+
diff --git a/src/main/resources/roles.json b/src/main/resources/roles.json
new file mode 100644
index 0000000000000000000000000000000000000000..f012d88bf1d7a245c2d84329acbdddc55fdee9e4
--- /dev/null
+++ b/src/main/resources/roles.json
@@ -0,0 +1,22 @@
+[
+  {
+    "role_type": "ROLE_ADAPTIVE_TRAINING_ADMINISTRATOR",
+    "default": false,
+    "description": "This role will allow you to do any operation in KYPO adaptive training microservice."
+  },
+  {
+    "role_type": "ROLE_ADAPTIVE_TRAINING_DESIGNER",
+    "default": false,
+    "description": "This role will allow you to manage, test, import and export adaptive training definitions in KYPO adaptive training microservice."
+  },
+  {
+    "role_type": "ROLE_ADAPTIVE_TRAINING_ORGANIZER",
+    "default": false,
+    "description": "This role will allow you to prepare adaptive training instances from adaptive training definitions and manage them, restart sandbox instances and view situational awareness in KYPO adaptive training microservice."
+  },
+  {
+    "role_type": "ROLE_ADAPTIVE_TRAINING_TRAINEE",
+    "default": true,
+    "description": "This role is default and will allow you to access adaptive training run and play a game in KYPO adaptive training microservice."
+  }
+]
diff --git a/src/test/java/cz/muni/ics/kypo/training/adaptive/DemoApplicationTests.java b/src/test/java/cz/muni/ics/kypo/training/adaptive/DemoApplicationTests.java
index 6a9221cfb2fb29b060a75d1d84162896236a21d9..53d7b0d9f3dca61608a2618b7c3b75c0bec1c5f1 100644
--- a/src/test/java/cz/muni/ics/kypo/training/adaptive/DemoApplicationTests.java
+++ b/src/test/java/cz/muni/ics/kypo/training/adaptive/DemoApplicationTests.java
@@ -3,11 +3,11 @@ package cz.muni.ics.kypo.training.adaptive;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
-@SpringBootTest
+@SpringBootTest(classes = TestAdaptiveTrainingConfiguration.class)
 class DemoApplicationTests {
 
-	@Test
-	void contextLoads() {
-	}
+    @Test
+    void contextLoads() {
+    }
 
 }
diff --git a/src/test/java/cz/muni/ics/kypo/training/adaptive/TestAdaptiveTrainingConfiguration.java b/src/test/java/cz/muni/ics/kypo/training/adaptive/TestAdaptiveTrainingConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..7190a3bce38908307fe1a58af35f86960052ecdd
--- /dev/null
+++ b/src/test/java/cz/muni/ics/kypo/training/adaptive/TestAdaptiveTrainingConfiguration.java
@@ -0,0 +1,8 @@
+package cz.muni.ics.kypo.training.adaptive;
+
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class TestAdaptiveTrainingConfiguration {
+
+}
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8f39588cc5852b801ed79d6181acc25ffa08e93f
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1 @@
+<configuration/>
\ No newline at end of file
diff --git a/supervisord.conf b/supervisord.conf
new file mode 100644
index 0000000000000000000000000000000000000000..78fa382bae7cff93e26637d5cfc4ed630b7134bc
--- /dev/null
+++ b/supervisord.conf
@@ -0,0 +1,49 @@
+; supervisor config file
+[unix_http_server]
+file=/var/run/supervisor.sock   ; (the path to the socket file)
+chmod=0700                       ; sockef file mode (default 0700)
+
+[supervisord]
+logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
+pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
+childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)
+nodaemon = true
+
+; the below section must remain in the config file for RPC
+; (supervisorctl/web interface) to work, additional interfaces may be
+; added by defining them in separate rpcinterface: sections
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[supervisorctl]
+serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket
+
+; The [include] section can just contain the "files" setting.  This
+; setting can list multiple files (separated by whitespace or
+; newlines).  It can also contain wildcards.  The filenames are
+; interpreted as relative to this file.  Included files *cannot*
+; include files themselves.
+
+[program:postgresql]
+command=/etc/init.d/postgresql start
+startsecs=5
+priority=1
+autostart=true
+autorestart=unexpected
+exitcodes=0
+startretries=5
+process_name=%(program_name)s-%(process_num)s
+stopsignal=TERM
+
+[program:adaptive-training-worker]
+command=/usr/local/openjdk-11/bin/java -Dspring.config.location=/app/etc/adaptive-adaptive-training.properties -jar /app/kypo-adaptive-training.jar
+process_name=%(program_name)s-%(process_num)s
+startsecs=15
+priority=999
+autostart=true
+autorestart=unexpected
+exitcodes=0
+startretries=10
+stopsignal=TERM
+stdout_logfile=/dev/stdout
+stdout_logfile_maxbytes=0