diff --git a/.gitignore b/.gitignore
index 40f55760b0..204ca95f52 100755
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,5 @@ view/target
 *.log
 fitnesse/target/
 daisy-integration/target/
+war/target/
+api/target/
diff --git a/GetToken.java b/GetToken.java
new file mode 100644
index 0000000000..68c0dee549
--- /dev/null
+++ b/GetToken.java
@@ -0,0 +1,101 @@
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Authenticator;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class GetToken {
+    public static void main(String[] args) throws IOException {
+        URI baseUri = URI.create("http://localhost:59733");
+        String clientId = "get-token";
+        String clientSecret = "get-token-secret";
+
+        System.out.println("Browse to " + baseUri.resolve("authorize?response_type=code&client_id=" + clientId));
+
+        HttpClient httpClient = HttpClient.newBuilder()
+                .authenticator(new Authenticator() {
+                    @Override
+                    protected PasswordAuthentication getPasswordAuthentication() {
+                        return new PasswordAuthentication(clientId, clientSecret.toCharArray());
+                    }
+                })
+                .build();
+
+        HttpServer httpServer = HttpServer.create();
+        httpServer.bind(new InetSocketAddress(59732), 0);
+
+        Thread thread = Thread.currentThread();
+
+        httpServer.createContext("/", exchange -> {
+            exchange.sendResponseHeaders(200, 0);
+            try (OutputStream responseBody = exchange.getResponseBody()) {
+                responseBody.write("All done, close tab".getBytes(StandardCharsets.UTF_8));
+            }
+
+            Map<String, List<String>> queryParams = getQueryParams(exchange);
+            String code = queryParams.get("code").get(0);
+            HttpRequest httpRequest = HttpRequest.newBuilder()
+                    .uri(baseUri.resolve("exchange"))
+                    .header("Content-Type", "application/x-www-form-urlencoded")
+                    .POST(HttpRequest.BodyPublishers.ofString("grant_type=authorization_code&code=" + code))
+                    .build();
+            try {
+                HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+                System.out.println(response.body());
+
+                // Try to copy the access token to the clipboard
+                Matcher matcher = Pattern.compile("access_token\":\"([^\"]+)\"")
+                        .matcher(response.body());
+                if (matcher.find()) {
+                    StringSelection clipboardData = new StringSelection(matcher.group(1));
+                    Toolkit.getDefaultToolkit()
+                            .getSystemClipboard()
+                            .setContents(clipboardData, clipboardData);
+                    try { Thread.sleep(1_000L); } catch (InterruptedException e) { }
+                    System.out.println("Access token copied to clipboard (probably)");
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                thread.interrupt();
+            }
+        });
+
+        httpServer.start();
+        try {
+            Thread.sleep(Duration.ofMinutes(1L).toMillis());
+            System.out.println("No authorization within one minute, exiting.");
+            System.exit(1);
+        } catch (InterruptedException ignored) {
+            // expected
+        }
+        httpServer.stop(0);
+    }
+
+    private static Map<String, List<String>> getQueryParams(final HttpExchange exchange) {
+        String query = exchange.getRequestURI()
+                .getQuery();
+        return Arrays.stream(query.split("&"))
+                .map(s -> s.split("="))
+                .collect(Collectors.groupingBy(
+                        split -> split[0],
+                        Collectors.mapping(split -> split[1], Collectors.toList())));
+    }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..749d07cb72
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+## Working with the API
+The API is protected by OAuth 2 acting as a [resource server](https://www.oauth.com/oauth2-servers/the-resource-server/)
+verifying tokens using [token introspection](https://datatracker.ietf.org/doc/html/rfc7662).
+
+When developing it uses a locally running instance of an
+[authorization server](https://datatracker.ietf.org/doc/html/rfc6749#section-1.1)
+that is run inside [Docker](https://www.docker.com). It can be started with `docker compose -f docker-compose.yml up`.
+Since there is no frontend to interact with the authorization server there's a helper script in
+[GetToken.java](GetToken.java) that can be run directly with `java GetToken.java` to run through the authorization flow
+and get an access token.
+
+Once the token has been obtained go to the [Swagger UI](http://localhost:8080/api/swagger) to interact with the API.
+Click the "Authorize" button in the top right and paste the access token to log in.
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000000..35c607e1b1
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,25 @@
+<?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"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>se.su.dsv.scipro</groupId>
+        <artifactId>SciPro</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>se.su.dsv.scipro</groupId>
+            <artifactId>core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/api/src/main/java/se/su/dsv/scipro/api/ApiController.java b/api/src/main/java/se/su/dsv/scipro/api/ApiController.java
new file mode 100644
index 0000000000..8059cdde66
--- /dev/null
+++ b/api/src/main/java/se/su/dsv/scipro/api/ApiController.java
@@ -0,0 +1,29 @@
+package se.su.dsv.scipro.api;
+
+import jakarta.inject.Inject;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.system.UserService;
+
+import java.util.Optional;
+
+@RestController
+public class ApiController {
+    private final UserService userService;
+
+    @Inject
+    public ApiController(UserService userService) {
+        this.userService = userService;
+    }
+
+    @GetMapping("/hello-world")
+    public String helloWorld(@RequestParam(value = "username", required = false) String username) {
+        String name = Optional.ofNullable(username)
+                .map(userService::findByUsername)
+                .map(User::getFullName)
+                .orElse("World");
+        return "Hello, " + name + "!";
+    }
+}
diff --git a/core/pom.xml b/core/pom.xml
index df548c1fad..a3c2a1a1e9 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -12,14 +12,6 @@
     <artifactId>core</artifactId>
 
     <dependencies>
-        <dependency>
-            <groupId>com.google.inject</groupId>
-            <artifactId>guice</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.inject.extensions</groupId>
-            <artifactId>guice-persist</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
@@ -62,12 +54,9 @@
             <groupId>org.glassfish.jersey.inject</groupId>
             <artifactId>jersey-hk2</artifactId>
         </dependency>
-
-        <!--Database stuff-->
         <dependency>
-            <groupId>org.hsqldb</groupId>
-            <artifactId>hsqldb</artifactId>
-            <scope>test</scope>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
         </dependency>
 
         <!--QueryDSL-->
@@ -87,10 +76,19 @@
             <groupId>jakarta.persistence</groupId>
             <artifactId>jakarta.persistence-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>jakarta.transaction</groupId>
+            <artifactId>jakarta.transaction-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.hibernate.orm</groupId>
             <artifactId>hibernate-core</artifactId>
-            <scope>runtime</scope>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <!--Additional stuff-->
@@ -130,8 +128,6 @@
             <version>4.0.5</version>
             <scope>runtime</scope>
         </dependency>
-
-
     </dependencies>
 
     <build>
diff --git a/core/src/main/java/modules/CoreModule.java b/core/src/main/java/modules/CoreModule.java
deleted file mode 100644
index 075c6e9ed8..0000000000
--- a/core/src/main/java/modules/CoreModule.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package modules;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.multibindings.Multibinder;
-import com.google.inject.multibindings.OptionalBinder;
-import se.su.dsv.scipro.activityplan.*;
-import se.su.dsv.scipro.checklist.*;
-import se.su.dsv.scipro.date.DateService;
-import se.su.dsv.scipro.date.DateServiceImpl;
-import se.su.dsv.scipro.events.EventModule;
-import se.su.dsv.scipro.finalseminar.*;
-import se.su.dsv.scipro.finalthesis.FinalThesisService;
-import se.su.dsv.scipro.finalthesis.FinalThesisServiceImpl;
-import se.su.dsv.scipro.finalthesis.PublishingConsentService;
-import se.su.dsv.scipro.finalthesis.PublishingConsentUnavailable;
-import se.su.dsv.scipro.firstmeeting.FirstMeetingService;
-import se.su.dsv.scipro.firstmeeting.FirstMeetingServiceImpl;
-import se.su.dsv.scipro.forum.ForumModule;
-import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
-import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsServiceImpl;
-import se.su.dsv.scipro.group.GroupFacade;
-import se.su.dsv.scipro.group.GroupFacadeImpl;
-import se.su.dsv.scipro.group.GroupService;
-import se.su.dsv.scipro.group.GroupServiceImpl;
-import se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarRepository;
-import se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarRepositoryImpl;
-import se.su.dsv.scipro.integration.activityforum.ActivityThreadRepository;
-import se.su.dsv.scipro.integration.activityforum.ActivityThreadRepositoryImpl;
-import se.su.dsv.scipro.mail.MailModule;
-import se.su.dsv.scipro.match.ApplicationPeriodFacade;
-import se.su.dsv.scipro.match.ApplicationPeriodFacadeImpl;
-import se.su.dsv.scipro.match.MatchModule;
-import se.su.dsv.scipro.milestones.service.*;
-import se.su.dsv.scipro.milestones.service.impl.MilestoneActivityTemplateServiceImpl;
-import se.su.dsv.scipro.milestones.service.impl.MilestonePhaseTemplateServiceImpl;
-import se.su.dsv.scipro.milestones.service.impl.MilestoneServiceImpl;
-import se.su.dsv.scipro.milestones.service.impl.MilestoneStatisticsServiceImpl;
-import se.su.dsv.scipro.misc.DaysService;
-import se.su.dsv.scipro.misc.DaysServiceImpl;
-import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodService;
-import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodServiceImpl;
-import se.su.dsv.scipro.notes.NoteService;
-import se.su.dsv.scipro.notes.NoteServiceImpl;
-import se.su.dsv.scipro.notifications.NotificationModule;
-import se.su.dsv.scipro.notifications.settings.service.DeliveryConfigurationService;
-import se.su.dsv.scipro.notifications.settings.service.DeliveryConfigurationServiceImpl;
-import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationService;
-import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServiceImpl;
-import se.su.dsv.scipro.peer.*;
-import se.su.dsv.scipro.plagiarism.*;
-import se.su.dsv.scipro.project.ProjectNoteService;
-import se.su.dsv.scipro.project.ProjectPeopleStatisticsService;
-import se.su.dsv.scipro.project.ProjectPeopleStatisticsServiceImpl;
-import se.su.dsv.scipro.project.ProjectService;
-import se.su.dsv.scipro.project.ProjectServiceImpl;
-import se.su.dsv.scipro.projectpartner.ProjectPartnerRepository;
-import se.su.dsv.scipro.projectpartner.ProjectPartnerRepositoryImpl;
-import se.su.dsv.scipro.projectpartner.ProjectPartnerService;
-import se.su.dsv.scipro.projectpartner.ProjectPartnerServiceImpl;
-import se.su.dsv.scipro.reflection.ReflectionModule;
-import se.su.dsv.scipro.report.*;
-import se.su.dsv.scipro.reviewing.ProjectFinalSeminarStatisticsService;
-import se.su.dsv.scipro.reviewing.ProjectFinalSeminarStatisticsServiceImpl;
-import se.su.dsv.scipro.springdata.serviceimpls.SupervisorServiceImpl;
-import se.su.dsv.scipro.springdata.serviceimpls.UnitServiceImpl;
-import se.su.dsv.scipro.springdata.serviceimpls.UserProfileServiceImpl;
-import se.su.dsv.scipro.springdata.services.SupervisorService;
-import se.su.dsv.scipro.springdata.services.UnitService;
-import se.su.dsv.scipro.springdata.services.UserProfileService;
-import se.su.dsv.scipro.system.*;
-import se.su.dsv.scipro.thesislink.ExternalLinkService;
-import se.su.dsv.scipro.thesislink.ExternalLinkServiceImpl;
-
-public class CoreModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        install(new RepositoryModule());
-        bind(FooterLinkService.class).to(FooterLinkServiceImpl.class);
-        bind(ActivityThreadRepository.class).to(ActivityThreadRepositoryImpl.class);
-        bind(ActivityFinalSeminarRepository.class).to(ActivityFinalSeminarRepositoryImpl.class);
-        bind(UserService.class).to(UserServiceImpl.class);
-        bind(MergeService.class).to(MergeServiceImpl.class);
-        bind(PasswordService.class).to(PasswordServiceImpl.class);
-        bind(GeneralSystemSettingsService.class).to(GeneralSystemSettingsServiceImpl.class);
-        bind(ProjectTypeService.class).to(ProjectTypeServiceImpl.class);
-        bind(UnitService.class).to(UnitServiceImpl.class);
-        bind(ResearchAreaService.class).to(ResearchAreaServiceImpl.class);
-        bind(DateService.class).to(DateServiceImpl.class);
-        bind(ActivityPlanFacade.class).to(ActivityPlanFacadeImpl.class);
-        bind(ProjectService.class).to(ProjectServiceImpl.class);
-        bind(ProjectFinalSeminarStatisticsService.class).to(ProjectFinalSeminarStatisticsServiceImpl.class);
-        bind(ProjectPeopleStatisticsService.class).to(ProjectPeopleStatisticsServiceImpl.class);
-        bind(DeliveryConfigurationService.class).to(DeliveryConfigurationServiceImpl.class);
-        bind(ReceiverConfigurationService.class).to(ReceiverConfigurationServiceImpl.class);
-        bind(ActivityService.class).to(ActivityServiceImpl.class);
-        bind(ActivityPlanService.class).to(ActivityPlanServiceImpl.class);
-        bind(ActivityPlanTemplateService.class).to(ActivityPlanTemplateServiceImpl.class);
-        bind(ChecklistService.class).to(ChecklistServiceImpl.class);
-        bind(UserProfileService.class).to(UserProfileServiceImpl.class);
-        bind(FinalSeminarService.class).to(FinalSeminarServiceImpl.class);
-        bind(FinalSeminarSettingsService.class).to(FinalSeminarSettingsServiceImpl.class);
-        bind(SupervisorService.class).to(SupervisorServiceImpl.class);
-        bind(DaysService.class).to(DaysServiceImpl.class);
-        bind(NonWorkDayPeriodService.class).to(NonWorkDayPeriodServiceImpl.class);
-        bind(FinalSeminarOppositionService.class).to(FinalSeminarOppositionServiceImpl.class);
-        bind(AuthorRepository.class).to(AuthorRepositoryImpl.class);
-        bind(OppositionReportService.class).to(OppositionReportServiceImpl.class);
-        bind(ApplicationPeriodFacade.class).to(ApplicationPeriodFacadeImpl.class);
-        bind(GroupFacade.class).to(GroupFacadeImpl.class);
-        bind(ExternalLinkService.class).to(ExternalLinkServiceImpl.class);
-        bind(PeerRequestService.class).to(PeerRequestServiceImpl.class);
-        bind(PeerReviewService.class).to(PeerReviewServiceImpl.class);
-        bind(MilestoneActivityTemplateService.class).to(MilestoneActivityTemplateServiceImpl.class);
-        bind(FinalThesisService.class).to(FinalThesisServiceImpl.class);
-        OptionalBinder.newOptionalBinder(binder(), PublishingConsentService.class)
-                        .setDefault().to(PublishingConsentUnavailable.class);
-        bind(ChecklistTemplateService.class).to(ChecklistTemplateServiceImpl.class);
-        bind(PeerPortal.class).to(PeerPortalImpl.class);
-        bind(FinalSeminarRespondentService.class).to(FinalSeminarRespondentServiceImpl.class);
-        bind(ProjectPartnerService.class).to(ProjectPartnerServiceImpl.class);
-        bind(GradingReportService.class).to(GradingReportServiceImpl.class);
-        bind(GradeCalculatorService.class).to(GradeCalculatorServiceImpl.class);
-        bind(UserNameService.class).to(UserNameServiceImpl.class);
-        bind(MileStoneService.class).to(MilestoneServiceImpl.class);
-        bind(MilestoneStatisticsService.class).to(MilestoneStatisticsServiceImpl.class);
-        bind(MilestonePhaseTemplateService.class).to(MilestonePhaseTemplateServiceImpl.class);
-        bind(ReportService.class).to(ReportServiceImpl.class);
-        bind(CommentThreadService.class).to(CommentThreadServiceImpl.class);
-        bind(CommentService.class).to(CommentServiceImpl.class);
-        bind(PerformReviewService.class).to(PeerPortalImpl.class);
-        bind(EventService.class).to(EventServiceImpl.class);
-        bind(ChecklistAnswerService.class).to(ChecklistAnswerServiceImpl.class);
-        bind(FinalSeminarUploadController.class).to(FinalSeminarUploadControllerImpl.class);
-        bind(FinalSeminarActiveParticipationService.class).to(FinalSeminarActiveParticipationServiceImpl.class);
-        bind(ExternalResourceService.class).to(ExternalResourceServiceImpl.class);
-        bind(GroupService.class).to(GroupServiceImpl.class);
-        bind(NoteService.class).to(NoteServiceImpl.class);
-        bind(MilestoneActivator.class).asEagerSingleton();
-        bind(ActivateCompletedMilestonesOnNewProjects.class).asEagerSingleton();
-        bind(FirstMeetingService.class).to(FirstMeetingServiceImpl.class);
-        bind(FinalSeminarCreationSubscribers.class).asEagerSingleton();
-        bind(ProjectPartnerRepository.class).to(ProjectPartnerRepositoryImpl.class);
-        bind(ProjectNoteService.class).to(ProjectServiceImpl.class);
-
-        install(new PlagiarismModule());
-        install(new NotificationModule());
-        install(new ProfileModule());
-        install(new EventModule());
-        install(new MatchModule());
-        install(new MailModule());
-        install(new ForumModule());
-        install(new ReflectionModule());
-
-        Multibinder.newSetBinder(binder(), UserImportService.class);
-        bind(UserSearchService.class).to(AggregateUserSearch.class);
-        Multibinder.newSetBinder(binder(), UserSearchProvider.class)
-                .addBinding().to(LocalUserSearch.class);
-
-    }
-}
diff --git a/core/src/main/java/modules/ProfileModule.java b/core/src/main/java/modules/ProfileModule.java
deleted file mode 100644
index 16d276dba8..0000000000
--- a/core/src/main/java/modules/ProfileModule.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package modules;
-
-import com.google.inject.Key;
-import com.google.inject.PrivateModule;
-import com.google.inject.name.Names;
-import se.su.dsv.scipro.profiles.CurrentProfile;
-
-public class ProfileModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        requireBinding(Key.get(String.class, Names.named("profile")));
-        bind(CurrentProfile.class).asEagerSingleton();
-        expose(CurrentProfile.class);
-    }
-}
diff --git a/core/src/main/java/modules/RepositoryModule.java b/core/src/main/java/modules/RepositoryModule.java
deleted file mode 100644
index 2ae13bc0cb..0000000000
--- a/core/src/main/java/modules/RepositoryModule.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package modules;
-
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.checklist.ChecklistCategoryRepo;
-import se.su.dsv.scipro.checklist.ChecklistCategoryRepoImpl;
-import se.su.dsv.scipro.checklist.ChecklistQuestionRepo;
-import se.su.dsv.scipro.checklist.ChecklistQuestionRepoImpl;
-import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepository;
-import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepositoryImpl;
-import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepo;
-import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepoImpl;
-import se.su.dsv.scipro.finalseminar.FinalSeminarRepository;
-import se.su.dsv.scipro.finalseminar.FinalSeminarRepositoryImpl;
-import se.su.dsv.scipro.milestones.MilestoneActivityTemplateRepository;
-import se.su.dsv.scipro.milestones.MilestoneActivityTemplateRepositoryImpl;
-import se.su.dsv.scipro.peer.CommentThreadRepo;
-import se.su.dsv.scipro.peer.CommentThreadRepoImpl;
-import se.su.dsv.scipro.peer.PeerRequestRepository;
-import se.su.dsv.scipro.peer.PeerRequestRepositoryImpl;
-import se.su.dsv.scipro.peer.PeerReviewRepository;
-import se.su.dsv.scipro.peer.PeerReviewRepositoryImpl;
-import se.su.dsv.scipro.project.ProjectRepo;
-import se.su.dsv.scipro.project.ProjectRepoImpl;
-import se.su.dsv.scipro.report.GradingReportTemplateRepo;
-import se.su.dsv.scipro.report.GradingReportTemplateRepoImpl;
-import se.su.dsv.scipro.report.OppositionReportRepo;
-import se.su.dsv.scipro.report.OppositionReportRepoImpl;
-import se.su.dsv.scipro.report.SupervisorGradingReportRepository;
-import se.su.dsv.scipro.report.SupervisorGradingReportRepositoryImpl;
-import se.su.dsv.scipro.reviewing.DecisionRepository;
-import se.su.dsv.scipro.reviewing.DecisionRepositoryImpl;
-import se.su.dsv.scipro.reviewing.ReviewerTargetRepository;
-import se.su.dsv.scipro.reviewing.ReviewerTargetRepositoryImpl;
-import se.su.dsv.scipro.system.FooterAddressRepo;
-import se.su.dsv.scipro.system.FooterAddressRepoImpl;
-import se.su.dsv.scipro.system.FooterLinkRepo;
-import se.su.dsv.scipro.system.FooterLinkRepoImpl;
-import se.su.dsv.scipro.system.PasswordRepo;
-import se.su.dsv.scipro.system.PasswordRepoImpl;
-import se.su.dsv.scipro.system.UserRepo;
-import se.su.dsv.scipro.system.UserRepoImpl;
-
-public class RepositoryModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(ChecklistQuestionRepo.class).to(ChecklistQuestionRepoImpl.class);
-        bind(FinalSeminarOppositionRepo.class).to(FinalSeminarOppositionRepoImpl.class);
-        bind(FinalSeminarActiveParticipationRepository.class).to(FinalSeminarActiveParticipationRepositoryImpl.class);
-        bind(GradingReportTemplateRepo.class).to(GradingReportTemplateRepoImpl.class);
-        bind(MilestoneActivityTemplateRepository.class).to(MilestoneActivityTemplateRepositoryImpl.class);
-        bind(OppositionReportRepo.class).to(OppositionReportRepoImpl.class);
-        bind(PasswordRepo.class).to(PasswordRepoImpl.class);
-        bind(ProjectRepo.class).to(ProjectRepoImpl.class);
-        bind(UserRepo.class).to(UserRepoImpl.class);
-        bind(PeerReviewRepository.class).to(PeerReviewRepositoryImpl.class);
-        bind(PeerRequestRepository.class).to(PeerRequestRepositoryImpl.class);
-        bind(ChecklistCategoryRepo.class).to(ChecklistCategoryRepoImpl.class);
-        bind(CommentThreadRepo.class).to(CommentThreadRepoImpl.class);
-        bind(FooterLinkRepo.class).to(FooterLinkRepoImpl.class);
-        bind(FooterAddressRepo.class).to(FooterAddressRepoImpl.class);
-        bind(FinalSeminarRepository.class).to(FinalSeminarRepositoryImpl.class);
-        bind(ReviewerTargetRepository.class).to(ReviewerTargetRepositoryImpl.class);
-        bind(DecisionRepository.class).to(DecisionRepositoryImpl.class);
-        bind(SupervisorGradingReportRepository.class).to(SupervisorGradingReportRepositoryImpl.class);
-    }
-}
\ No newline at end of file
diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java
new file mode 100644
index 0000000000..d9245d2155
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java
@@ -0,0 +1,1012 @@
+package se.su.dsv.scipro;
+
+import com.google.common.eventbus.EventBus;
+import jakarta.inject.Provider;
+import jakarta.persistence.EntityManager;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import se.su.dsv.scipro.activityplan.ActivityPlanFacade;
+import se.su.dsv.scipro.activityplan.ActivityPlanFacadeImpl;
+import se.su.dsv.scipro.activityplan.ActivityPlanServiceImpl;
+import se.su.dsv.scipro.activityplan.ActivityPlanTemplateServiceImpl;
+import se.su.dsv.scipro.activityplan.ActivityServiceImpl;
+import se.su.dsv.scipro.checklist.ChecklistAnswerServiceImpl;
+import se.su.dsv.scipro.checklist.ChecklistServiceImpl;
+import se.su.dsv.scipro.checklist.ChecklistTemplateService;
+import se.su.dsv.scipro.checklist.ChecklistTemplateServiceImpl;
+import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl;
+import se.su.dsv.scipro.date.DateServiceImpl;
+import se.su.dsv.scipro.file.FileDescriptionRepo;
+import se.su.dsv.scipro.file.FileReferenceRepository;
+import se.su.dsv.scipro.file.FileService;
+import se.su.dsv.scipro.file.FileServiceImpl;
+import se.su.dsv.scipro.file.FileStore;
+import se.su.dsv.scipro.file.ProjectFileRepository;
+import se.su.dsv.scipro.file.ProjectFileService;
+import se.su.dsv.scipro.file.ProjectFileServiceImpl;
+import se.su.dsv.scipro.finalseminar.AuthorRepository;
+import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepository;
+import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationServiceImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarCreationSubscribers;
+import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepo;
+import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionServiceImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarRepository;
+import se.su.dsv.scipro.finalseminar.FinalSeminarRespondentServiceImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarService;
+import se.su.dsv.scipro.finalseminar.FinalSeminarServiceImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarSettingsService;
+import se.su.dsv.scipro.finalseminar.FinalSeminarSettingsServiceImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarUploadControllerImpl;
+import se.su.dsv.scipro.finalthesis.FinalThesisService;
+import se.su.dsv.scipro.finalthesis.FinalThesisServiceImpl;
+import se.su.dsv.scipro.firstmeeting.FirstMeetingServiceImpl;
+import se.su.dsv.scipro.forum.AbstractThreadRepository;
+import se.su.dsv.scipro.forum.BasicForumService;
+import se.su.dsv.scipro.forum.BasicForumServiceImpl;
+import se.su.dsv.scipro.forum.ForumPostReadStateRepository;
+import se.su.dsv.scipro.forum.ForumPostRepository;
+import se.su.dsv.scipro.forum.GroupForumService;
+import se.su.dsv.scipro.forum.GroupForumServiceImpl;
+import se.su.dsv.scipro.forum.GroupThreadRepository;
+import se.su.dsv.scipro.forum.ProjectForumService;
+import se.su.dsv.scipro.forum.ProjectForumServiceImpl;
+import se.su.dsv.scipro.forum.ProjectThreadRepository;
+import se.su.dsv.scipro.forum.notifications.ForumNotificationRepository;
+import se.su.dsv.scipro.forum.notifications.ForumNotifications;
+import se.su.dsv.scipro.gdpr.ZipReporter;
+import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
+import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsServiceImpl;
+import se.su.dsv.scipro.grading.GradingHistory;
+import se.su.dsv.scipro.grading.GradingHistoryEventRepository;
+import se.su.dsv.scipro.grading.GradingServiceImpl;
+import se.su.dsv.scipro.grading.NationalSubjectCategoryRepository;
+import se.su.dsv.scipro.grading.NationalSubjectCategoryServiceImpl;
+import se.su.dsv.scipro.grading.PublicationMetadataRepository;
+import se.su.dsv.scipro.grading.PublicationMetadataServiceImpl;
+import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
+import se.su.dsv.scipro.group.GroupService;
+import se.su.dsv.scipro.group.GroupServiceImpl;
+import se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarRepository;
+import se.su.dsv.scipro.integration.activityfinalseminar.FinalSeminarActivityHandler;
+import se.su.dsv.scipro.integration.activityforum.ActivityThreadRepository;
+import se.su.dsv.scipro.integration.activityforum.PostActivityUploadToForum;
+import se.su.dsv.scipro.mail.MailEventService;
+import se.su.dsv.scipro.mail.MailEventServiceImpl;
+import se.su.dsv.scipro.match.AddActivityPlanOnProjectStart;
+import se.su.dsv.scipro.match.ApplicationPeriodFacadeImpl;
+import se.su.dsv.scipro.match.ApplicationPeriodProjectTypeServiceImpl;
+import se.su.dsv.scipro.match.ApplicationPeriodService;
+import se.su.dsv.scipro.match.ApplicationPeriodServiceImpl;
+import se.su.dsv.scipro.match.FirstMeetingRepository;
+import se.su.dsv.scipro.match.IdeaRepository;
+import se.su.dsv.scipro.match.IdeaService;
+import se.su.dsv.scipro.match.IdeaServiceImpl;
+import se.su.dsv.scipro.match.KeywordServiceImpl;
+import se.su.dsv.scipro.match.MatchFollowUpServiceImpl;
+import se.su.dsv.scipro.match.MatchServiceImpl;
+import se.su.dsv.scipro.match.PreliminaryMatchServiceImpl;
+import se.su.dsv.scipro.match.ProgramServiceImpl;
+import se.su.dsv.scipro.match.ProjectStartNotifier;
+import se.su.dsv.scipro.match.TargetRepository;
+import se.su.dsv.scipro.match.TargetServiceImpl;
+import se.su.dsv.scipro.milestones.MilestoneActivityTemplateRepository;
+import se.su.dsv.scipro.milestones.service.ActivateCompletedMilestonesOnNewProjects;
+import se.su.dsv.scipro.milestones.service.MilestoneActivityTemplateService;
+import se.su.dsv.scipro.milestones.service.impl.MilestoneActivityTemplateServiceImpl;
+import se.su.dsv.scipro.milestones.service.impl.MilestonePhaseTemplateServiceImpl;
+import se.su.dsv.scipro.milestones.service.impl.MilestoneServiceImpl;
+import se.su.dsv.scipro.milestones.service.impl.MilestoneStatisticsServiceImpl;
+import se.su.dsv.scipro.misc.DaysService;
+import se.su.dsv.scipro.misc.DaysServiceImpl;
+import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodService;
+import se.su.dsv.scipro.nonworkperiod.NonWorkDayPeriodServiceImpl;
+import se.su.dsv.scipro.notes.NoteServiceImpl;
+import se.su.dsv.scipro.notifications.NotificationController;
+import se.su.dsv.scipro.notifications.NotificationControllerImpl;
+import se.su.dsv.scipro.notifications.NotificationEventRepository;
+import se.su.dsv.scipro.notifications.NotificationEventServiceImpl;
+import se.su.dsv.scipro.notifications.NotificationService;
+import se.su.dsv.scipro.notifications.NotificationServiceImpl;
+import se.su.dsv.scipro.notifications.Notifications;
+import se.su.dsv.scipro.notifications.interfaces.NotificationMailFormatter;
+import se.su.dsv.scipro.notifications.interfaces.impl.NotificationMailFormatterImpl;
+import se.su.dsv.scipro.notifications.settings.service.DeliveryConfigurationService;
+import se.su.dsv.scipro.notifications.settings.service.DeliveryConfigurationServiceImpl;
+import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationService;
+import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServiceImpl;
+import se.su.dsv.scipro.oauth.OAuthServiceImpl;
+import se.su.dsv.scipro.oauth.OAuthSettings;
+import se.su.dsv.scipro.peer.CommentServiceImpl;
+import se.su.dsv.scipro.peer.CommentThreadRepo;
+import se.su.dsv.scipro.peer.CommentThreadServiceImpl;
+import se.su.dsv.scipro.peer.PeerPortalImpl;
+import se.su.dsv.scipro.peer.PeerRequestRepository;
+import se.su.dsv.scipro.peer.PeerRequestService;
+import se.su.dsv.scipro.peer.PeerRequestServiceImpl;
+import se.su.dsv.scipro.peer.PeerReviewRepository;
+import se.su.dsv.scipro.peer.PeerReviewService;
+import se.su.dsv.scipro.peer.PeerReviewServiceImpl;
+import se.su.dsv.scipro.plagiarism.PlagiarismControl;
+import se.su.dsv.scipro.plagiarism.PlagiarismControlImpl;
+import se.su.dsv.scipro.plagiarism.PlagiarismRequestRepository;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundApi;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundApiImpl;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundServiceImpl;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSettingsRepository;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSubmissionRepository;
+import se.su.dsv.scipro.project.ProjectPeopleStatisticsServiceImpl;
+import se.su.dsv.scipro.project.ProjectRepo;
+import se.su.dsv.scipro.project.ProjectService;
+import se.su.dsv.scipro.project.ProjectServiceImpl;
+import se.su.dsv.scipro.projectpartner.ProjectPartnerServiceImpl;
+import se.su.dsv.scipro.reflection.ReflectionService;
+import se.su.dsv.scipro.reflection.ReflectionServiceImpl;
+import se.su.dsv.scipro.report.GradeCalculatorServiceImpl;
+import se.su.dsv.scipro.report.GradingReportService;
+import se.su.dsv.scipro.report.GradingReportServiceImpl;
+import se.su.dsv.scipro.report.GradingReportTemplateRepo;
+import se.su.dsv.scipro.report.GradingReportTemplateRepoImpl;
+import se.su.dsv.scipro.report.OppositionReportRepo;
+import se.su.dsv.scipro.report.OppositionReportServiceImpl;
+import se.su.dsv.scipro.report.ReportServiceImpl;
+import se.su.dsv.scipro.report.SupervisorGradingReportRepository;
+import se.su.dsv.scipro.reviewing.DecisionRepository;
+import se.su.dsv.scipro.reviewing.FinalSeminarApprovalService;
+import se.su.dsv.scipro.reviewing.FinalSeminarApprovalServiceImpl;
+import se.su.dsv.scipro.reviewing.ProjectFinalSeminarStatisticsServiceImpl;
+import se.su.dsv.scipro.reviewing.ReviewerAssignedDeadline;
+import se.su.dsv.scipro.reviewing.ReviewerCapacityServiceImpl;
+import se.su.dsv.scipro.reviewing.ReviewerDeadlineFollowupServiceImpl;
+import se.su.dsv.scipro.reviewing.ReviewerDeadlineSettingsRepository;
+import se.su.dsv.scipro.reviewing.ReviewerDeadlineSettingsService;
+import se.su.dsv.scipro.reviewing.ReviewerDeadlineSettingsServiceImpl;
+import se.su.dsv.scipro.reviewing.ReviewerInteractionService;
+import se.su.dsv.scipro.reviewing.ReviewerInteractionServiceImpl;
+import se.su.dsv.scipro.reviewing.ReviewerTargetRepository;
+import se.su.dsv.scipro.reviewing.ReviewerThreadRepository;
+import se.su.dsv.scipro.reviewing.ReviewingServiceImpl;
+import se.su.dsv.scipro.reviewing.RoughDraftApprovalService;
+import se.su.dsv.scipro.reviewing.RoughDraftApprovalServiceImpl;
+import se.su.dsv.scipro.security.auth.AuthenticationProvider;
+import se.su.dsv.scipro.security.auth.AuthenticationServiceImpl;
+import se.su.dsv.scipro.security.auth.LocalAuthentication;
+import se.su.dsv.scipro.springdata.serviceimpls.SupervisorServiceImpl;
+import se.su.dsv.scipro.springdata.serviceimpls.UnitServiceImpl;
+import se.su.dsv.scipro.springdata.serviceimpls.UserProfileServiceImpl;
+import se.su.dsv.scipro.springdata.services.UserProfileService;
+import se.su.dsv.scipro.sukat.LDAP;
+import se.su.dsv.scipro.sukat.Sukat;
+import se.su.dsv.scipro.survey.QuestionRepository;
+import se.su.dsv.scipro.survey.SurveyRepository;
+import se.su.dsv.scipro.survey.SurveyServiceImpl;
+import se.su.dsv.scipro.system.CurrentUser;
+import se.su.dsv.scipro.system.EventServiceImpl;
+import se.su.dsv.scipro.system.ExternalResourceServiceImpl;
+import se.su.dsv.scipro.system.FooterLinkRepo;
+import se.su.dsv.scipro.system.FooterLinkServiceImpl;
+import se.su.dsv.scipro.system.LocalUserSearch;
+import se.su.dsv.scipro.system.PasswordRepo;
+import se.su.dsv.scipro.system.PasswordService;
+import se.su.dsv.scipro.system.PasswordServiceImpl;
+import se.su.dsv.scipro.system.ProjectTypeService;
+import se.su.dsv.scipro.system.ProjectTypeServiceImpl;
+import se.su.dsv.scipro.system.ResearchAreaServiceImpl;
+import se.su.dsv.scipro.system.UserNameServiceImpl;
+import se.su.dsv.scipro.system.UserRepo;
+import se.su.dsv.scipro.system.UserService;
+import se.su.dsv.scipro.system.UserServiceImpl;
+import se.su.dsv.scipro.thesislink.ExternalLinkServiceImpl;
+import se.su.dsv.scipro.workerthreads.WorkerDataServiceImpl;
+
+import java.time.Clock;
+import java.util.Set;
+
+@Configuration(proxyBeanMethods = false)
+public class CoreConfig {
+    @Bean
+    public EventBus eventBus() {
+        return new EventBus();
+    }
+
+    @Bean
+    public GradingHistory gradingHistory(GradingHistoryEventRepository gradingHistoryEventRepository) {
+        return new GradingHistory(gradingHistoryEventRepository);
+    }
+
+    @Bean
+    public OAuthSettings oAuthSettings(
+            @Value("${oauth.uri}") String uri,
+            @Value("${oauth.redirectUri}") String redirectUri,
+            @Value("${oauth.clientId}") String clientId,
+            @Value("${oauth.clientSecret}") String clientSecret)
+    {
+        return new OAuthSettings(uri, redirectUri, clientId, clientSecret);
+    }
+
+    @Bean
+    public Sukat sukat() {
+        return new LDAP();
+    }
+
+    @Bean
+    public DaisyAPIImpl daisyAPI(
+            @Value("${daisy.api.url}") final String baseUrl,
+            @Value("${daisy.api.username}") final String user,
+            @Value("${daisy.api.password}") final String password)
+    {
+        return new DaisyAPIImpl(baseUrl, user, password);
+    }
+
+    @Bean
+    public GradingServiceImpl gradingService(@Value("${service.grading.url}") final String url) {
+        return new GradingServiceImpl(url);
+    }
+
+    @Bean
+    public OAuthServiceImpl oAuthService(OAuthSettings settings) {
+        return new OAuthServiceImpl(settings);
+    }
+
+    @Bean
+    public FinalSeminarApprovalServiceImpl finalSeminarApprovalService(
+            Provider<EntityManager> em,
+            FileService fileDescriptionService,
+            EventBus eventBus,
+            DaysService daysService,
+            ReviewerDeadlineSettingsService reviewerDeadlineSettingsService)
+    {
+        return new FinalSeminarApprovalServiceImpl(em, fileDescriptionService, eventBus, daysService, reviewerDeadlineSettingsService);
+    }
+
+    @Bean
+    public RoughDraftApprovalServiceImpl roughDraftApprovalService(
+            Provider<EntityManager> em,
+            FileService fileDescriptionService,
+            EventBus eventBus,
+            DaysService daysService,
+            ReviewerDeadlineSettingsService reviewerDeadlineSettingsService)
+    {
+        return new RoughDraftApprovalServiceImpl(em, eventBus, fileDescriptionService, daysService, reviewerDeadlineSettingsService);
+    }
+
+    @Bean
+    public ActivityPlanServiceImpl activityPlanService(Provider<EntityManager> em) {
+        return new ActivityPlanServiceImpl(em);
+    }
+
+    @Bean
+    public ActivityPlanTemplateServiceImpl activityPlanTemplateService(Provider<EntityManager> em) {
+        return new ActivityPlanTemplateServiceImpl(em);
+    }
+
+    @Bean
+    public ActivityServiceImpl activityService(Provider<EntityManager> em) {
+        return new ActivityServiceImpl(em);
+    }
+
+    @Bean
+    public ActivityPlanFacadeImpl activityPlanFacade(
+            EventBus eventBus,
+            ProjectFileService projectFileService,
+            ChecklistTemplateService checklistTemplateService,
+            DaysService daysService,
+            FileService fileService
+    )
+    {
+        return new ActivityPlanFacadeImpl(eventBus, projectFileService, checklistTemplateService, daysService,
+                fileService);
+    }
+
+    @Bean
+    public ApplicationPeriodFacadeImpl applicationPeriodFacade() {
+        return new ApplicationPeriodFacadeImpl();
+    }
+
+    @Bean
+    public ApplicationPeriodProjectTypeServiceImpl applicationPeriodProjectTypeService(Provider<EntityManager> em) {
+        return new ApplicationPeriodProjectTypeServiceImpl(em);
+    }
+
+    @Bean
+    public ApplicationPeriodServiceImpl applicationPeriodService(Provider<EntityManager> em, Clock clock) {
+        return new ApplicationPeriodServiceImpl(em, clock);
+    }
+
+    @Bean
+    public AuthenticationServiceImpl authenticationService(Set<AuthenticationProvider> authenticationProviders) {
+        return new AuthenticationServiceImpl(authenticationProviders);
+    }
+
+    @Bean
+    public LocalAuthentication localAuthentication(
+            UserService userService,
+            PasswordService passwordService)
+    {
+        return new LocalAuthentication(userService, passwordService);
+    }
+
+    @Bean
+    public BasicForumServiceImpl basicForumService(
+            ForumPostRepository forumPostRepository,
+            ForumPostReadStateRepository readStateRepository,
+            AbstractThreadRepository threadRepository,
+            FileService fileService,
+            EventBus eventBus)
+    {
+        return new BasicForumServiceImpl(forumPostRepository, readStateRepository, threadRepository, fileService,
+                eventBus);
+    }
+
+    @Bean
+    public ChecklistAnswerServiceImpl checklistAnswerService(Provider<EntityManager> em) {
+        return new ChecklistAnswerServiceImpl(em);
+    }
+
+    @Bean
+    public ChecklistServiceImpl checklistService(Provider<EntityManager> em) {
+        return new ChecklistServiceImpl(em);
+    }
+
+    @Bean
+    public ChecklistTemplateServiceImpl checklistTemplateService(Provider<EntityManager> em) {
+        return new ChecklistTemplateServiceImpl(em);
+    }
+
+    @Bean
+    public CommentServiceImpl commentService(Provider<EntityManager> em) {
+        return new CommentServiceImpl(em);
+    }
+
+    @Bean
+    public DateServiceImpl dateService() {
+        return new DateServiceImpl();
+    }
+
+    @Bean
+    public DaysServiceImpl daysService(NonWorkDayPeriodService nonWorkDayPeriodService) {
+        return new DaysServiceImpl(nonWorkDayPeriodService);
+    }
+
+    @Bean
+    public DeliveryConfigurationServiceImpl deliveryConfigurationService(
+            Provider<EntityManager> em,
+            UserProfileService userProfileService)
+    {
+        return new DeliveryConfigurationServiceImpl(em, userProfileService);
+    }
+
+    @Bean
+    public EventServiceImpl eventService(Provider<EntityManager> em) {
+        return new EventServiceImpl(em);
+    }
+
+    @Bean
+    public ExternalLinkServiceImpl externalLinkService(Provider<EntityManager> em) {
+        return new ExternalLinkServiceImpl(em);
+    }
+
+    @Bean
+    public ExternalResourceServiceImpl externalResourceService(Provider<EntityManager> em) {
+        return new ExternalResourceServiceImpl(em);
+    }
+
+    @Bean
+    public FileServiceImpl fileService(
+            Provider<EntityManager> em,
+            FileStore fileStore,
+            FileReferenceRepository fileReferenceRepository,
+            FileDescriptionRepo fileDescriptionRepository)
+    {
+        return new FileServiceImpl(em, fileReferenceRepository, fileDescriptionRepository, fileStore);
+    }
+
+    @Bean
+    public FinalSeminarActiveParticipationServiceImpl finalSeminarActiveParticipationService(Provider<EntityManager> em) {
+        return new FinalSeminarActiveParticipationServiceImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarOppositionServiceImpl finalSeminarOppositionService(Provider<EntityManager> em) {
+        return new FinalSeminarOppositionServiceImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarRespondentServiceImpl finalSeminarRespondentService(Provider<EntityManager> em) {
+        return new FinalSeminarRespondentServiceImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarServiceImpl finalSeminarService(
+            Provider<EntityManager> em,
+            EventBus eventBus,
+            FileService fileService,
+            AuthorRepository authorRepository,
+            FinalSeminarOppositionRepo finalSeminarOppositionRepository,
+            FinalSeminarActiveParticipationRepository finalSeminarActiveParticipationRepository,
+            FinalSeminarRepository finalSeminarRepository,
+            Clock clock,
+            RoughDraftApprovalService roughDraftApprovalService)
+    {
+        return new FinalSeminarServiceImpl(
+                em,
+                eventBus,
+                authorRepository,
+                fileService,
+                finalSeminarOppositionRepository,
+                finalSeminarActiveParticipationRepository,
+                finalSeminarRepository,
+                clock,
+                roughDraftApprovalService);
+    }
+
+    @Bean
+    public FinalSeminarSettingsServiceImpl finalSeminarSettingsService(Provider<EntityManager> em) {
+        return new FinalSeminarSettingsServiceImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarUploadControllerImpl finalSeminarUploadController(
+            EventBus eventBus,
+            FileService fileService,
+            FinalSeminarService finalSeminarService,
+            ProjectFileService projectFileService,
+            ProjectService projectService,
+            FinalSeminarSettingsService finalSeminarSettingsService,
+            PlagiarismControl plagiarismControl)
+    {
+        return new FinalSeminarUploadControllerImpl(projectService, fileService, finalSeminarSettingsService,
+                finalSeminarService, eventBus, projectFileService, plagiarismControl);
+    }
+
+    @Bean
+    public FinalThesisServiceImpl finalThesisService(
+            Provider<EntityManager> em,
+            NotificationController notification,
+            ProjectFileService projectFile,
+            FileService fileService,
+            EventBus eventBus,
+            PlagiarismControl plagiarismControl,
+            GradingReportService gradingReportService)
+    {
+        return new FinalThesisServiceImpl(em, notification, projectFile,
+                fileService, eventBus, plagiarismControl, gradingReportService);
+    }
+
+    @Bean
+    public FirstMeetingServiceImpl firstMeetingService(
+            Provider<EntityManager> em,
+            ActivityPlanFacade activityPlanFacade)
+    {
+        return new FirstMeetingServiceImpl(em, activityPlanFacade);
+    }
+
+    @Bean
+    public FooterLinkServiceImpl footerLinkService(FooterLinkRepo footerLinkRepository) {
+        return new FooterLinkServiceImpl(footerLinkRepository);
+    }
+
+    @Bean
+    public GeneralSystemSettingsServiceImpl generalSystemSettingsService(Provider<EntityManager> em) {
+        return new GeneralSystemSettingsServiceImpl(em);
+    }
+
+    @Bean
+    public GradeCalculatorServiceImpl gradeCalculatorService(GradingReportService gradingReportService) {
+        return new GradeCalculatorServiceImpl(gradingReportService);
+    }
+
+    @Bean
+    public GradingReportServiceImpl gradingReportService(
+            EventBus eventBus,
+            ThesisSubmissionHistoryService thesisSubmissionHistoryService,
+            Clock clock,
+            SupervisorGradingReportRepository supervisorGradingReportRepository,
+            GradingReportTemplateRepoImpl gradingReportTemplateRepo,
+            ProjectTypeService projectTypeService)
+    {
+        return new GradingReportServiceImpl(
+                eventBus,
+                thesisSubmissionHistoryService,
+                clock,
+                supervisorGradingReportRepository,
+                gradingReportTemplateRepo,
+                projectTypeService);
+    }
+
+    @Bean
+    public GroupForumServiceImpl groupForumService(
+            EventBus eventBus,
+            GroupThreadRepository groupThreadRepository,
+            BasicForumService basicForumService)
+    {
+        return new GroupForumServiceImpl(groupThreadRepository, basicForumService, eventBus);
+    }
+
+    @Bean
+    public GroupServiceImpl groupService(Provider<EntityManager> em) {
+        return new GroupServiceImpl(em);
+    }
+
+    @Bean
+    public IdeaServiceImpl ideaService(
+            Provider<EntityManager> em,
+            ApplicationPeriodService applicationPeriodService,
+            FirstMeetingRepository firstMeetingRepository,
+            NotificationController notificationController,
+            ProjectService projectService,
+            GeneralSystemSettingsService generalSystemSettingsService,
+            TargetRepository targetRepository,
+            IdeaRepository ideaRepository,
+            Clock clock)
+    {
+        return new IdeaServiceImpl(em, applicationPeriodService, firstMeetingRepository, notificationController,
+                projectService, generalSystemSettingsService, targetRepository, ideaRepository, clock);
+    }
+
+    @Bean
+    public KeywordServiceImpl keywordService(Provider<EntityManager> em) {
+        return new KeywordServiceImpl(em);
+    }
+
+    @Bean
+    public LocalUserSearch localUserSearch(UserRepo userRepository) {
+        return new LocalUserSearch(userRepository);
+    }
+
+    @Bean
+    public MailEventServiceImpl mailEventService(Provider<EntityManager> em) {
+        return new MailEventServiceImpl(em);
+    }
+
+    @Bean
+    public MatchFollowUpServiceImpl matchFollowUpService(Provider<EntityManager> em) {
+        return new MatchFollowUpServiceImpl(em);
+    }
+
+    @Bean
+    public MatchServiceImpl matchService(Provider<EntityManager> em) {
+        return new MatchServiceImpl(em);
+    }
+
+    @Bean
+    public MilestoneActivityTemplateServiceImpl milestoneActivityTemplateService(
+            MilestoneActivityTemplateRepository milestoneActivityTemplateRepository)
+    {
+        return new MilestoneActivityTemplateServiceImpl(milestoneActivityTemplateRepository);
+    }
+
+    @Bean
+    public MilestonePhaseTemplateServiceImpl milestonePhaseTemplateService(Provider<EntityManager> em) {
+        return new MilestonePhaseTemplateServiceImpl(em);
+    }
+
+    @Bean
+    public MilestoneServiceImpl milestoneService(
+            Provider<EntityManager> em,
+            NotificationController notificationController)
+    {
+        return new MilestoneServiceImpl(notificationController, em);
+    }
+
+    @Bean
+    public MilestoneStatisticsServiceImpl milestoneStatisticsService(Provider<EntityManager> em) {
+        return new MilestoneStatisticsServiceImpl(em);
+    }
+
+    @Bean
+    public NationalSubjectCategoryServiceImpl nationalSubjectCategoryService(
+            NationalSubjectCategoryRepository nationalSubjectCategoryRepository)
+    {
+        return new NationalSubjectCategoryServiceImpl(nationalSubjectCategoryRepository);
+    }
+
+    @Bean
+    public NonWorkDayPeriodServiceImpl nonWorkDayPeriodService(Provider<EntityManager> em) {
+        return new NonWorkDayPeriodServiceImpl(em);
+    }
+
+    @Bean
+    public NoteServiceImpl noteService(Provider<EntityManager> em) {
+        return new NoteServiceImpl(em);
+    }
+
+    @Bean
+    public NotificationServiceImpl notificationService(Provider<EntityManager> em) {
+        return new NotificationServiceImpl(em);
+    }
+
+    @Bean
+    public OppositionReportServiceImpl oppositionReportService(
+            OppositionReportRepo oppositionReportRepository,
+            GradingReportTemplateRepo gradingReportTemplateRepository,
+            FileService fileService,
+            FinalSeminarOppositionRepo finalSeminarOppositionRepository)
+    {
+        return new OppositionReportServiceImpl(oppositionReportRepository, gradingReportTemplateRepository,
+                fileService, finalSeminarOppositionRepository);
+    }
+
+    @Bean
+    public PasswordServiceImpl passwordService(PasswordRepo passwordRepo) {
+        return new PasswordServiceImpl(passwordRepo);
+    }
+
+    @Bean
+    public PeerPortalImpl peerPortal(
+            FileService fileService,
+            PeerReviewRepository peerReviewRepository,
+            PeerRequestRepository peerRequestRepository,
+            EventBus eventBus,
+            ProjectFileService projectFileService,
+            DaysService daisyService,
+            Clock clock)
+    {
+        return new PeerPortalImpl(fileService, peerReviewRepository, peerRequestRepository,
+                eventBus, projectFileService, daisyService, clock);
+    }
+
+    @Bean
+    public PeerRequestServiceImpl peerRequestService(
+            Provider<EntityManager> em,
+            EventBus eventBus,
+            FileService fileService)
+    {
+        return new PeerRequestServiceImpl(em, eventBus, fileService);
+    }
+
+    @Bean
+    public PeerReviewServiceImpl peerReviewService(
+            Provider<EntityManager> em,
+            PeerReviewRepository peerReviewRepository)
+    {
+        return new PeerReviewServiceImpl(em, peerReviewRepository);
+    }
+
+    @Bean
+    public PlagiarismControlImpl plagiarismControl(
+            FileService fileService,
+            PlagiarismRequestRepository plagiarismRequestRepository,
+            UrkundSubmissionRepository urkundSubmissionRepository)
+    {
+        return new PlagiarismControlImpl(plagiarismRequestRepository, urkundSubmissionRepository, fileService);
+    }
+
+    @Bean
+    public PreliminaryMatchServiceImpl preliminaryMatchService(Provider<EntityManager> em, IdeaService ideaService) {
+        return new PreliminaryMatchServiceImpl(em, ideaService);
+    }
+
+    @Bean
+    public ProgramServiceImpl programService(Provider<EntityManager> em) {
+        return new ProgramServiceImpl(em);
+    }
+
+    @Bean
+    public ProjectFileServiceImpl projectFileService(
+            FileService fileService,
+            ProjectFileRepository projectFileRepository)
+    {
+        return new ProjectFileServiceImpl(fileService, projectFileRepository);
+    }
+
+    @Bean
+    public ProjectFinalSeminarStatisticsServiceImpl projectFinalSeminarStatisticsService(Provider<EntityManager> em) {
+        return new ProjectFinalSeminarStatisticsServiceImpl(em);
+    }
+
+    @Bean
+    public ProjectForumServiceImpl projectForumService(
+            EventBus eventBus,
+            BasicForumService basicForumService,
+            ProjectThreadRepository projectThreadRepository,
+            ForumPostRepository postRepository,
+            ProjectFileService projectFileService)
+    {
+        return new ProjectForumServiceImpl(projectThreadRepository,
+                postRepository, projectFileService, basicForumService, eventBus);
+    }
+
+    @Bean
+    public ProjectPartnerServiceImpl projectPartnerService(Provider<EntityManager> em) {
+        return new ProjectPartnerServiceImpl(em);
+    }
+
+    @Bean
+    public ProjectPeopleStatisticsServiceImpl projectPeopleStatisticsService(Provider<EntityManager> em) {
+        return new ProjectPeopleStatisticsServiceImpl(em);
+    }
+
+    @Bean
+    public ProjectServiceImpl projectService(
+            Provider<EntityManager> em,
+            EventBus eventBus,
+            ProjectRepo projectRepo,
+            Clock clock)
+    {
+        return new ProjectServiceImpl(projectRepo, clock, eventBus, em);
+    }
+
+    @Bean
+    public ProjectTypeServiceImpl projectTypeService(Provider<EntityManager> em) {
+        return new ProjectTypeServiceImpl(em);
+    }
+
+    @Bean
+    public PublicationMetadataServiceImpl publicationMetadataService(
+            PublicationMetadataRepository publicationMetadataRepository)
+    {
+        return new PublicationMetadataServiceImpl(publicationMetadataRepository);
+    }
+
+    @Bean
+    public ReflectionServiceImpl reflectionService(
+            AuthorRepository authorRepository,
+            FinalSeminarServiceImpl finalSeminarService)
+    {
+        return new ReflectionServiceImpl(authorRepository, finalSeminarService);
+    }
+
+    @Bean
+    public ReceiverConfigurationServiceImpl receiverConfigurationService(Provider<EntityManager> em) {
+        return new ReceiverConfigurationServiceImpl(em);
+    }
+
+    @Bean
+    public ReviewerDeadlineFollowupServiceImpl reviewerDeadlineFollowupService(Provider<EntityManager> em) {
+        return new ReviewerDeadlineFollowupServiceImpl(em);
+    }
+
+    @Bean
+    public ZipReporter reporter(
+            FileService fileService,
+            ProjectService projectService,
+            FinalSeminarService finalSeminarService,
+            ProjectForumService projectForumService,
+            PeerReviewService peerReviewService,
+            PeerRequestService peerRequestService,
+            GroupService groupService,
+            GroupForumService groupForumService,
+            IdeaService ideaService,
+            GradingReportService gradingReportService,
+            ReviewerInteractionService reviewerInteractionService,
+            RoughDraftApprovalService roughDraftApprovalService,
+            FinalSeminarApprovalService finalSeminarApprovalService)
+    {
+        return new ZipReporter(fileService, projectService, finalSeminarService, projectForumService, peerReviewService,
+                peerRequestService, groupService, groupForumService, ideaService, gradingReportService,
+                reviewerInteractionService, roughDraftApprovalService, finalSeminarApprovalService);
+    }
+
+    @Bean
+    public ReportServiceImpl reportService(Provider<EntityManager> em, FileService fileService) {
+        return new ReportServiceImpl(em, fileService);
+    }
+
+    @Bean
+    public ResearchAreaServiceImpl researchAreaService(Provider<EntityManager> em) {
+        return new ResearchAreaServiceImpl(em);
+    }
+
+    @Bean
+    public ReviewerInteractionServiceImpl reviewerInteractionService(
+            ReviewerThreadRepository reviewerThreadRepository,
+            BasicForumService forumService,
+            EventBus eventBus)
+    {
+        return new ReviewerInteractionServiceImpl(reviewerThreadRepository, forumService, eventBus);
+    }
+
+    @Bean
+    public ReviewingServiceImpl reviewingService(
+            Provider<EntityManager> em,
+            EventBus eventBus,
+            FileService fileService)
+    {
+        return new ReviewingServiceImpl(em, fileService, eventBus);
+    }
+
+    @Bean
+    public ReviewerCapacityServiceImpl reviewerCapacityService(
+            ReviewerTargetRepository reviewerTargetRepository,
+            DecisionRepository decisionRepository,
+            UserService userService,
+            ProjectService projectService,
+            EventBus eventBus)
+    {
+        return new ReviewerCapacityServiceImpl(reviewerTargetRepository, decisionRepository, userService,
+                projectService, eventBus);
+    }
+
+    @Bean
+    public ReviewerDeadlineSettingsServiceImpl reviewerDeadlineSettingsService(
+            ReviewerDeadlineSettingsRepository reviewerDeadlineSettingsRepository)
+    {
+        return new ReviewerDeadlineSettingsServiceImpl(reviewerDeadlineSettingsRepository);
+    }
+
+    @Bean
+    public SupervisorServiceImpl supervisorService(Provider<EntityManager> em) {
+        return new SupervisorServiceImpl(em);
+    }
+
+    @Bean
+    public SurveyServiceImpl surveyService(
+            SurveyRepository surveyRepository,
+            QuestionRepository questionRepository,
+            FinalThesisService finalThesisService,
+            GeneralSystemSettingsService generalSystemSettingsService,
+            ReflectionService reflectionService)
+    {
+        return new SurveyServiceImpl(surveyRepository, questionRepository, finalThesisService,
+                generalSystemSettingsService, reflectionService);
+    }
+
+    @Bean
+    public TargetServiceImpl targetService(Provider<EntityManager> em, IdeaService ideaService) {
+        return new TargetServiceImpl(em, ideaService);
+    }
+
+    @Bean
+    public UnitServiceImpl unitService(Provider<EntityManager> em) {
+        return new UnitServiceImpl(em);
+    }
+
+    @Bean
+    public UrkundApiImpl urkundApi(
+            UrkundSettingsRepository urkundSettingsRepository,
+            FileService fileService)
+    {
+        return new UrkundApiImpl(urkundSettingsRepository, fileService);
+    }
+
+    @Bean
+    public UrkundServiceImpl urkundService(
+            UrkundApi urkundApi,
+            UrkundSubmissionRepository urkundSubmissionRepository,
+            Sukat sukat,
+            FileService fileService)
+    {
+        return new UrkundServiceImpl(urkundApi, urkundSubmissionRepository, sukat, fileService);
+    }
+
+    @Bean
+    public UserNameServiceImpl userNameService(Provider<EntityManager> em) {
+        return new UserNameServiceImpl(em);
+    }
+
+    @Bean
+    public UserProfileServiceImpl userProfileService(Provider<EntityManager> em) {
+        return new UserProfileServiceImpl(em);
+    }
+
+    @Bean
+    public UserServiceImpl userService(Provider<EntityManager> em) {
+        return new UserServiceImpl(em);
+    }
+
+    @Bean
+    public WorkerDataServiceImpl workerDataService(Provider<EntityManager> em) {
+        return new WorkerDataServiceImpl(em);
+    }
+
+    @Bean
+    public NotificationControllerImpl notificationController(
+            NotificationServiceImpl notificationService,
+            NotificationMailFormatter notificationMailFormatter,
+            MailEventService mailEventService,
+            ReceiverConfigurationService receiverConfigurationService,
+            DeliveryConfigurationService deliveryConfigurationService,
+            Provider<CurrentUser> currentUserProvider)
+    {
+        return new NotificationControllerImpl(notificationService,
+                notificationMailFormatter,
+                mailEventService, receiverConfigurationService, deliveryConfigurationService, currentUserProvider);
+    }
+
+    @Bean
+    public NotificationMailFormatterImpl notificationMailFormatter() {
+        return new NotificationMailFormatterImpl();
+    }
+
+    @Bean
+    public ReviewerAssignedDeadline reviewerAssignedDeadline(
+            EventBus eventBus,
+            ReviewerDeadlineSettingsService reviewerDeadlineSettingsService,
+            RoughDraftApprovalService roughDraftApprovalService,
+            FinalSeminarApprovalService finalSeminarApprovalService,
+            DaysService daysService,
+            Clock clock)
+    {
+        return new ReviewerAssignedDeadline(roughDraftApprovalService, finalSeminarApprovalService,
+                reviewerDeadlineSettingsService, daysService, eventBus, clock);
+    }
+
+    @Bean
+    public DataInitializer dataInitializer() {
+        return new DataInitializer();
+    }
+
+    @Bean
+    public FinalSeminarActivityHandler finalSeminarActivityHandler(
+            ActivityPlanFacade activityPlanFacade,
+            ActivityFinalSeminarRepository activityFinalSeminarRepository,
+            EventBus eventBus)
+    {
+        return new FinalSeminarActivityHandler(activityPlanFacade, activityFinalSeminarRepository, eventBus);
+    }
+
+    @Bean
+    public PostActivityUploadToForum postActivityUploadToForum(
+            EventBus eventBus,
+            ProjectForumService projectForumService,
+            ActivityThreadRepository activityThreadRepository)
+    {
+        return new PostActivityUploadToForum(projectForumService, activityThreadRepository, eventBus);
+    }
+
+    @Bean
+    public ForumNotifications forumNotifications(
+            EventBus eventBus,
+            NotificationController notificationController,
+            ForumNotificationRepository forumNotificationRepository,
+            NotificationService notificationService)
+    {
+        return new ForumNotifications(eventBus, notificationController, forumNotificationRepository,
+                notificationService);
+    }
+
+    @Bean
+    public ActivateCompletedMilestonesOnNewProjects activateCompletedMilestonesOnNewProjects(
+            EventBus eventBus,
+            MilestoneServiceImpl milestoneService,
+            PeerReviewService peerReviewService,
+            MilestoneActivityTemplateService milestoneActivityTemplateService,
+            FinalSeminarService finalSeminarService)
+    {
+        return new ActivateCompletedMilestonesOnNewProjects(peerReviewService, milestoneActivityTemplateService,
+                milestoneService, eventBus, finalSeminarService);
+    }
+
+    @Bean
+    public FinalSeminarCreationSubscribers finalSeminarCreationSubscribers(
+            EventBus eventBus,
+            FinalSeminarServiceImpl finalSeminarService,
+            NotificationController notificationController,
+            AuthorRepository authorRepository)
+    {
+        return new FinalSeminarCreationSubscribers(authorRepository, finalSeminarService, notificationController, eventBus);
+    }
+
+    @Bean
+    public ProjectStartNotifier projectStartNotifier(EventBus eventBus, NotificationController notificationController) {
+        return new ProjectStartNotifier(eventBus, notificationController);
+    }
+
+    @Bean
+    public AddActivityPlanOnProjectStart addActivityPlanOnProjectStart(
+            ActivityPlanFacade activityPlanFacade,
+            EventBus eventBus)
+    {
+        return new AddActivityPlanOnProjectStart(activityPlanFacade, eventBus);
+    }
+
+    @Bean
+    public Notifications notifications(
+            EventBus eventBus,
+            NotificationController notificationController,
+            NotificationService notificationService)
+    {
+        return new Notifications(notificationController, notificationService, eventBus);
+    }
+
+    @Bean
+    public CommentThreadServiceImpl commentThreadService(CommentThreadRepo commentThreadRepository) {
+        return new CommentThreadServiceImpl(commentThreadRepository);
+    }
+
+    @Bean
+    public NotificationEventServiceImpl notificationEventService(NotificationEventRepository notificationEventRepository) {
+        return new NotificationEventServiceImpl(notificationEventRepository);
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
index 6720ffac7e..762ea0c32a 100644
--- a/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
+++ b/core/src/main/java/se/su/dsv/scipro/DataInitializer.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.checklist.ChecklistCategory;
 import se.su.dsv.scipro.match.ApplicationPeriod;
 import se.su.dsv.scipro.match.Keyword;
diff --git a/core/src/main/java/se/su/dsv/scipro/RepositoryConfiguration.java b/core/src/main/java/se/su/dsv/scipro/RepositoryConfiguration.java
new file mode 100644
index 0000000000..6a2167bc1f
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/RepositoryConfiguration.java
@@ -0,0 +1,285 @@
+package se.su.dsv.scipro;
+
+import jakarta.inject.Provider;
+import jakarta.persistence.EntityManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import se.su.dsv.scipro.checklist.ChecklistCategoryRepoImpl;
+import se.su.dsv.scipro.checklist.ChecklistQuestionRepoImpl;
+import se.su.dsv.scipro.file.FileDescriptionRepoImpl;
+import se.su.dsv.scipro.file.FileReferenceRepositoryImpl;
+import se.su.dsv.scipro.file.ProjectFileRepositoryImpl;
+import se.su.dsv.scipro.finalseminar.AuthorRepositoryImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarActiveParticipationRepositoryImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarOppositionRepoImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarRepositoryImpl;
+import se.su.dsv.scipro.forum.AbstractThreadRepositoryImpl;
+import se.su.dsv.scipro.forum.ForumPostReadStateRepositoryImpl;
+import se.su.dsv.scipro.forum.ForumPostRepositoryImpl;
+import se.su.dsv.scipro.forum.GroupThreadRepositoryImpl;
+import se.su.dsv.scipro.forum.ProjectThreadRepositoryImpl;
+import se.su.dsv.scipro.forum.notifications.ForumNotificationRepositoryImpl;
+import se.su.dsv.scipro.grading.GradingHistoryEventRepositoryImpl;
+import se.su.dsv.scipro.grading.NationalSubjectCategoryRepositoryImpl;
+import se.su.dsv.scipro.grading.PublicationMetadataRepositoryImpl;
+import se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarRepositoryImpl;
+import se.su.dsv.scipro.integration.activityforum.ActivityThreadRepositoryImpl;
+import se.su.dsv.scipro.match.FirstMeetingRepositoryImpl;
+import se.su.dsv.scipro.match.IdeaRepositoryImpl;
+import se.su.dsv.scipro.match.TargetRepositoryImpl;
+import se.su.dsv.scipro.milestones.MilestoneActivityTemplateRepositoryImpl;
+import se.su.dsv.scipro.notifications.NotificationEventRepositoryImpl;
+import se.su.dsv.scipro.peer.CommentThreadRepoImpl;
+import se.su.dsv.scipro.peer.PeerRequestRepositoryImpl;
+import se.su.dsv.scipro.peer.PeerReviewRepositoryImpl;
+import se.su.dsv.scipro.plagiarism.PlagiarismRequestRepositoryImpl;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSettingsRepositoryImpl;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSubmissionRepositoryImpl;
+import se.su.dsv.scipro.project.ProjectRepoImpl;
+import se.su.dsv.scipro.projectpartner.ProjectPartnerRepositoryImpl;
+import se.su.dsv.scipro.report.GradingReportTemplateRepoImpl;
+import se.su.dsv.scipro.report.OppositionReportRepoImpl;
+import se.su.dsv.scipro.report.SupervisorGradingReportRepositoryImpl;
+import se.su.dsv.scipro.reviewing.DecisionRepositoryImpl;
+import se.su.dsv.scipro.reviewing.ReviewerDeadlineSettingsRepositoryImpl;
+import se.su.dsv.scipro.reviewing.ReviewerTargetRepositoryImpl;
+import se.su.dsv.scipro.reviewing.ReviewerThreadRepositoryImpl;
+import se.su.dsv.scipro.survey.QuestionRepositoryImpl;
+import se.su.dsv.scipro.survey.SurveyRepositoryImpl;
+import se.su.dsv.scipro.system.FooterAddressRepoImpl;
+import se.su.dsv.scipro.system.FooterLinkRepoImpl;
+import se.su.dsv.scipro.system.PasswordRepoImpl;
+import se.su.dsv.scipro.system.UserRepoImpl;
+
+@Configuration(proxyBeanMethods = false)
+public class RepositoryConfiguration {
+    @Bean
+    public GradingHistoryEventRepositoryImpl gradingHistoryEventRepository(Provider<EntityManager> em) {
+        return new GradingHistoryEventRepositoryImpl(em);
+    }
+
+    @Bean
+    public AbstractThreadRepositoryImpl abstractThreadRepository(Provider<EntityManager> em) {
+        return new AbstractThreadRepositoryImpl(em);
+    }
+
+    @Bean
+    public ActivityFinalSeminarRepositoryImpl activityFinalSeminarRepository(Provider<EntityManager> em) {
+        return new ActivityFinalSeminarRepositoryImpl(em);
+    }
+
+    @Bean
+    public ActivityThreadRepositoryImpl activityThreadRepository(Provider<EntityManager> em) {
+        return new ActivityThreadRepositoryImpl(em);
+    }
+
+    @Bean
+    public AuthorRepositoryImpl authorRepository(Provider<EntityManager> em) {
+        return new AuthorRepositoryImpl(em);
+    }
+
+    @Bean
+    public ChecklistCategoryRepoImpl checklistCategoryRepo(Provider<EntityManager> em) {
+        return new ChecklistCategoryRepoImpl(em);
+    }
+
+    @Bean
+    public ChecklistQuestionRepoImpl checklistQuestionRepo(Provider<EntityManager> em) {
+        return new ChecklistQuestionRepoImpl(em);
+    }
+
+    @Bean
+    public CommentThreadRepoImpl commentThreadRepo(Provider<EntityManager> em) {
+        return new CommentThreadRepoImpl(em);
+    }
+
+    @Bean
+    public DecisionRepositoryImpl decisionRepository(Provider<EntityManager> em) {
+        return new DecisionRepositoryImpl(em);
+    }
+
+    @Bean
+    public FileDescriptionRepoImpl fileDescriptionRepo(Provider<EntityManager> em) {
+        return new FileDescriptionRepoImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarActiveParticipationRepositoryImpl finalSeminarActiveParticipationRepository(Provider<EntityManager> em) {
+        return new FinalSeminarActiveParticipationRepositoryImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarRepositoryImpl finalSeminarRepository(Provider<EntityManager> em) {
+        return new FinalSeminarRepositoryImpl(em);
+    }
+
+    @Bean
+    public FileReferenceRepositoryImpl fileReferenceRepository(Provider<EntityManager> em) {
+        return new FileReferenceRepositoryImpl(em);
+    }
+
+    @Bean
+    public FinalSeminarOppositionRepoImpl finalSeminarOppositionRepo(Provider<EntityManager> em) {
+        return new FinalSeminarOppositionRepoImpl(em);
+    }
+
+    @Bean
+    public FirstMeetingRepositoryImpl firstMeetingRepository(Provider<EntityManager> em) {
+        return new FirstMeetingRepositoryImpl(em);
+    }
+
+    @Bean
+    public FooterAddressRepoImpl footerAddressRepo(Provider<EntityManager> em) {
+        return new FooterAddressRepoImpl(em);
+    }
+
+    @Bean
+    public FooterLinkRepoImpl footerLinkRepo(Provider<EntityManager> em) {
+        return new FooterLinkRepoImpl(em);
+    }
+
+    @Bean
+    public ForumNotificationRepositoryImpl forumNotificationRepository(Provider<EntityManager> em) {
+        return new ForumNotificationRepositoryImpl(em);
+    }
+
+    @Bean
+    public ForumPostReadStateRepositoryImpl forumPostReadStateRepository(Provider<EntityManager> em) {
+        return new ForumPostReadStateRepositoryImpl(em);
+    }
+
+    @Bean
+    public ForumPostRepositoryImpl forumPostRepository(Provider<EntityManager> em) {
+        return new ForumPostRepositoryImpl(em);
+    }
+
+    @Bean
+    public GradingReportTemplateRepoImpl gradingReportTemplateRepo(Provider<EntityManager> em) {
+        return new GradingReportTemplateRepoImpl(em);
+    }
+
+    @Bean
+    public GroupThreadRepositoryImpl groupThreadRepository(Provider<EntityManager> em) {
+        return new GroupThreadRepositoryImpl(em);
+    }
+
+    @Bean
+    public IdeaRepositoryImpl ideaRepository(Provider<EntityManager> em) {
+        return new IdeaRepositoryImpl(em);
+    }
+
+    @Bean
+    public MilestoneActivityTemplateRepositoryImpl milestoneActivityTemplateRepository(Provider<EntityManager> em) {
+        return new MilestoneActivityTemplateRepositoryImpl(em);
+    }
+
+    @Bean
+    public NationalSubjectCategoryRepositoryImpl nationalSubjectCategoryRepository(Provider<EntityManager> em) {
+        return new NationalSubjectCategoryRepositoryImpl(em);
+    }
+
+    @Bean
+    public OppositionReportRepoImpl oppositionReportRepo(Provider<EntityManager> em) {
+        return new OppositionReportRepoImpl(em);
+    }
+
+    @Bean
+    public PasswordRepoImpl passwordRepo(Provider<EntityManager> em) {
+        return new PasswordRepoImpl(em);
+    }
+
+    @Bean
+    public PeerRequestRepositoryImpl peerRequestRepository(Provider<EntityManager> em) {
+        return new PeerRequestRepositoryImpl(em);
+    }
+
+    @Bean
+    public PeerReviewRepositoryImpl peerReviewRepository(Provider<EntityManager> em) {
+        return new PeerReviewRepositoryImpl(em);
+    }
+
+    @Bean
+    public ProjectPartnerRepositoryImpl projectPartnerRepository(Provider<EntityManager> em) {
+        return new ProjectPartnerRepositoryImpl(em);
+    }
+
+    @Bean
+    public ProjectRepoImpl projectRepo(Provider<EntityManager> em) {
+        return new ProjectRepoImpl(em);
+    }
+
+    @Bean
+    public ProjectThreadRepositoryImpl projectThreadRepository(Provider<EntityManager> em) {
+        return new ProjectThreadRepositoryImpl(em);
+    }
+
+    @Bean
+    public PublicationMetadataRepositoryImpl publicationMetadataRepository(Provider<EntityManager> em) {
+        return new PublicationMetadataRepositoryImpl(em);
+    }
+
+    @Bean
+    public QuestionRepositoryImpl questionRepository(Provider<EntityManager> em) {
+        return new QuestionRepositoryImpl(em);
+    }
+
+    @Bean
+    public ReviewerDeadlineSettingsRepositoryImpl reviewerDeadlineSettingsRepository(Provider<EntityManager> em) {
+        return new ReviewerDeadlineSettingsRepositoryImpl(em);
+    }
+
+    @Bean
+    public ReviewerTargetRepositoryImpl reviewerTargetRepository(Provider<EntityManager> em) {
+        return new ReviewerTargetRepositoryImpl(em);
+    }
+
+    @Bean
+    public ReviewerThreadRepositoryImpl reviewerThreadRepository(Provider<EntityManager> em) {
+        return new ReviewerThreadRepositoryImpl(em);
+    }
+
+    @Bean
+    public SurveyRepositoryImpl surveyRepository(Provider<EntityManager> em) {
+        return new SurveyRepositoryImpl(em);
+    }
+
+    @Bean
+    public TargetRepositoryImpl targetRepository(Provider<EntityManager> em) {
+        return new TargetRepositoryImpl(em);
+    }
+
+    @Bean
+    public UrkundSettingsRepositoryImpl urkundSettingsRepository(Provider<EntityManager> em) {
+        return new UrkundSettingsRepositoryImpl(em);
+    }
+
+    @Bean
+    public UrkundSubmissionRepositoryImpl urkundSubmissionRepository(Provider<EntityManager> em) {
+        return new UrkundSubmissionRepositoryImpl(em);
+    }
+
+    @Bean
+    public UserRepoImpl userRepo(Provider<EntityManager> em) {
+        return new UserRepoImpl(em);
+    }
+
+    @Bean
+    public PlagiarismRequestRepositoryImpl plagiarismRequestRepository(Provider<EntityManager> em) {
+        return new PlagiarismRequestRepositoryImpl(em);
+    }
+
+    @Bean
+    public ProjectFileRepositoryImpl projectFileRepository(Provider<EntityManager> em) {
+        return new ProjectFileRepositoryImpl(em);
+    }
+
+    @Bean
+    public NotificationEventRepositoryImpl notificationEventRepository(Provider<EntityManager> em) {
+        return new NotificationEventRepositoryImpl(em);
+    }
+
+    @Bean
+    public SupervisorGradingReportRepositoryImpl supervisorGradingReportRepository(Provider<EntityManager> em) {
+        return new SupervisorGradingReportRepositoryImpl(em);
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
index d211c1cbac..4f7955f835 100755
--- a/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/activityplan/ActivityPlanFacadeImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.activityplan;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import se.su.dsv.scipro.system.Pageable;
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistAnswerServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistAnswerServiceImpl.java
index 1e1893ccda..20a612c25f 100644
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistAnswerServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistAnswerServiceImpl.java
@@ -9,7 +9,7 @@ import jakarta.persistence.EntityManager;
 public class ChecklistAnswerServiceImpl extends AbstractServiceImpl<ChecklistAnswer, Long> implements ChecklistAnswerService {
 
     @Inject
-    protected ChecklistAnswerServiceImpl(Provider<EntityManager> em) {
+    public ChecklistAnswerServiceImpl(Provider<EntityManager> em) {
         super(em, ChecklistAnswer.class, QChecklistAnswer.checklistAnswer);
     }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
index 9b5883089e..64b595d29e 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistCategoryRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.checklist;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
index 410c90439a..3fb6407ebc 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistQuestionRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.checklist;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistServiceImpl.java
index cba683c25a..9a9a9c234e 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.checklist;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.activityplan.QActivity;
 import se.su.dsv.scipro.activityplan.QActivityPlan;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
index 0e43b8c13d..336d0e8b58 100755
--- a/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/checklist/ChecklistTemplateServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.checklist;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
diff --git a/core/src/main/java/se/su/dsv/scipro/daisyExternal/DaisyExternalModule.java b/core/src/main/java/se/su/dsv/scipro/daisyExternal/DaisyExternalModule.java
deleted file mode 100644
index 63de05fdfb..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/daisyExternal/DaisyExternalModule.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package se.su.dsv.scipro.daisyExternal;
-
-import com.google.inject.Key;
-import com.google.inject.PrivateModule;
-import com.google.inject.Scopes;
-import com.google.inject.name.Names;
-import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
-import se.su.dsv.scipro.daisyExternal.http.DaisyAPIImpl;
-
-public class DaisyExternalModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        requireBinding(Key.get(String.class, Names.named("daisy.api.url")));
-        requireBinding(Key.get(String.class, Names.named("daisy.api.username")));
-        requireBinding(Key.get(String.class, Names.named("daisy.api.password")));
-
-        bind(DaisyAPI.class).to(DaisyAPIImpl.class).in(Scopes.SINGLETON);
-        expose(DaisyAPI.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/events/EventModule.java b/core/src/main/java/se/su/dsv/scipro/events/EventModule.java
deleted file mode 100644
index 61fb042f87..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/events/EventModule.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package se.su.dsv.scipro.events;
-
-import com.google.common.eventbus.EventBus;
-import com.google.inject.AbstractModule;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class EventModule extends AbstractModule {
-    private static final Logger LOGGER = LoggerFactory.getLogger(EventModule.class);
-
-    @Override
-    protected void configure() {
-        bind(EventBus.class).toInstance(new EventBus((throwable, context) -> LOGGER.error("Could not dispatch event: " + context.getSubscriber() + " to " + context.getSubscriberMethod(), throwable)));
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java b/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
index eccb9da1eb..c649d10bfe 100755
--- a/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/FileDescriptionRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.file;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileModule.java b/core/src/main/java/se/su/dsv/scipro/file/FileModule.java
deleted file mode 100644
index 82cc9172ea..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/file/FileModule.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package se.su.dsv.scipro.file;
-
-import com.google.inject.AbstractModule;
-
-public class FileModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(FileService.class).to(FileServiceImpl.class);
-        bind(ProjectFileService.class).to(ProjectFileServiceImpl.class);
-        bind(ProjectFileRepository.class).to(ProjectFileRepositoryImpl.class);
-        bind(FileReferenceRepository.class).to(FileReferenceRepositoryImpl.class);
-        bind(FileDescriptionRepo.class).to(FileDescriptionRepoImpl.class);
-
-        requireBinding(FileStore.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepository.java b/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepository.java
index a942b50e60..3264448133 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.file;
 
-interface FileReferenceRepository {
+public interface FileReferenceRepository {
     FileReference create(FileReference fileReference);
 
     void delete(FileReference fileReference);
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepositoryImpl.java
index cacc3f7a16..25e00cf450 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/FileReferenceRepositoryImpl.java
@@ -1,16 +1,16 @@
 package se.su.dsv.scipro.file;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractRepository;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
 
-class FileReferenceRepositoryImpl extends AbstractRepository implements FileReferenceRepository {
+public class FileReferenceRepositoryImpl extends AbstractRepository implements FileReferenceRepository {
 
     @Inject
-    FileReferenceRepositoryImpl(final Provider<EntityManager> em) {
+    public FileReferenceRepositoryImpl(final Provider<EntityManager> em) {
         super(em);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/file/FileServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/file/FileServiceImpl.java
index 2fe1f634d6..c6643f50ad 100755
--- a/core/src/main/java/se/su/dsv/scipro/file/FileServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/FileServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.file;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
index e995fd1831..db099d6323 100644
--- a/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/file/ProjectFileServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.file;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.Pageable;
 import se.su.dsv.scipro.project.Project;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
index 7def75b680..46957a9678 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarOppositionRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.finalseminar;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.system.ProjectType;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRepositoryImpl.java
index f17e41be75..40e72964a7 100644
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.finalseminar;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.system.AbstractRepository;
 
@@ -9,7 +9,7 @@ import jakarta.inject.Provider;
 
 public class FinalSeminarRepositoryImpl extends AbstractRepository implements FinalSeminarRepository {
     @Inject
-    protected FinalSeminarRepositoryImpl(Provider<EntityManager> em) {
+    public FinalSeminarRepositoryImpl(Provider<EntityManager> em) {
         super(em);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
index e3510ddfae..b5514bcd3e 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.finalseminar;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import jakarta.persistence.EntityManager;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarSettingsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarSettingsServiceImpl.java
index 24aa74c59e..c912f35abb 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarSettingsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarSettingsServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.finalseminar;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarUploadControllerImpl.java b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarUploadControllerImpl.java
index 5feff42af2..2613d77c32 100755
--- a/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarUploadControllerImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalseminar/FinalSeminarUploadControllerImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.finalseminar;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import se.su.dsv.scipro.file.*;
diff --git a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
index 98b775c07b..41251d1700 100644
--- a/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/finalthesis/FinalThesisServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.finalthesis;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import jakarta.persistence.EntityManager;
diff --git a/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingModule.java b/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingModule.java
deleted file mode 100644
index e567605a8d..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/firstmeeting/FirstMeetingModule.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package se.su.dsv.scipro.firstmeeting;
-
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.firstmeeting.FirstMeetingReminderWorker.FirstMeetingReminderWorkerSchedule;
-
-public class FirstMeetingModule extends AbstractModule {
-    @Override
-    public void configure() {
-        bind(FirstMeetingReminderWorkerSchedule.class).asEagerSingleton();
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
index 05642970d2..0dae3383bb 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumModule.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumModule.java
deleted file mode 100644
index 7346fd1166..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package se.su.dsv.scipro.forum;
-
-import com.google.inject.PrivateModule;
-
-public class ForumModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        bind(AbstractThreadRepository.class).to(AbstractThreadRepositoryImpl.class);
-        bind(ForumPostReadStateRepository.class).to(ForumPostReadStateRepositoryImpl.class);
-        bind(ForumPostRepository.class).to(ForumPostRepositoryImpl.class);
-        bind(ProjectThreadRepository.class).to(ProjectThreadRepositoryImpl.class);
-        bind(GroupThreadRepository.class).to(GroupThreadRepositoryImpl.class);
-
-        expose(ProjectThreadRepository.class);
-        expose(GroupThreadRepository.class);
-
-        bind(ProjectForumService.class).to(ProjectForumServiceImpl.class);
-        bind(GroupForumService.class).to(GroupForumServiceImpl.class);
-
-        expose(ProjectForumService.class);
-        expose(GroupForumService.class);
-
-        bind(BasicForumService.class).to(BasicForumServiceImpl.class);
-        expose(BasicForumService.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
index 4d385c0255..00dd52bb7c 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepositoryImpl.java
index 909c57693f..862884445c 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostReadStateRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.LockModeType;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumPostReadState;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
index 0b39495268..92e3afb1c8 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/GroupForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/GroupForumServiceImpl.java
index bed0169c09..0ba81e0b92 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/GroupForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/GroupForumServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.forum.dataobjects.GroupThread;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
index 37e0a30d31..2c254d5bd2 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/GroupThreadRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.GroupThread;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
index 7ed3e1715c..1825a16197 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.forum;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileSource;
 import se.su.dsv.scipro.file.ProjectFileService;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
index b50913b764..cbe28506a1 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotifications.java b/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotifications.java
index b294608351..5b1ff17920 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotifications.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotifications.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.forum.notifications;
 
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.ForumPostReadEvent;
 import se.su.dsv.scipro.forum.NewGroupForumReplyEvent;
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotificationsModule.java b/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotificationsModule.java
deleted file mode 100644
index 2a77013615..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/forum/notifications/ForumNotificationsModule.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package se.su.dsv.scipro.forum.notifications;
-
-import com.google.inject.AbstractModule;
-
-public class ForumNotificationsModule extends AbstractModule {
-    @Override
-    public void configure() {
-        bind(ForumNotificationRepository.class).to(ForumNotificationRepositoryImpl.class);
-        bind(ForumNotifications.class).asEagerSingleton();
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/gdpr/GDPRModule.java b/core/src/main/java/se/su/dsv/scipro/gdpr/GDPRModule.java
deleted file mode 100644
index 65a3f3ae8f..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/gdpr/GDPRModule.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package se.su.dsv.scipro.gdpr;
-
-import com.google.inject.PrivateModule;
-
-public class GDPRModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        bind(Reporter.class).to(ZipReporter.class);
-
-        expose(Reporter.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/generalsystemsettings/GeneralSystemSettingsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/generalsystemsettings/GeneralSystemSettingsServiceImpl.java
index 1550844b4e..5450f5b837 100755
--- a/core/src/main/java/se/su/dsv/scipro/generalsystemsettings/GeneralSystemSettingsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/generalsystemsettings/GeneralSystemSettingsServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.generalsystemsettings;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractServiceImpl;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/GradingHistoryEventRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/grading/GradingHistoryEventRepositoryImpl.java
index 68b25d1cd0..de3f36ac13 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/GradingHistoryEventRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/GradingHistoryEventRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.grading;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractRepository;
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java b/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java
deleted file mode 100644
index e67d559b77..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/grading/GradingModule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package se.su.dsv.scipro.grading;
-
-import com.google.inject.Key;
-import com.google.inject.PrivateModule;
-import com.google.inject.name.Names;
-import se.su.dsv.scipro.report.GradingReportServiceImpl;
-
-public class GradingModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        requireBinding(Key.get(String.class, Names.named("service.grading.url")));
-        bind(GradingService.class).to(GradingServiceImpl.class);
-
-        expose(GradingService.class);
-
-        bind(PublicationMetadataRepository.class).to(PublicationMetadataRepositoryImpl.class);
-        bind(PublicationMetadataService.class).to(PublicationMetadataServiceImpl.class);
-        expose(PublicationMetadataService.class);
-
-        bind(ExaminerTimelineService.class).to(GradingHistory.class);
-        expose(ExaminerTimelineService.class);
-        bind(GradingHistoryEventRepository.class).to(GradingHistoryEventRepositoryImpl.class);
-        bind(ThesisRejectionHistoryService.class).to(GradingHistory.class);
-        expose(ThesisRejectionHistoryService.class);
-        bind(ThesisApprovedHistoryService.class).to(GradingHistory.class);
-        expose(ThesisApprovedHistoryService.class);
-        bind(ThesisSubmissionHistoryService.class).to(GradingHistory.class);
-        expose(ThesisSubmissionHistoryService.class);
-
-        bind(NationalSubjectCategoryRepository.class).to(NationalSubjectCategoryRepositoryImpl.class);
-        bind(NationalSubjectCategoryService.class).to(NationalSubjectCategoryServiceImpl.class);
-        expose(NationalSubjectCategoryService.class);
-
-        bind(GradingReportTemplateService.class).to(GradingReportServiceImpl.class);
-        expose(GradingReportTemplateService.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/NationalSubjectCategoryRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/grading/NationalSubjectCategoryRepositoryImpl.java
index 2a6d009a47..90c2723739 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/NationalSubjectCategoryRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/NationalSubjectCategoryRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.grading;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.system.AbstractRepository;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepository.java b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepository.java
index 05702c1d93..10c37dab9e 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepository.java
@@ -2,7 +2,7 @@ package se.su.dsv.scipro.grading;
 
 import se.su.dsv.scipro.project.Project;
 
-interface PublicationMetadataRepository {
+public interface PublicationMetadataRepository {
     void save(PublicationMetadata publicationMetadata);
 
     PublicationMetadata findByProject(Project project);
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepositoryImpl.java
index f1b2ac5a38..503b62f6ec 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.grading;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.AbstractRepository;
@@ -8,9 +8,9 @@ import se.su.dsv.scipro.system.AbstractRepository;
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 
-class PublicationMetadataRepositoryImpl extends AbstractRepository implements PublicationMetadataRepository {
+public class PublicationMetadataRepositoryImpl extends AbstractRepository implements PublicationMetadataRepository {
     @Inject
-    PublicationMetadataRepositoryImpl(Provider<EntityManager> em) {
+    public PublicationMetadataRepositoryImpl(Provider<EntityManager> em) {
         super(em);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataServiceImpl.java
index 26f0e1801b..684e24ae7b 100644
--- a/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/grading/PublicationMetadataServiceImpl.java
@@ -6,11 +6,11 @@ import se.su.dsv.scipro.system.Language;
 import jakarta.inject.Inject;
 import java.util.Objects;
 
-class PublicationMetadataServiceImpl implements PublicationMetadataService {
+public class PublicationMetadataServiceImpl implements PublicationMetadataService {
     private final PublicationMetadataRepository publicationMetadataRepository;
 
     @Inject
-    PublicationMetadataServiceImpl(PublicationMetadataRepository publicationMetadataRepository) {
+    public PublicationMetadataServiceImpl(PublicationMetadataRepository publicationMetadataRepository) {
         this.publicationMetadataRepository = publicationMetadataRepository;
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarModule.java b/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarModule.java
deleted file mode 100644
index 0d5d8bf9a4..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarModule.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package se.su.dsv.scipro.integration.activityfinalseminar;
-
-import com.google.inject.AbstractModule;
-
-public class ActivityFinalSeminarModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(FinalSeminarActivityHandler.class).asEagerSingleton();
-        bind(ActivityFinalSeminarRepository.class).to(ActivityFinalSeminarRepositoryImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java b/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
index 87191121e6..6f1af4098c 100644
--- a/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/integration/activityfinalseminar/ActivityFinalSeminarRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.integration.activityfinalseminar;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
diff --git a/core/src/main/java/se/su/dsv/scipro/integration/activityforum/ActivityForumModule.java b/core/src/main/java/se/su/dsv/scipro/integration/activityforum/ActivityForumModule.java
deleted file mode 100644
index c14a655f13..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/integration/activityforum/ActivityForumModule.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package se.su.dsv.scipro.integration.activityforum;
-
-import com.google.inject.AbstractModule;
-
-public class ActivityForumModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(PostActivityUploadToForum.class).asEagerSingleton();
-        bind(ActivityThreadRepository.class).to(ActivityThreadRepositoryImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/mail/PrintingMailer.java b/core/src/main/java/se/su/dsv/scipro/mail/PrintingMailer.java
index a5c7f597be..179bffc02c 100644
--- a/core/src/main/java/se/su/dsv/scipro/mail/PrintingMailer.java
+++ b/core/src/main/java/se/su/dsv/scipro/mail/PrintingMailer.java
@@ -6,7 +6,7 @@ import se.su.dsv.scipro.file.FileDescription;
 import java.util.Arrays;
 import java.util.UUID;
 
-class PrintingMailer implements Mailer {
+public class PrintingMailer implements Mailer {
     @Override
     public MailResult mail(final String fromName, final String fromEmail, final String[] recipients, final String subject, final String message, final FileDescription attachment) {
         return new MailResult() {
diff --git a/core/src/main/java/se/su/dsv/scipro/match/AllowAllIdeaCreationJudge.java b/core/src/main/java/se/su/dsv/scipro/match/AllowAllIdeaCreationJudge.java
index f5e0035fa5..6a91588ff1 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/AllowAllIdeaCreationJudge.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/AllowAllIdeaCreationJudge.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-class AllowAllIdeaCreationJudge implements IdeaCreationJudge {
+public class AllowAllIdeaCreationJudge implements IdeaCreationJudge {
     @Override
     public Decision ruling(Idea idea) {
         return Decision.allowed();
diff --git a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
index 0156c88bd8..29499c6f0c 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/ApplicationPeriodServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import com.querydsl.core.types.dsl.Expressions;
 import se.su.dsv.scipro.system.PageRequest;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java b/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
index 7714a267a9..caa4bab050 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/FirstMeetingRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
index 07a468f894..d3c410eb05 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/IdeaServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
index 44c702b1aa..9eb0a58232 100755
--- a/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/KeywordServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/core/src/main/java/se/su/dsv/scipro/match/MatchModule.java b/core/src/main/java/se/su/dsv/scipro/match/MatchModule.java
deleted file mode 100644
index 28e19d2295..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/match/MatchModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package se.su.dsv.scipro.match;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.multibindings.OptionalBinder;
-
-public class MatchModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        OptionalBinder.newOptionalBinder(binder(), IdeaCreationJudge.class)
-                .setDefault().to(AllowAllIdeaCreationJudge.class);
-        bind(ProjectStartNotifier.class).asEagerSingleton();
-        bind(AddActivityPlanOnProjectStart.class).asEagerSingleton();
-        bind(ApplicationPeriodService.class).to(ApplicationPeriodServiceImpl.class);
-        bind(IdeaService.class).to(IdeaServiceImpl.class);
-        bind(FirstMeetingRepository.class).to(FirstMeetingRepositoryImpl.class);
-        bind(KeywordService.class).to(KeywordServiceImpl.class);
-        bind(MatchService.class).to(MatchServiceImpl.class);
-        bind(ProgramService.class).to(ProgramServiceImpl.class);
-        bind(MatchFollowUpService.class).to(MatchFollowUpServiceImpl.class);
-        bind(TargetService.class).to(TargetServiceImpl.class);
-        bind(ApplicationPeriodProjectTypeService.class).to(ApplicationPeriodProjectTypeServiceImpl.class);
-        bind(PreliminaryMatchService.class).to(PreliminaryMatchServiceImpl.class);
-        bind(IdeaRepository.class).to(IdeaRepositoryImpl.class);
-        bind(TargetRepository.class).to(TargetRepositoryImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
index e759f53f93..925831af46 100644
--- a/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/match/ProgramServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import se.su.dsv.scipro.system.PageRequest;
 import se.su.dsv.scipro.system.Sort;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java b/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
index e43bb892e8..8d8aaaeb33 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/MilestoneActivityTemplateRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.milestones;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate;
diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
index f5bd91a316..197e947bda 100644
--- a/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/impl/MilestonePhaseTemplateServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.milestones.service.impl;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.Sort;
 import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate;
 import se.su.dsv.scipro.milestones.dataobjects.QMilestonePhaseTemplate;
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationControllerImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationControllerImpl.java
index 8bd1a88a13..0a4a6e08f4 100755
--- a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationControllerImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationControllerImpl.java
@@ -1,6 +1,5 @@
 package se.su.dsv.scipro.notifications;
 
-import com.google.inject.ProvisionException;
 import se.su.dsv.scipro.data.dataobjects.Member;
 import se.su.dsv.scipro.finalseminar.FinalSeminar;
 import se.su.dsv.scipro.group.Group;
@@ -94,7 +93,7 @@ public class NotificationControllerImpl implements NotificationController {
         try {
             return currentUserProvider.get().get();
         }
-        catch (ProvisionException ignored) {
+        catch (RuntimeException ignored) {
             return null;
         }
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationModule.java b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationModule.java
deleted file mode 100644
index 507ca4bcab..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationModule.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package se.su.dsv.scipro.notifications;
-
-import com.google.inject.PrivateModule;
-import se.su.dsv.scipro.notifications.interfaces.NotificationMailFormatter;
-import se.su.dsv.scipro.notifications.interfaces.impl.NotificationMailFormatterImpl;
-
-public class NotificationModule extends PrivateModule {
-
-    public static final String NOTIFICATION_RELATIVE_PAGE_URL = "notification";
-
-    @Override
-    protected void configure() {
-        bind(Notifications.class).asEagerSingleton();
-
-        bind(NotificationService.class).to(NotificationServiceImpl.class);
-        expose(NotificationService.class);
-
-        bind(NotificationEventRepository.class).to(NotificationEventRepositoryImpl.class);
-        bind(NotificationEventService.class).to(NotificationEventServiceImpl.class);
-        expose(NotificationEventService.class);
-
-        bind(NotificationMailFormatter.class).to(NotificationMailFormatterImpl.class);
-        expose(NotificationMailFormatter.class);
-        bind(NotificationController.class).to(NotificationControllerImpl.class);
-        expose(NotificationController.class);
-    }
-
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
index 826991801f..6c460a0741 100755
--- a/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/NotificationServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.notifications;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import se.su.dsv.scipro.system.Pageable;
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/interfaces/impl/NotificationMailFormatterImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/interfaces/impl/NotificationMailFormatterImpl.java
index cdf67a52d4..b8aeadcb99 100755
--- a/core/src/main/java/se/su/dsv/scipro/notifications/interfaces/impl/NotificationMailFormatterImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/interfaces/impl/NotificationMailFormatterImpl.java
@@ -8,7 +8,6 @@ import se.su.dsv.scipro.finalseminar.FinalSeminar;
 import se.su.dsv.scipro.finalseminar.FinalSeminarService;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
-import se.su.dsv.scipro.notifications.NotificationModule;
 import se.su.dsv.scipro.notifications.dataobject.*;
 import se.su.dsv.scipro.notifications.interfaces.NotificationMailFormatter;
 import se.su.dsv.scipro.project.Project;
@@ -158,7 +157,7 @@ public class NotificationMailFormatterImpl implements NotificationMailFormatter
     private String getAbsoluteURL(final Notification notification) {
         final String baseUrl = systemSettingsService.getGeneralSystemSettingsInstance().getSciproURL();
         long id = notification.getNotificationEvent().getId();
-        return baseUrl + "/" + NotificationModule.NOTIFICATION_RELATIVE_PAGE_URL + "?id=" + id;
+        return baseUrl + "/" + "notification" + "?id=" + id;
     }
 
     String makeProperty(Object... parts) {
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/DeliveryConfigurationServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/DeliveryConfigurationServiceImpl.java
index 285b9f6c0a..75f7624ac6 100644
--- a/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/DeliveryConfigurationServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/DeliveryConfigurationServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.notifications.settings.service;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.notifications.dataobject.Notification;
 import se.su.dsv.scipro.notifications.dataobject.Notification.Type;
 import se.su.dsv.scipro.notifications.settings.entities.DeliveryConfiguration;
diff --git a/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/ReceiverConfigurationServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/ReceiverConfigurationServiceImpl.java
index 37befe6c34..596c2e3821 100644
--- a/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/ReceiverConfigurationServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/notifications/settings/service/ReceiverConfigurationServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.notifications.settings.service;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.data.dataobjects.Member;
 import se.su.dsv.scipro.notifications.dataobject.Notification;
 import se.su.dsv.scipro.notifications.settings.entities.QReceiverConfiguration;
diff --git a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthModule.java b/core/src/main/java/se/su/dsv/scipro/oauth/OAuthModule.java
deleted file mode 100644
index bce825afe1..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthModule.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package se.su.dsv.scipro.oauth;
-
-import com.google.inject.PrivateModule;
-import com.google.inject.Provides;
-
-import jakarta.inject.Named;
-
-public class OAuthModule extends PrivateModule {
-    @Override
-    protected void configure() {
-        bind(OAuthService.class).to(OAuthServiceImpl.class);
-
-        expose(OAuthService.class);
-    }
-
-    @Provides
-    OAuthSettings settings(
-        @Named("oauth.uri") String uri,
-        @Named("oauth.redirectUri") String redirectUri,
-        @Named("oauth.clientId") String clientId,
-        @Named("oauth.clientSecret") String clientSecret)
-    {
-        return new OAuthSettings(uri, redirectUri, clientId, clientSecret);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java b/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java
index a8b41b8648..2a20bcf204 100644
--- a/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java
+++ b/core/src/main/java/se/su/dsv/scipro/oauth/OAuthSettings.java
@@ -1,4 +1,4 @@
 package se.su.dsv.scipro.oauth;
 
-record OAuthSettings(String uri, String redirectUri, String clientId, String clientSecret) {
+public record OAuthSettings(String uri, String redirectUri, String clientId, String clientSecret) {
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/CommentServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/CommentServiceImpl.java
index db26096621..63723ecf16 100644
--- a/core/src/main/java/se/su/dsv/scipro/peer/CommentServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/CommentServiceImpl.java
@@ -10,7 +10,7 @@ import java.util.List;
 public class CommentServiceImpl extends AbstractServiceImpl<Comment, Long> implements CommentService {
 
     @Inject
-    protected CommentServiceImpl(Provider<EntityManager> em) {
+    public CommentServiceImpl(Provider<EntityManager> em) {
         super(em, Comment.class, QComment.comment1);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
index 025bb02da1..8210abd3c5 100644
--- a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadServiceImpl.java
index 82d1e68e53..604e36b9a5 100644
--- a/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/CommentThreadServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 import jakarta.inject.Inject;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerModule.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerModule.java
deleted file mode 100644
index 0fdd05980c..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerModule.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package se.su.dsv.scipro.peer;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.multibindings.Multibinder;
-import se.su.dsv.scipro.system.Lifecycle;
-
-public class PeerModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        Multibinder.newSetBinder(binder(), Lifecycle.class)
-                .addBinding().to(PeerWorkerSchedules.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerPortalImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerPortalImpl.java
index 5582001f6a..aa6b178072 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerPortalImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerPortalImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.peer;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import se.su.dsv.scipro.file.FileReference;
@@ -152,7 +152,7 @@ public class PeerPortalImpl implements PeerPortal, PerformReviewService {
     }
 
     @Override
-    @Transactional(ignore = {TooShortCommentException.class, MissingAnswerException.class})
+    @Transactional(dontRollbackOn = {TooShortCommentException.class, MissingAnswerException.class})
     public void submit(PeerReview review, Optional<FileUpload> upload) {
         review.submit();
         final Optional<FileReference> fileDescription = storePeerReviewFileUpload(upload);
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
index d6ca147c68..1a5c7439ff 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
index b79394efd6..ad77dcfe22 100755
--- a/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/peer/PeerRequestServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.peer;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.dsl.BooleanExpression;
 import se.su.dsv.scipro.system.Pageable;
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismControlImpl.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismControlImpl.java
index b21bd32fe9..d72913163d 100755
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismControlImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismControlImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.plagiarism;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismModule.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismModule.java
deleted file mode 100644
index 3f862ea21c..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/PlagiarismModule.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package se.su.dsv.scipro.plagiarism;
-
-import com.google.inject.AbstractModule;
-
-public class PlagiarismModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(PlagiarismControl.class).to(PlagiarismControlImpl.class);
-        bind(PlagiarismRequestRepository.class).to(PlagiarismRequestRepositoryImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundApiImpl.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundApiImpl.java
index 774dfdda3a..5a5f58f929 100644
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundApiImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundApiImpl.java
@@ -36,7 +36,7 @@ public class UrkundApiImpl implements UrkundApi {
     private final FileService fileService;
 
     @Inject
-    UrkundApiImpl(final UrkundSettingsRepository urkundSettingsRepository, FileService fileService) {
+    public UrkundApiImpl(final UrkundSettingsRepository urkundSettingsRepository, FileService fileService) {
         this.urkundSettingsRepository = urkundSettingsRepository;
         this.fileService = fileService;
         objectMapper = new ObjectMapper();
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundModule.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundModule.java
deleted file mode 100644
index 573a3a5559..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package se.su.dsv.scipro.plagiarism.urkund;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
-import com.google.inject.Scopes;
-import se.su.dsv.scipro.sukat.Sukat;
-
-import jakarta.persistence.EntityManager;
-
-public class UrkundModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(UrkundService.class).to(UrkundServiceImpl.class);
-        bind(UrkundSubmissionRepository.class).to(UrkundSubmissionRepositoryImpl.class);
-        bind(UrkundApi.class).to(UrkundApiImpl.class).in(Scopes.SINGLETON);
-        bind(UrkundSettingsRepository.class).to(UrkundSettingsRepositoryImpl.class);
-
-        requireBinding(EntityManager.class);
-        requireBinding(Sukat.class);
-    }
-
-    @Provides
-    public UrkundSettings urkundSettings(UrkundSettingsRepository urkundSettingsRepository) {
-        return urkundSettingsRepository.getSettings();
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundServiceImpl.java
index 8eac423c9b..e83125690b 100644
--- a/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/plagiarism/urkund/UrkundServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.plagiarism.urkund;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileDescription;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
diff --git a/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java b/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java
index 679ba53157..6bf6f2bd19 100755
--- a/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java
+++ b/core/src/main/java/se/su/dsv/scipro/profiles/CurrentProfile.java
@@ -1,14 +1,10 @@
 package se.su.dsv.scipro.profiles;
 
-import jakarta.inject.Inject;
-import jakarta.inject.Named;
-
 public class CurrentProfile {
 
     private String currentProfileString;
 
-    @Inject
-    public void setCurrentProfileString(@Named("profile") String currentProfileString) {
+    public void setCurrentProfileString(String currentProfileString) {
         this.currentProfileString = currentProfileString;
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectPeopleStatisticsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectPeopleStatisticsServiceImpl.java
index 9a2862ae63..ada1db066b 100644
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectPeopleStatisticsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectPeopleStatisticsServiceImpl.java
@@ -10,7 +10,7 @@ import jakarta.persistence.EntityManager;
 public class ProjectPeopleStatisticsServiceImpl extends AbstractServiceImpl<Project, Long> implements ProjectPeopleStatisticsService {
 
     @Inject
-    protected ProjectPeopleStatisticsServiceImpl(Provider<EntityManager> em) {
+    public ProjectPeopleStatisticsServiceImpl(Provider<EntityManager> em) {
         super(em, Project.class, QProject.project);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
index 43892156ca..a1c985044f 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.project;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.JpaRepository;
 import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.system.User;
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
index f8dfb462bd..8211412568 100644
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.project;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.GenericRepo;
 import se.su.dsv.scipro.system.User;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
index 617f39e795..e52cae2397 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.project;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.BooleanBuilder;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.BooleanExpression;
diff --git a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerRepositoryImpl.java
index 63dada7bf6..a437f162d5 100644
--- a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.projectpartner;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.system.AbstractRepository;
 
 import jakarta.inject.Inject;
diff --git a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
index 3b39364a33..5af5e3a0a7 100644
--- a/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/projectpartner/ProjectPartnerServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.projectpartner;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.dsl.Expressions;
 import com.querydsl.jpa.JPAExpressions;
 import se.su.dsv.scipro.system.Pageable;
diff --git a/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionModule.java b/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionModule.java
deleted file mode 100644
index 872fd7d8dd..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionModule.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package se.su.dsv.scipro.reflection;
-
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.finalseminar.AuthorRepository;
-
-public class ReflectionModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        requireBinding(AuthorRepository.class);
-
-        bind(ReflectionService.class).to(ReflectionServiceImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionServiceImpl.java
index ccd23a6244..bdc36bc904 100644
--- a/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reflection/ReflectionServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reflection;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.finalseminar.AuthorRepository;
 import se.su.dsv.scipro.finalseminar.FinalSeminarService;
 import se.su.dsv.scipro.project.Author;
@@ -9,12 +9,12 @@ import se.su.dsv.scipro.system.User;
 
 import jakarta.inject.Inject;
 
-class ReflectionServiceImpl implements ReflectionService {
+public class ReflectionServiceImpl implements ReflectionService {
     private final AuthorRepository authorRepository;
     private final FinalSeminarService finalSeminarService;
 
     @Inject
-    ReflectionServiceImpl(AuthorRepository authorRepository, FinalSeminarService finalSeminarService) {
+    public ReflectionServiceImpl(AuthorRepository authorRepository, FinalSeminarService finalSeminarService) {
         this.authorRepository = authorRepository;
         this.finalSeminarService = finalSeminarService;
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
index 6f9b1a9824..1dd53e1665 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.report;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
 import se.su.dsv.scipro.grading.GradingBasis;
 import se.su.dsv.scipro.grading.GradingReportTemplateService;
@@ -133,6 +133,7 @@ public class GradingReportServiceImpl implements GradingReportTemplateService, G
     }
 
     @Override
+    @Transactional
     public List<SupervisorGradingReport> getSupervisorGradingReports(Project project) {
         List<SupervisorGradingReport> gradingReports = new ArrayList<>();
         for (User user : project.getProjectParticipants()) {
diff --git a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
index 8cf96941a1..7eec180ce5 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/GradingReportTemplateRepoImpl.java
@@ -1,8 +1,8 @@
 package se.su.dsv.scipro.report;
 
-import com.google.inject.persist.Transactional;
 import com.querydsl.jpa.JPAExpressions;
 import com.querydsl.jpa.JPQLQuery;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.grading.GradingReportTemplateUpdate;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.GenericRepo;
diff --git a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
index 417e7a15bf..231fe6f692 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/OppositionReportServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.report;
 
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.finalseminar.FinalSeminarOpposition;
diff --git a/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
index f8ba6bd969..44d938d819 100644
--- a/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/report/ReportServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.report;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.file.FileUpload;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/FinalSeminarApprovalServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/FinalSeminarApprovalServiceImpl.java
index 5d8e123342..fec755260c 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/FinalSeminarApprovalServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/FinalSeminarApprovalServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.reviewing;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.file.FileUpload;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
index 736c9d4120..9a5239e570 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ProjectFinalSeminarStatisticsServiceImpl.java
@@ -27,7 +27,7 @@ import static com.querydsl.core.types.dsl.Expressions.anyOf;
 public class ProjectFinalSeminarStatisticsServiceImpl extends AbstractServiceImpl<Project, Long> implements ProjectFinalSeminarStatisticsService {
 
     @Inject
-    protected ProjectFinalSeminarStatisticsServiceImpl(Provider<EntityManager> em) {
+    public ProjectFinalSeminarStatisticsServiceImpl(Provider<EntityManager> em) {
         super(em, Project.class, QProject.project);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerCapacityServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerCapacityServiceImpl.java
index c758523db5..08e67aa38b 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerCapacityServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerCapacityServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.reviewing;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectService;
 import se.su.dsv.scipro.project.ReviewerAssignedEvent;
@@ -25,7 +25,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Stream;
 
-class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAssignmentService {
+public class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAssignmentService {
     private final ReviewerTargetRepository reviewerTargetRepository;
     private final DecisionRepository decisionRepository;
     private final UserService userService;
@@ -33,7 +33,7 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
     private final EventBus eventBus;
 
     @Inject
-    ReviewerCapacityServiceImpl(
+    public ReviewerCapacityServiceImpl(
             ReviewerTargetRepository reviewerTargetRepository,
             DecisionRepository decisionRepository,
             UserService userService,
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepository.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepository.java
index 0094b9e7dc..e95b191bb8 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepository.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-interface ReviewerDeadlineSettingsRepository {
+public interface ReviewerDeadlineSettingsRepository {
     ReviewerDeadlineSettings findOne(long instanceId);
 
     ReviewerDeadlineSettings save(ReviewerDeadlineSettings entity);
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepositoryImpl.java
index bcaf1e434e..6839c0cac5 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsRepositoryImpl.java
@@ -6,12 +6,12 @@ import se.su.dsv.scipro.system.AbstractRepository;
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 
-class ReviewerDeadlineSettingsRepositoryImpl
+public class ReviewerDeadlineSettingsRepositoryImpl
         extends AbstractRepository
         implements ReviewerDeadlineSettingsRepository
 {
     @Inject
-    ReviewerDeadlineSettingsRepositoryImpl(Provider<EntityManager> em) {
+    public ReviewerDeadlineSettingsRepositoryImpl(Provider<EntityManager> em) {
         super(em);
     }
 
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsServiceImpl.java
index e52d28d078..3f0db387dd 100755
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerDeadlineSettingsServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 import jakarta.inject.Inject;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
index 0079e40a2b..a08f5ffcbc 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerInteractionServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.reviewing;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.forum.Attachment;
 import se.su.dsv.scipro.forum.BasicForumService;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerTargetRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerTargetRepositoryImpl.java
index 985ae1934b..ba1b9b961c 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerTargetRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerTargetRepositoryImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.persistence.EntityManager;
 import se.su.dsv.scipro.system.AbstractRepository;
 import se.su.dsv.scipro.system.User;
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingModule.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingModule.java
deleted file mode 100644
index 9a0d1d53f7..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewingModule.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package se.su.dsv.scipro.reviewing;
-
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.file.FileService;
-
-import jakarta.persistence.EntityManager;
-
-public class ReviewingModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        requireBinding(FileService.class);
-        requireBinding(EntityManager.class);
-        bind(ReviewerThreadRepository.class).to(ReviewerThreadRepositoryImpl.class);
-        bind(ReviewerInteractionService.class).to(ReviewerInteractionServiceImpl.class);
-        bind(FinalSeminarApprovalService.class).to(FinalSeminarApprovalServiceImpl.class);
-        bind(MyReviewService.class).to(ReviewingServiceImpl.class);
-        bind(ReviewerDecisionService.class).to(ReviewingServiceImpl.class);
-        bind(RoughDraftApprovalService.class).to(RoughDraftApprovalServiceImpl.class);
-        bind(ReviewerDeadlineSettingsService.class).to(ReviewerDeadlineSettingsServiceImpl.class);
-        bind(ReviewerDeadlineSettingsRepository.class).to(ReviewerDeadlineSettingsRepositoryImpl.class);
-        bind(ReviewerDeadlineFollowupService.class).to(ReviewerDeadlineFollowupServiceImpl.class);
-        bind(ReviewerCapacityService.class).to(ReviewerCapacityServiceImpl.class);
-        bind(ReviewerAssignmentService.class).to(ReviewerCapacityServiceImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java
index fea26c83d6..f7a4365b24 100644
--- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java
@@ -1,7 +1,7 @@
 package se.su.dsv.scipro.reviewing;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.file.FileReference;
 import se.su.dsv.scipro.file.FileService;
 import se.su.dsv.scipro.file.FileUpload;
diff --git a/core/src/main/java/se/su/dsv/scipro/security/auth/AuthenticationModule.java b/core/src/main/java/se/su/dsv/scipro/security/auth/AuthenticationModule.java
deleted file mode 100644
index 3598358043..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/security/auth/AuthenticationModule.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package se.su.dsv.scipro.security.auth;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.multibindings.Multibinder;
-
-public class AuthenticationModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(AuthenticationService.class).to(AuthenticationServiceImpl.class);
-
-        Multibinder<AuthenticationProvider> authenticationProviders
-                = Multibinder.newSetBinder(binder(), AuthenticationProvider.class);
-        authenticationProviders.addBinding().to(LocalAuthentication.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
index a89512dab1..788ae44b07 100644
--- a/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/springdata/serviceimpls/UserProfileServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.springdata.serviceimpls;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 import se.su.dsv.scipro.settings.dataobjects.QUserProfile;
 import se.su.dsv.scipro.settings.dataobjects.UserProfile;
@@ -43,6 +43,7 @@ public class UserProfileServiceImpl extends AbstractServiceImpl<UserProfile, Lon
     }
 
     @Override
+    @Transactional
     public void setSelectedRole(User user, Roles role) {
         UserProfile profile = findByUser(user);
         profile.setSelectedRole(role);
diff --git a/core/src/main/java/se/su/dsv/scipro/sukat/SukatModule.java b/core/src/main/java/se/su/dsv/scipro/sukat/SukatModule.java
deleted file mode 100644
index 544a17ec39..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/sukat/SukatModule.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package se.su.dsv.scipro.sukat;
-
-import com.google.inject.AbstractModule;
-
-public class SukatModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        bind(Sukat.class).to(LDAP.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/survey/SurveyModule.java b/core/src/main/java/se/su/dsv/scipro/survey/SurveyModule.java
deleted file mode 100644
index 54a6c83c92..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/survey/SurveyModule.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package se.su.dsv.scipro.survey;
-
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.finalthesis.FinalThesisService;
-
-import jakarta.persistence.EntityManager;
-
-public class SurveyModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        requireBinding(EntityManager.class);
-        requireBinding(FinalThesisService.class);
-        bind(SurveyRepository.class).to(SurveyRepositoryImpl.class);
-        bind(QuestionRepository.class).to(QuestionRepositoryImpl.class);
-        bind(SurveyService.class).to(SurveyServiceImpl.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/survey/SurveyServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/survey/SurveyServiceImpl.java
index ecb7c22070..386c771880 100644
--- a/core/src/main/java/se/su/dsv/scipro/survey/SurveyServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/survey/SurveyServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.survey;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.finalthesis.FinalThesisService;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
 import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
index 748940900e..f6d52a414b 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/AbstractServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.EntityPathBase;
 import com.querydsl.core.types.dsl.Expressions;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java b/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
index ad0f13494d..b326a9c967 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FooterAddressRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 @Transactional
 public interface FooterAddressRepo extends JpaRepository<FooterAddress, Long>, QueryDslPredicateExecutor<FooterAddress> {
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
index bd0c349289..8994ba19ae 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 import java.util.List;
 import java.util.Optional;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkServiceImpl.java
index 327b83accc..901a487f84 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/FooterLinkServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/FooterLinkServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 import jakarta.inject.Inject;
 import java.util.List;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java b/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
index 2a42a60a0e..07ae33135f 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/GenericRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import com.querydsl.core.types.Predicate;
 import com.querydsl.core.types.dsl.EntityPathBase;
 import com.querydsl.core.types.dsl.Expressions;
diff --git a/core/src/main/java/se/su/dsv/scipro/system/MergeServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/system/MergeServiceImpl.java
index 0c58d7f4d5..e14516ffb9 100644
--- a/core/src/main/java/se/su/dsv/scipro/system/MergeServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/MergeServiceImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 import jakarta.inject.Inject;
 
diff --git a/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java b/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
index 32d5c4c4f9..53dfe5d7a8 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/PasswordRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 
 
 @Transactional
diff --git a/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java b/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
index 1a227078e1..9831c44da1 100755
--- a/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
+++ b/core/src/main/java/se/su/dsv/scipro/system/UserRepo.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.system;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 
 import java.util.Collection;
diff --git a/core/src/main/java/se/su/dsv/scipro/war/PluginConfiguration.java b/core/src/main/java/se/su/dsv/scipro/war/PluginConfiguration.java
new file mode 100644
index 0000000000..a111714130
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/war/PluginConfiguration.java
@@ -0,0 +1,4 @@
+package se.su.dsv.scipro.war;
+
+public interface PluginConfiguration {
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java b/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java
index 751b6c8831..5c721b2ffe 100755
--- a/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java
+++ b/core/src/main/java/se/su/dsv/scipro/workerthreads/AbstractWorker.java
@@ -1,6 +1,5 @@
 package se.su.dsv.scipro.workerthreads;
 
-import com.google.inject.persist.UnitOfWork;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -23,9 +22,7 @@ public abstract class AbstractWorker implements Worker {
     private WorkerData wd = null;
     private Date lastSuccessfulRun = new Date(0);
     private boolean successfulWorker = true;
-    private Provider<EntityManager> emProvider;
-    private EntityTransaction transaction;
-    private UnitOfWork unitOfWork;
+    private WorkerTransactionManager txManager;
 
     /**
      * Subclasses must be annotated with @Component or similar annotation in order for autowiring of dependencies to work
@@ -39,22 +36,13 @@ public abstract class AbstractWorker implements Worker {
     }
 
     @Inject
-    public void setTxManager(Provider<EntityManager> em) {
-        this.emProvider = em;
-    }
-
-    @Inject
-    public void setUnitOfWork(UnitOfWork unitOfWork) {
-        this.unitOfWork = unitOfWork;
+    public void setTxManager(WorkerTransactionManager txManager) {
+        this.txManager = txManager;
     }
 
     @Override
     public void run() {
-        unitOfWork.begin();
-        EntityManager em = emProvider.get();
         try {
-            transaction = em.getTransaction();
-
             wd = workerDataService.getWorkerDataByName(getName());
             if (wd != null) {
                 lastSuccessfulRun = wd.getLastSuccessfulRun();
@@ -80,9 +68,7 @@ public abstract class AbstractWorker implements Worker {
                 setSuccessfulWorker(false);
             }
             finally {
-                if (transaction.isActive() && em.isOpen()) {
-                    transaction.rollback();
-                }
+                txManager.rollbackIfActive();
             }
 
             // in case a job crashes or clears the cache (so the entity is detached)
@@ -95,10 +81,7 @@ public abstract class AbstractWorker implements Worker {
             saveWorkerData();
 
         } finally {
-            if (transaction.isActive() && em.isOpen()) {
-                transaction.rollback();
-            }
-            unitOfWork.end();
+            txManager.rollbackIfActive();
         }
     }
 
@@ -148,15 +131,15 @@ public abstract class AbstractWorker implements Worker {
     protected abstract void doWork();
 
     protected void beginTransaction() {
-        transaction.begin();
+        txManager.begin();
     }
 
     protected void commitTransaction() {
-        transaction.commit();
+        txManager.commit();
     }
 
     protected void rollbackTransaction() {
-        transaction.rollback();
+        txManager.rollback();
     }
 
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerModule.java b/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerModule.java
deleted file mode 100644
index 370d486d83..0000000000
--- a/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package se.su.dsv.scipro.workerthreads;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Scopes;
-import com.google.inject.multibindings.Multibinder;
-import se.su.dsv.scipro.mail.MailEventWorker;
-import se.su.dsv.scipro.projectpartner.RemoveFulfilledPartnerAdsWorker;
-import se.su.dsv.scipro.system.Lifecycle;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-
-public class WorkerModule extends AbstractModule {
-    private static final int NUMBER_OF_WORKER_THREADS = 4;
-
-    @Override
-    protected void configure() {
-        Multibinder.newSetBinder(binder(), Lifecycle.class).addBinding().to(SchedulerImpl.class);
-        bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS));
-        bind(Scheduler.class).to(SchedulerImpl.class).in(Scopes.SINGLETON);
-        bind(TemporaryWorkerScheduler.class).asEagerSingleton();
-        bind(WorkerDataService.class).to(WorkerDataServiceImpl.class);
-        bind(ThesisUploadDeadlineWorker.class);
-        bind(ManualMatchRemindWorker.class);
-        bind(MailEventWorker.class);
-        bind(NotificationCompilationWorker.class);
-        bind(ThesisUploadReminderWorker.class);
-        bind(IdeaExportWorker.class);
-        bind(RemoveFulfilledPartnerAdsWorker.class);
-    }
-}
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerTransactionManager.java b/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerTransactionManager.java
new file mode 100644
index 0000000000..1ff2b458d4
--- /dev/null
+++ b/core/src/main/java/se/su/dsv/scipro/workerthreads/WorkerTransactionManager.java
@@ -0,0 +1,20 @@
+package se.su.dsv.scipro.workerthreads;
+
+public interface WorkerTransactionManager {
+    void rollbackIfActive();
+
+    /**
+     * @throws IllegalStateException if a transaction is already active
+     */
+    void begin();
+
+    /**
+     * @throws IllegalStateException if a transaction is not active
+     */
+    void commit();
+
+    /**
+     * @throws IllegalStateException if a transaction is not active
+     */
+    void rollback();
+}
diff --git a/core/src/test/java/se/su/dsv/scipro/forum/ForumModuleTest.java b/core/src/test/java/se/su/dsv/scipro/forum/ForumModuleTest.java
index 4c0f37379d..90cc242e77 100644
--- a/core/src/test/java/se/su/dsv/scipro/forum/ForumModuleTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/forum/ForumModuleTest.java
@@ -1,20 +1,15 @@
 package se.su.dsv.scipro.forum;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import se.su.dsv.scipro.date.DateService;
-import se.su.dsv.scipro.file.FileModule;
-import se.su.dsv.scipro.file.FileStore;
 import se.su.dsv.scipro.notifications.NotificationController;
 import se.su.dsv.scipro.notifications.NotificationService;
-import se.su.dsv.scipro.test.GuiceTest;
-import se.su.dsv.scipro.test.InMemoryFileStore;
+import se.su.dsv.scipro.test.SpringTest;
 
 @ExtendWith(MockitoExtension.class)
-public abstract class ForumModuleTest extends GuiceTest {
+public abstract class ForumModuleTest extends SpringTest {
     // TODO: Work towards removing these dependencies.
     @Mock
     private DateService dateService;
@@ -23,18 +18,4 @@ public abstract class ForumModuleTest extends GuiceTest {
     @Mock
     private NotificationService notificationService;
 
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(new ForumModule());
-                install(new FileModule());
-                bind(FileStore.class).to(InMemoryFileStore.class);
-                bind(DateService.class).toInstance(dateService);
-                bind(NotificationController.class).toInstance(notificationController);
-                bind(NotificationService.class).toInstance(notificationService);
-            }
-        };
-    }
 }
diff --git a/core/src/test/java/se/su/dsv/scipro/group/GroupServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/group/GroupServiceImplTest.java
index da314ab9c1..4a3e6dc33c 100644
--- a/core/src/test/java/se/su/dsv/scipro/group/GroupServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/group/GroupServiceImplTest.java
@@ -1,135 +1,135 @@
-package se.su.dsv.scipro.group;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.DegreeType;
-import se.su.dsv.scipro.system.ProjectType;
-import se.su.dsv.scipro.system.User;
-import se.su.dsv.scipro.test.IntegrationTest;
-
-import jakarta.inject.Inject;
-import java.time.LocalDate;
-import java.util.Collections;
-import java.util.HashSet;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasItem;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class GroupServiceImplTest extends IntegrationTest {
-
-    @Inject
-    private GroupServiceImpl groupService;
-    private User author;
-    private User creator;
-    private User reviewer;
-    private User cosupervisor;
-    private Group group;
-    private ProjectType projectType;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        author = saveUser("author", "name", "author@example.com");
-        creator = saveUser("supervisor", "sup", "supervisor@example.com");
-        reviewer = saveUser("reviewer", "test", "reviewer@example.com");
-        cosupervisor = saveUser("cosupervisor", "test", "reviewer@example.com");
-        projectType = save(new ProjectType(DegreeType.BACHELOR, "b", "b"));
-        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
-        Project project = save(Project.builder().title("title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
-        project.addProjectParticipant(author);
-        project.addReviewer(reviewer);
-        project.addCoSupervisor(cosupervisor);
-        createGroup(project);
-    }
-
-    @Test
-    public void find_by_author() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setAuthor(author);
-        assertGroup(filter);
-    }
-
-    @Test
-    public void find_all() {
-        GroupService.Filter filter = new GroupService.Filter();
-        assertGroup(filter);
-    }
-
-    @Test
-    public void find_by_creator_fails() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setCreator(author);
-        assertTrue(groupService.findAll(filter).isEmpty());
-    }
-
-    @Test
-    public void find_by_creator() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setCreator(creator);
-        assertGroup(filter);
-    }
-
-    @Test
-    public void find_by_reviewer_involvee() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setNonAuthorInvolvee(reviewer);
-        assertThat(groupService.findAll(filter), not(hasItem(group)));
-    }
-
-    @Test
-    public void find_by_cosupervisor_involvee() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setNonAuthorInvolvee(cosupervisor);
-        assertGroup(filter);
-    }
-
-    @Test
-    public void find_active_groups() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setActive(true);
-        assertGroup(filter);
-
-        filter.setActive(false);
-        assertTrue(groupService.findAll(filter).isEmpty());
-    }
-
-    @Test
-    public void count() {
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setAuthor(author);
-        assertEquals(1, groupService.count(filter));
-    }
-
-    @Test
-    public void find_groups_by_project() {
-        Group another = new Group();
-        another.setTitle("hi");
-        another.setUser(creator);
-        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
-        Project anotherProject = save(Project.builder().title("another").projectType(projectType).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
-        another.setProjects(new HashSet<>(Collections.singletonList(anotherProject)));
-        save(another);
-        GroupService.Filter filter = new GroupService.Filter();
-        filter.setProject(anotherProject);
-        assertEquals(another, groupService.findAll(filter).get(0));
-    }
-
-    private void assertGroup(GroupService.Filter filter) {
-        assertThat(groupService.findAll(filter), hasItem(group));
-    }
-
-    private void createGroup(Project project) {
-        group = new Group();
-        group.setTitle("group title");
-        group.setProjects(new HashSet<>(Collections.singletonList(project)));
-        group.setUser(creator);
-        save(group);
-    }
-
-    private User saveUser(String firstName, String lastName, String emailAddress) {
-        return save(User.builder().firstName(firstName).lastName(lastName).emailAddress(emailAddress).build());
-    }
-}
+package se.su.dsv.scipro.group;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.DegreeType;
+import se.su.dsv.scipro.system.ProjectType;
+import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.test.IntegrationTest;
+
+import jakarta.inject.Inject;
+import java.time.LocalDate;
+import java.util.Collections;
+import java.util.HashSet;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class GroupServiceImplTest extends IntegrationTest {
+
+    @Inject
+    private GroupServiceImpl groupService;
+    private User author;
+    private User creator;
+    private User reviewer;
+    private User cosupervisor;
+    private Group group;
+    private ProjectType projectType;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        author = saveUser("author", "name", "author@example.com");
+        creator = saveUser("supervisor", "sup", "supervisor@example.com");
+        reviewer = saveUser("reviewer", "test", "reviewer@example.com");
+        cosupervisor = saveUser("cosupervisor", "test", "reviewer@example.com");
+        projectType = save(new ProjectType(DegreeType.BACHELOR, "b", "b"));
+        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
+        Project project = save(Project.builder().title("title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
+        project.addProjectParticipant(author);
+        project.addReviewer(reviewer);
+        project.addCoSupervisor(cosupervisor);
+        createGroup(project);
+    }
+
+    @Test
+    public void find_by_author() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setAuthor(author);
+        assertGroup(filter);
+    }
+
+    @Test
+    public void find_all() {
+        GroupService.Filter filter = new GroupService.Filter();
+        assertGroup(filter);
+    }
+
+    @Test
+    public void find_by_creator_fails() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setCreator(author);
+        assertTrue(groupService.findAll(filter).isEmpty());
+    }
+
+    @Test
+    public void find_by_creator() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setCreator(creator);
+        assertGroup(filter);
+    }
+
+    @Test
+    public void find_by_reviewer_involvee() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setNonAuthorInvolvee(reviewer);
+        assertThat(groupService.findAll(filter), not(hasItem(group)));
+    }
+
+    @Test
+    public void find_by_cosupervisor_involvee() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setNonAuthorInvolvee(cosupervisor);
+        assertGroup(filter);
+    }
+
+    @Test
+    public void find_active_groups() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setActive(true);
+        assertGroup(filter);
+
+        filter.setActive(false);
+        assertTrue(groupService.findAll(filter).isEmpty());
+    }
+
+    @Test
+    public void count() {
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setAuthor(author);
+        assertEquals(1, groupService.count(filter));
+    }
+
+    @Test
+    public void find_groups_by_project() {
+        Group another = new Group();
+        another.setTitle("hi");
+        another.setUser(creator);
+        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
+        Project anotherProject = save(Project.builder().title("another").projectType(projectType).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
+        another.setProjects(new HashSet<>(Collections.singletonList(anotherProject)));
+        save(another);
+        GroupService.Filter filter = new GroupService.Filter();
+        filter.setProject(anotherProject);
+        assertEquals(another, groupService.findAll(filter).get(0));
+    }
+
+    private void assertGroup(GroupService.Filter filter) {
+        assertThat(groupService.findAll(filter), hasItem(group));
+    }
+
+    private void createGroup(Project project) {
+        group = new Group();
+        group.setTitle("group title");
+        group.setProjects(new HashSet<>(Collections.singletonList(project)));
+        group.setUser(creator);
+        save(group);
+    }
+
+    private User saveUser(String firstName, String lastName, String emailAddress) {
+        return save(User.builder().firstName(firstName).lastName(lastName).emailAddress(emailAddress).build());
+    }
+}
diff --git a/core/src/test/java/se/su/dsv/scipro/match/MatchModuleTest.java b/core/src/test/java/se/su/dsv/scipro/match/MatchModuleTest.java
index 63417a754b..7a189d32f4 100644
--- a/core/src/test/java/se/su/dsv/scipro/match/MatchModuleTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/match/MatchModuleTest.java
@@ -1,9 +1,6 @@
 package se.su.dsv.scipro.match;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.name.Names;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -13,10 +10,10 @@ import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
 import se.su.dsv.scipro.mail.MailEventService;
 import se.su.dsv.scipro.notifications.NotificationController;
 import se.su.dsv.scipro.project.ProjectService;
-import se.su.dsv.scipro.test.GuiceTest;
+import se.su.dsv.scipro.test.SpringTest;
 
 @ExtendWith(MockitoExtension.class)
-public abstract class MatchModuleTest extends GuiceTest {
+public abstract class MatchModuleTest extends SpringTest {
     // ToDo:The system services should be move to a system module that other modules can depend on
     @Mock
     protected DateService dateService;
@@ -34,28 +31,4 @@ public abstract class MatchModuleTest extends GuiceTest {
     @Mock
     protected ProjectService projectService;
 
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(new MatchModule());
-                bind(GeneralSystemSettingsService.class).toInstance(generalSystemSettingsService);
-                bind(NotificationController.class).toInstance(notificationController);
-                bind(DateService.class).toInstance(dateService);
-                bind(MailEventService.class).toInstance(mailEventService);
-                bind(ActivityPlanFacade.class).toInstance(activityPlanFacade);
-                bind(EventBus.class).toInstance(eventBus);
-                bind(ProjectService.class).toInstance(projectService);
-
-                // Work towards removing the dependency on NotificationSourceURL
-                bindConstant().annotatedWith(Names.named("source.export.fail.supervisor")).to("");
-                bindConstant().annotatedWith(Names.named("source.export.fail.user")).to("");
-                bindConstant().annotatedWith(Names.named("source.export.success.supervisor")).to("");
-                bindConstant().annotatedWith(Names.named("source.export.success.user")).to("");
-                bindConstant().annotatedWith(Names.named("source.project.supervisor")).to("");
-                bindConstant().annotatedWith(Names.named("source.project.student")).to("");
-            }
-        };
-    }
 }
diff --git a/core/src/test/java/se/su/dsv/scipro/notifications/NotificationControllerImplTest.java b/core/src/test/java/se/su/dsv/scipro/notifications/NotificationControllerImplTest.java
index 6453a0f537..6c770e1a9d 100755
--- a/core/src/test/java/se/su/dsv/scipro/notifications/NotificationControllerImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/notifications/NotificationControllerImplTest.java
@@ -1,6 +1,5 @@
 package se.su.dsv.scipro.notifications;
 
-import com.google.inject.util.Providers;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
@@ -55,7 +54,7 @@ public class NotificationControllerImplTest {
                 notificationService,
                 mailFormatter,
                 mailEventService,
-                receiverConfiguration, deliveryConfiguration, Providers.of(currentUser));
+                receiverConfiguration, deliveryConfiguration, () -> currentUser);
 
         when(mailFormatter.format(isA(Notification.class))).thenReturn(new NotificationMail("Subject", "Body"));
         when(deliveryConfiguration.isDelivery(isA(Notification.Type.class), isA(Enum.class), isA(DeliveryMethod.class), isA(Optional.class))).thenReturn(true);
diff --git a/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSettingsRepositoryTest.java b/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSettingsRepositoryTest.java
index a2eb62c871..8b113e1524 100644
--- a/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSettingsRepositoryTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSettingsRepositoryTest.java
@@ -1,35 +1,16 @@
 package se.su.dsv.scipro.plagiarism.urkund;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
 import org.junit.jupiter.api.Test;
-import se.su.dsv.scipro.file.FileModule;
-import se.su.dsv.scipro.file.FileStore;
-import se.su.dsv.scipro.sukat.SukatModule;
-import se.su.dsv.scipro.test.GuiceTest;
-import se.su.dsv.scipro.test.InMemoryFileStore;
+import se.su.dsv.scipro.test.SpringTest;
 
 import jakarta.inject.Inject;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class UrkundSettingsRepositoryTest extends GuiceTest {
+public class UrkundSettingsRepositoryTest extends SpringTest {
     @Inject
     private UrkundSettingsRepository urkundSettingsRepository;
 
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(new SukatModule());
-                install(new UrkundModule());
-                install(new FileModule());
-                bind(FileStore.class).to(InMemoryFileStore.class);
-            }
-        };
-    }
-
     @Test
     public void change_settings() {
         final UrkundSettings settings = urkundSettingsRepository.getSettings();
diff --git a/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSubmissionRepositoryTest.java b/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSubmissionRepositoryTest.java
index 0d2599f5fd..6120fe3468 100644
--- a/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSubmissionRepositoryTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/plagiarism/urkund/UrkundSubmissionRepositoryTest.java
@@ -1,18 +1,12 @@
 package se.su.dsv.scipro.plagiarism.urkund;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.jupiter.api.Test;
 import se.su.dsv.scipro.file.FileDescription;
-import se.su.dsv.scipro.file.FileModule;
 import se.su.dsv.scipro.file.FileReference;
-import se.su.dsv.scipro.file.FileStore;
-import se.su.dsv.scipro.sukat.Sukat;
-import se.su.dsv.scipro.test.GuiceTest;
-import se.su.dsv.scipro.test.InMemoryFileStore;
+import se.su.dsv.scipro.test.SpringTest;
 
 import jakarta.inject.Inject;
 import java.time.Instant;
@@ -22,23 +16,10 @@ import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-public class UrkundSubmissionRepositoryTest extends GuiceTest {
+public class UrkundSubmissionRepositoryTest extends SpringTest {
 
     @Inject
     private UrkundSubmissionRepository submissionRepository;
-    
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(new UrkundModule());
-                bind(Sukat.class).toInstance((username) -> Optional.empty());
-                install(new FileModule());
-                bind(FileStore.class).to(InMemoryFileStore.class);
-            }
-        };
-    }
 
     @Test
     public void save() {
diff --git a/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingModuleTest.java b/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingModuleTest.java
index 8d59c17234..ba058fcce1 100644
--- a/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingModuleTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingModuleTest.java
@@ -1,18 +1,6 @@
 package se.su.dsv.scipro.reviewing;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
 import se.su.dsv.scipro.test.IntegrationTest;
 
 public abstract class ReviewingModuleTest extends IntegrationTest {
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                install(ReviewingModuleTest.super.moduleUnderTest());
-                bind(ReviewerAssignedDeadline.class).asEagerSingleton();
-            }
-        };
-    }
 }
diff --git a/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingServiceImplTest.java
index 56398d9f29..dd4ad1a076 100644
--- a/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingServiceImplTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/reviewing/ReviewingServiceImplTest.java
@@ -1,161 +1,161 @@
-package se.su.dsv.scipro.reviewing;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentMatchers;
-import se.su.dsv.scipro.file.FileUpload;
-import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.DegreeType;
-import se.su.dsv.scipro.system.Page;
-import se.su.dsv.scipro.system.ProjectType;
-import se.su.dsv.scipro.system.User;
-import se.su.dsv.scipro.util.Either;
-
-import jakarta.inject.Inject;
-import java.time.LocalDate;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-
-import static org.hamcrest.CoreMatchers.hasItem;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class ReviewingServiceImplTest extends ReviewingModuleTest {
-    private static final TestPage ALL = new TestPage(0, Long.MAX_VALUE);
-
-    @Inject
-    private ReviewingServiceImpl reviewingService;
-    @Inject
-    private FinalSeminarApprovalServiceImpl finalSeminarApprovalService;
-    @Inject
-    private RoughDraftApprovalServiceImpl roughDraftApprovalService;
-
-    private Project project;
-    private User reviewer;
-    private Project project3;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        reviewer = save(User.builder().firstName("Ronny").lastName("Reviewer").emailAddress("reviewer@dsv.su.se").build());
-        User reviewer2 = save(User.builder().firstName("Ronny").lastName("Reviewer").emailAddress("reviewer@dsv.su.se").build());
-        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
-        ProjectType bachelor = save(new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"));
-        project = save(Project.builder().title("My project").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
-        project.addReviewer(reviewer);
-        Project project2 = save(Project.builder().title("My project 2").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
-        project2.addReviewer(reviewer2);
-        project3 = save(Project.builder().title("My project 3").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
-        project3.addReviewer(reviewer);
-    }
-
-    @Test
-    public void count() {
-        finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertEquals(1, reviewingService.countDecisions(getFilter(ReviewerApproval.Step.FINAL_SEMINAR_APPROVAL)));
-    }
-
-    private MyReviewService.Filter getFilter(ReviewerApproval.Step step) {
-        MyReviewService.Filter filter = new MyReviewService.Filter();
-        filter.setUser(reviewer);
-        filter.setStep(step);
-        return filter;
-    }
-
-    @Test
-    public void find_undecided() {
-        finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertThat(reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.FINAL_SEMINAR_APPROVAL), ALL), hasItem(where(Decision::getReviewerApproval, instanceOf(FinalSeminarApproval.class))));
-    }
-
-    @Test
-    public void find_undecided_rough_draft_approvals() {
-        roughDraftApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertThat(reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), ALL), hasItem(where(Decision::getReviewerApproval, instanceOf(RoughDraftApproval.class))));
-    }
-
-    @Test
-    public void count_undecided_rough_drafts() {
-        roughDraftApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertEquals(reviewingService.countUndecidedRoughDraft(reviewer), 1);
-    }
-
-    @Test
-    public void back_and_forth() {
-        assertTrue(finalSeminarApprovalService.requiresUpload(project));
-        Either<AlreadyRequested, FinalSeminarApproval> first = finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertFalse(finalSeminarApprovalService.requiresUpload(project));
-
-        first.foreach(firstFinalSeminarApproval -> reviewingService.reject(firstFinalSeminarApproval, "Very bad", Optional.of(createFileUpload())));
-
-        assertTrue(finalSeminarApprovalService.requiresUpload(project));
-        Either<AlreadyRequested, FinalSeminarApproval> second = finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
-        assertFalse(finalSeminarApprovalService.requiresUpload(project));
-
-        second.foreach(secondFinalSeminarApproval -> reviewingService.approve(secondFinalSeminarApproval, "Very good", Optional.empty()));
-        assertFalse(finalSeminarApprovalService.requiresUpload(project));
-    }
-
-    @Test
-    public void only_fetches_my_reviews() {
-        Either<AlreadyRequested, RoughDraftApproval> myRequest =
-                roughDraftApprovalService.requestApproval(project, createFileUpload(), "my request");
-        assertTrue(myRequest.isRight());
-
-        List<Decision> requests = reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), ALL);
-        assertEquals(1, requests.size());
-    }
-
-    @Test
-    public void sort_by_requested() {
-        Either<AlreadyRequested, RoughDraftApproval> request1 =
-                roughDraftApprovalService.requestApproval(project, createFileUpload(), "request 1");
-        assertTrue(request1.isRight());
-
-        Either<AlreadyRequested, RoughDraftApproval> request2 =
-                roughDraftApprovalService.requestApproval(project3, createFileUpload(), "request 2");
-        assertTrue(request2.isRight());
-
-        var sortByRequested = new Page.Sort<>(MyReviewService.Sort.TITLE, Page.Direction.DESCENDING);
-        TestPage page = new TestPage(0, Long.MAX_VALUE, sortByRequested);
-        List<Decision> requests =
-                reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), page);
-
-        assertEquals(2, requests.size());
-        assertEquals(request2.right(), requests.get(0).getReviewerApproval());
-        assertEquals(request1.right(), requests.get(1).getReviewerApproval());
-    }
-
-    private FileUpload createFileUpload() {
-        FileUpload fileUpload = mock(FileUpload.class);
-        when(fileUpload.handleData(ArgumentMatchers.any())).thenReturn(null);
-        return fileUpload;
-    }
-
-    record TestPage(long offset, long limit, Sort<MyReviewService.Sort> sort) implements Page<MyReviewService.Sort> {
-        TestPage(long offset, long limit) {
-            this(offset, limit, new Sort<>(MyReviewService.Sort.TITLE, Direction.ASCENDING));
-        }
-    }
-
-    private static <A, B> Matcher<A> where(Function<A, B> f, Matcher<? extends B> matcher) {
-        return new TypeSafeMatcher<>() {
-            @Override
-            protected boolean matchesSafely(A a) {
-                return matcher.matches(f.apply(a));
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("an object where ")
-                        .appendDescriptionOf(matcher);
-            }
-        };
-    }
-}
+package se.su.dsv.scipro.reviewing;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatchers;
+import se.su.dsv.scipro.file.FileUpload;
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.DegreeType;
+import se.su.dsv.scipro.system.Page;
+import se.su.dsv.scipro.system.ProjectType;
+import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.util.Either;
+
+import jakarta.inject.Inject;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ReviewingServiceImplTest extends ReviewingModuleTest {
+    private static final TestPage ALL = new TestPage(0, Long.MAX_VALUE);
+
+    @Inject
+    private ReviewingServiceImpl reviewingService;
+    @Inject
+    private FinalSeminarApprovalServiceImpl finalSeminarApprovalService;
+    @Inject
+    private RoughDraftApprovalServiceImpl roughDraftApprovalService;
+
+    private Project project;
+    private User reviewer;
+    private Project project3;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        reviewer = save(User.builder().firstName("Ronny").lastName("Reviewer").emailAddress("reviewer@dsv.su.se").build());
+        User reviewer2 = save(User.builder().firstName("Ronny").lastName("Reviewer").emailAddress("reviewer@dsv.su.se").build());
+        User headSupervisor = save(User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build());
+        ProjectType bachelor = save(new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"));
+        project = save(Project.builder().title("My project").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
+        project.addReviewer(reviewer);
+        Project project2 = save(Project.builder().title("My project 2").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
+        project2.addReviewer(reviewer2);
+        project3 = save(Project.builder().title("My project 3").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(headSupervisor).build());
+        project3.addReviewer(reviewer);
+    }
+
+    @Test
+    public void count() {
+        finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertEquals(1, reviewingService.countDecisions(getFilter(ReviewerApproval.Step.FINAL_SEMINAR_APPROVAL)));
+    }
+
+    private MyReviewService.Filter getFilter(ReviewerApproval.Step step) {
+        MyReviewService.Filter filter = new MyReviewService.Filter();
+        filter.setUser(reviewer);
+        filter.setStep(step);
+        return filter;
+    }
+
+    @Test
+    public void find_undecided() {
+        finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertThat(reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.FINAL_SEMINAR_APPROVAL), ALL), hasItem(where(Decision::getReviewerApproval, instanceOf(FinalSeminarApproval.class))));
+    }
+
+    @Test
+    public void find_undecided_rough_draft_approvals() {
+        roughDraftApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertThat(reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), ALL), hasItem(where(Decision::getReviewerApproval, instanceOf(RoughDraftApproval.class))));
+    }
+
+    @Test
+    public void count_undecided_rough_drafts() {
+        roughDraftApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertEquals(reviewingService.countUndecidedRoughDraft(reviewer), 1);
+    }
+
+    @Test
+    public void back_and_forth() {
+        assertTrue(finalSeminarApprovalService.requiresUpload(project));
+        Either<AlreadyRequested, FinalSeminarApproval> first = finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertFalse(finalSeminarApprovalService.requiresUpload(project));
+
+        first.foreach(firstFinalSeminarApproval -> reviewingService.reject(firstFinalSeminarApproval, "Very bad", Optional.of(createFileUpload())));
+
+        assertTrue(finalSeminarApprovalService.requiresUpload(project));
+        Either<AlreadyRequested, FinalSeminarApproval> second = finalSeminarApprovalService.requestApproval(project, createFileUpload(), "test");
+        assertFalse(finalSeminarApprovalService.requiresUpload(project));
+
+        second.foreach(secondFinalSeminarApproval -> reviewingService.approve(secondFinalSeminarApproval, "Very good", Optional.empty()));
+        assertFalse(finalSeminarApprovalService.requiresUpload(project));
+    }
+
+    @Test
+    public void only_fetches_my_reviews() {
+        Either<AlreadyRequested, RoughDraftApproval> myRequest =
+                roughDraftApprovalService.requestApproval(project, createFileUpload(), "my request");
+        assertTrue(myRequest.isRight());
+
+        List<Decision> requests = reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), ALL);
+        assertEquals(1, requests.size());
+    }
+
+    @Test
+    public void sort_by_requested() {
+        Either<AlreadyRequested, RoughDraftApproval> request1 =
+                roughDraftApprovalService.requestApproval(project, createFileUpload(), "request 1");
+        assertTrue(request1.isRight());
+
+        Either<AlreadyRequested, RoughDraftApproval> request2 =
+                roughDraftApprovalService.requestApproval(project3, createFileUpload(), "request 2");
+        assertTrue(request2.isRight());
+
+        var sortByRequested = new Page.Sort<>(MyReviewService.Sort.TITLE, Page.Direction.DESCENDING);
+        TestPage page = new TestPage(0, Long.MAX_VALUE, sortByRequested);
+        List<Decision> requests =
+                reviewingService.findAllDecisions(getFilter(ReviewerApproval.Step.ROUGH_DRAFT_APPROVAL), page);
+
+        assertEquals(2, requests.size());
+        assertEquals(request2.right(), requests.get(0).getReviewerApproval());
+        assertEquals(request1.right(), requests.get(1).getReviewerApproval());
+    }
+
+    private FileUpload createFileUpload() {
+        FileUpload fileUpload = mock(FileUpload.class);
+        when(fileUpload.handleData(ArgumentMatchers.any())).thenReturn(null);
+        return fileUpload;
+    }
+
+    record TestPage(long offset, long limit, Sort<MyReviewService.Sort> sort) implements Page<MyReviewService.Sort> {
+        TestPage(long offset, long limit) {
+            this(offset, limit, new Sort<>(MyReviewService.Sort.TITLE, Direction.ASCENDING));
+        }
+    }
+
+    private static <A, B> Matcher<A> where(Function<A, B> f, Matcher<? extends B> matcher) {
+        return new TypeSafeMatcher<>() {
+            @Override
+            protected boolean matchesSafely(A a) {
+                return matcher.matches(f.apply(a));
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("an object where ")
+                        .appendDescriptionOf(matcher);
+            }
+        };
+    }
+}
diff --git a/core/src/test/java/se/su/dsv/scipro/test/GuiceTest.java b/core/src/test/java/se/su/dsv/scipro/test/GuiceTest.java
deleted file mode 100644
index 6d2667ef5c..0000000000
--- a/core/src/test/java/se/su/dsv/scipro/test/GuiceTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package se.su.dsv.scipro.test;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-import com.google.inject.persist.PersistService;
-import com.google.inject.persist.UnitOfWork;
-import com.google.inject.persist.jpa.JpaPersistModule;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.EntityTransaction;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-
-import jakarta.inject.Inject;
-import jakarta.inject.Provider;
-import java.time.Clock;
-
-public abstract class GuiceTest {
-    @Inject
-    private UnitOfWork unitOfWork;
-    @Inject
-    private PersistService persistService;
-    @Inject
-    private Provider<EntityManager> entityManager;
-
-    @BeforeEach
-    public final void prepareGuice() {
-        Injector injector = Guice.createInjector(
-                new JpaPersistModule("testPersistenceUnit"),
-                new AbstractModule() {
-                    @Override
-                    protected void configure() {
-                        final MutableFixedClock clock = new MutableFixedClock(Clock.systemDefaultZone());
-                        bind(Clock.class).toInstance(clock);
-                        bind(MutableFixedClock.class).toInstance(clock);
-                    }
-                },
-                moduleUnderTest());
-
-        // Have to start the PersistService and UnitOfWork manually since they are no longer
-        // automatically started as an EntityManager is injected
-        injector.getInstance(PersistService.class).start();
-        injector.getInstance(UnitOfWork.class).begin();
-
-        injector.injectMembers(this);
-
-        persistService.start();
-        EntityTransaction transaction = entityManager.get().getTransaction();
-        transaction.begin();
-        transaction.setRollbackOnly();
-    }
-
-    protected abstract Module moduleUnderTest();
-
-    @AfterEach
-    public final void shutDownPersistence() {
-        entityManager.get().getTransaction().rollback();
-        unitOfWork.end();
-        persistService.stop();
-    }
-
-    protected <T> T save(T entity) {
-        EntityManager em = entityManager.get();
-        if (em.contains(entity)) {
-            return em.merge(entity);
-        }
-        else {
-            em.persist(entity);
-            return entity;
-        }
-    }
-}
diff --git a/core/src/test/java/se/su/dsv/scipro/test/IntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/test/IntegrationTest.java
index 4833f0e4bd..837f58dc90 100644
--- a/core/src/test/java/se/su/dsv/scipro/test/IntegrationTest.java
+++ b/core/src/test/java/se/su/dsv/scipro/test/IntegrationTest.java
@@ -1,57 +1,5 @@
 package se.su.dsv.scipro.test;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.name.Names;
-import modules.CoreModule;
-import se.su.dsv.scipro.file.FileModule;
-import se.su.dsv.scipro.file.FileStore;
-import se.su.dsv.scipro.grading.GradingHistory;
-import se.su.dsv.scipro.grading.GradingHistoryEventRepository;
-import se.su.dsv.scipro.grading.GradingHistoryEventRepositoryImpl;
-import se.su.dsv.scipro.grading.GradingModule;
-import se.su.dsv.scipro.grading.ThesisSubmissionHistoryService;
-import se.su.dsv.scipro.plagiarism.urkund.UrkundModule;
-import se.su.dsv.scipro.reviewing.ReviewingModule;
-import se.su.dsv.scipro.sukat.SukatModule;
-import se.su.dsv.scipro.system.CurrentUser;
-import se.su.dsv.scipro.system.User;
+public abstract class IntegrationTest extends SpringTest {
 
-import jakarta.inject.Singleton;
-
-public abstract class IntegrationTest extends GuiceTest {
-
-    @Override
-    protected Module moduleUnderTest() {
-        return new AbstractModule() {
-            @Override
-            protected void configure() {
-                bindConstant().annotatedWith(Names.named("profile")).to("TEST");
-                install(new CoreModule());
-                install(new FileModule());
-                install(new ReviewingModule());
-                install(new SukatModule());
-                install(new UrkundModule());
-                bind(ThesisSubmissionHistoryService.class).to(GradingHistory.class);
-                bind(GradingHistoryEventRepository.class).to(GradingHistoryEventRepositoryImpl.class);
-                bind(CurrentUser.class).to(TestUser.class);
-                bind(FileStore.class).to(InMemoryFileStore.class);
-            }
-        };
-    }
-
-    @Singleton
-    private static class TestUser implements CurrentUser {
-
-        private User user;
-
-        public void set(User user) {
-            this.user = user;
-        }
-
-        @Override
-        public User get() {
-            return user;
-        }
-    }
 }
diff --git a/core/src/test/java/se/su/dsv/scipro/test/SpringTest.java b/core/src/test/java/se/su/dsv/scipro/test/SpringTest.java
new file mode 100644
index 0000000000..eae4492265
--- /dev/null
+++ b/core/src/test/java/se/su/dsv/scipro/test/SpringTest.java
@@ -0,0 +1,92 @@
+package se.su.dsv.scipro.test;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.EntityTransaction;
+import jakarta.persistence.Persistence;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import se.su.dsv.scipro.profiles.CurrentProfile;
+import se.su.dsv.scipro.sukat.Sukat;
+import se.su.dsv.scipro.system.CurrentUser;
+import se.su.dsv.scipro.CoreConfig;
+import se.su.dsv.scipro.RepositoryConfiguration;
+
+import java.time.Clock;
+import java.util.Optional;
+
+public abstract class SpringTest {
+    private EntityManager entityManager;
+    private EntityManagerFactory entityManagerFactory;
+
+    @BeforeEach
+    public final void prepareSpring() {
+        entityManagerFactory = Persistence.createEntityManagerFactory("testPersistenceUnit");
+        this.entityManager = entityManagerFactory.createEntityManager();
+        EntityTransaction transaction = entityManager.getTransaction();
+        transaction.begin();
+        transaction.setRollbackOnly();
+
+        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
+        annotationConfigApplicationContext.register(TestContext.class);
+        annotationConfigApplicationContext.getBeanFactory()
+                .registerSingleton("entityManager", this.entityManager);
+        annotationConfigApplicationContext.refresh();
+        annotationConfigApplicationContext.getAutowireCapableBeanFactory()
+                .autowireBean(this);
+    }
+
+    @AfterEach
+    public final void shutDownPersistence() {
+        entityManager.getTransaction().rollback();
+        entityManager.close();
+        entityManagerFactory.close();
+    }
+
+    protected <T> T save(T entity) {
+        EntityManager em = entityManager;
+        if (em.contains(entity)) {
+            return em.merge(entity);
+        }
+        else {
+            em.persist(entity);
+            return entity;
+        }
+    }
+
+    @Configuration(proxyBeanMethods = false)
+    @Import({CoreConfig.class, RepositoryConfiguration.class})
+    public static class TestContext {
+        @Bean
+        public InMemoryFileStore inMemoryFileStore() {
+            return new InMemoryFileStore();
+        }
+
+        @Bean
+        public MutableFixedClock clock() {
+            return new MutableFixedClock(Clock.systemDefaultZone());
+        }
+
+        @Bean
+        public Sukat sukat() {
+            return (username) -> Optional.empty();
+        }
+
+        @Bean
+        public CurrentUser currentUser() {
+            return () -> null;
+        }
+
+        @Bean
+        public CurrentProfile currentProfile() {
+            CurrentProfile currentProfile = new CurrentProfile();
+            currentProfile.setCurrentProfileString("DEV");
+            return currentProfile;
+        }
+    }
+}
diff --git a/daisy-integration/pom.xml b/daisy-integration/pom.xml
index 891a45a81e..3d94bb165c 100644
--- a/daisy-integration/pom.xml
+++ b/daisy-integration/pom.xml
@@ -17,8 +17,8 @@
             <artifactId>core</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.inject.extensions</groupId>
-            <artifactId>guice-servlet</artifactId>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
         </dependency>
         <dependency>
             <groupId>jakarta.servlet</groupId>
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
index 3d4acbe581..3f78ffd415 100755
--- a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
+++ b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ExternalImporterDaisyImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.daisyExternal.impl;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import se.su.dsv.scipro.system.PageRequest;
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java
index 0fe1dd6e51..9e6ea05554 100644
--- a/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java
+++ b/daisy-integration/src/main/java/se/su/dsv/scipro/daisyExternal/impl/ImporterTransactionsImpl.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.daisyExternal.impl;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import jakarta.ws.rs.core.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyIntegrationConfiguration.java b/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyIntegrationConfiguration.java
new file mode 100644
index 0000000000..1a50b0c056
--- /dev/null
+++ b/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyIntegrationConfiguration.java
@@ -0,0 +1,182 @@
+package se.su.dsv.scipro.integration.daisy;
+
+import com.google.common.eventbus.EventBus;
+import jakarta.inject.Provider;
+import jakarta.persistence.EntityManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import se.su.dsv.scipro.daisyExternal.ExternalImporter;
+import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
+import se.su.dsv.scipro.daisyExternal.http.DaisyAPI;
+import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
+import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
+import se.su.dsv.scipro.finalseminar.FinalSeminarService;
+import se.su.dsv.scipro.finalthesis.FinalThesisService;
+import se.su.dsv.scipro.forum.ProjectForumService;
+import se.su.dsv.scipro.grading.NationalSubjectCategoryService;
+import se.su.dsv.scipro.grading.ThesisApprovedHistoryService;
+import se.su.dsv.scipro.grading.ThesisRejectionHistoryService;
+import se.su.dsv.scipro.integration.daisy.workers.GradingCompletedMilestoneActivator;
+import se.su.dsv.scipro.integration.daisy.workers.ImportNationalCategories;
+import se.su.dsv.scipro.integration.daisy.workers.ProjectExporter;
+import se.su.dsv.scipro.integration.daisy.workers.ProjectFinalizer;
+import se.su.dsv.scipro.integration.daisy.workers.RejectedThesisWorker;
+import se.su.dsv.scipro.integration.daisy.workers.UserImportWorker;
+import se.su.dsv.scipro.io.ExternalExporter;
+import se.su.dsv.scipro.io.facade.ExporterFacade;
+import se.su.dsv.scipro.io.impl.ExternalExporterDaisyImpl;
+import se.su.dsv.scipro.match.ProgramService;
+import se.su.dsv.scipro.project.ProjectRepo;
+import se.su.dsv.scipro.project.ProjectService;
+import se.su.dsv.scipro.report.GradingReportService;
+import se.su.dsv.scipro.springdata.services.UnitService;
+import se.su.dsv.scipro.system.MergeService;
+import se.su.dsv.scipro.system.MergeServiceImpl;
+import se.su.dsv.scipro.system.ResearchAreaService;
+import se.su.dsv.scipro.system.UserNameService;
+import se.su.dsv.scipro.system.UserService;
+import se.su.dsv.scipro.war.PluginConfiguration;
+import se.su.dsv.scipro.workerthreads.Scheduler;
+
+@Configuration
+public class DaisyIntegrationConfiguration implements PluginConfiguration {
+    @Bean
+    public Daisy daisy(ExporterFacade exporterFacade, ExternalExporter externalExporter) {
+        return new Daisy(exporterFacade, externalExporter);
+    }
+
+    @Bean
+    public ExporterFacade exporterFacade(ExternalExporter externalExporter) {
+        return new ExporterFacade(externalExporter);
+    }
+
+    @Bean
+    public ExternalExporterDaisyImpl externalExporter(DaisyAPI daisyApi) {
+        return new ExternalExporterDaisyImpl(daisyApi);
+    }
+
+    @Bean
+    public DaisyWorkerInitialization daisyWorkerInitialization(
+            Scheduler scheduler,
+            Provider<ProjectExporter> projectExporter,
+            Provider<UserImportWorker> userImportWorker,
+            Provider<ProjectFinalizer> projectFinalizer,
+            Provider<RejectedThesisWorker> rejectedThesisWorkerProvider,
+            Provider<GradingCompletedMilestoneActivator> gradingFinalizer,
+            Provider<ImportNationalCategories> importNationalCategories)
+    {
+        return new DaisyWorkerInitialization(scheduler, projectExporter, userImportWorker, projectFinalizer,
+                rejectedThesisWorkerProvider, gradingFinalizer, importNationalCategories);
+    }
+
+    @Bean
+    public ProjectExporter projectExporter(
+            ExporterFacade exporterFacade,
+            ProjectRepo projectRepo,
+            DaisyAPI daisyApi,
+            ExternalExporter externalExporter,
+            FinalSeminarService finalSeminarService,
+            FinalThesisService finalThesisService)
+    {
+        return new ProjectExporter(projectRepo, exporterFacade, daisyApi, externalExporter, finalSeminarService,
+                finalThesisService);
+    }
+
+    @Bean
+    public UserImportWorker userImportWorker(
+            DaisyAPI daisyApi,
+            ImporterTransactions importerTransactions,
+            ExternalImporter externalImporter,
+            MergeService mergeService,
+            UserService userService,
+            ProgramService programService,
+            Provider<EntityManager> entityManager,
+            ResearchAreaService researchAreaService)
+    {
+        return new UserImportWorker(daisyApi, importerTransactions, externalImporter, mergeService, userService,
+                programService, entityManager, researchAreaService);
+    }
+
+    @Bean
+    public ProjectFinalizer projectFinalizer(
+            ProjectService projectService,
+            DaisyAPI daisyApi,
+            ThesisApprovedHistoryService thesisApprovedHistoryService)
+    {
+        return new ProjectFinalizer(projectService, daisyApi, thesisApprovedHistoryService);
+    }
+
+    @Bean
+    public RejectedThesisWorker rejectedThesisWorker(
+            GradingReportService gradingReportService,
+            ProjectService projectService,
+            FinalThesisService finalThesisService,
+            ProjectForumService projectForumService,
+            ThesisRejectionHistoryService thesisRejectionHistoryService,
+            DaisyAPI daisyApi)
+    {
+        return new RejectedThesisWorker(gradingReportService, projectService, finalThesisService, projectForumService,
+                thesisRejectionHistoryService, daisyApi);
+    }
+
+    @Bean
+    public GradingCompletedMilestoneActivator gradingFinalizer(
+            ProjectService projectService,
+            DaisyAPI daisyApi,
+            EventBus eventBus,
+            UserService userService)
+    {
+        return new GradingCompletedMilestoneActivator(projectService, daisyApi, eventBus, userService);
+    }
+
+    @Bean
+    public ImportNationalCategories importNationalCategories(
+            DaisyAPI daisyApi,
+            NationalSubjectCategoryService nationalSubjectCategoryService)
+    {
+        return new ImportNationalCategories(daisyApi, nationalSubjectCategoryService);
+    }
+
+    @Bean
+    public ImporterTransactionsImpl importerTransactions(
+            UserService userService,
+            ResearchAreaService researchAreaService,
+            ProjectService projectService,
+            UserNameService userNameService,
+            DaisyAPI daisyApi,
+            ProgramService programService)
+    {
+        return new ImporterTransactionsImpl(userService, researchAreaService, projectService, userNameService,
+                daisyApi, programService);
+    }
+
+    @Bean
+    public ExternalImporterDaisyImpl externalImporter(
+            DaisyAPI daisyApi,
+            ImporterTransactions importerTransactions,
+            UserService userService,
+            UnitService unitService)
+    {
+        return new ExternalImporterDaisyImpl(userService, unitService, daisyApi, importerTransactions);
+    }
+
+    @Bean
+    public MergeServiceImpl mergeService(UserService userService, UserNameService userNameService) {
+        return new MergeServiceImpl(userNameService, userService);
+    }
+
+    @Bean
+    SyncReviewerWithDaisy syncReviewerWithDaisy(DaisyAPI daisyApi, EventBus eventBus) {
+        return new SyncReviewerWithDaisy(daisyApi, eventBus);
+    }
+
+    @Bean
+    public DaisyUserSearchService daisyUserSearchService(DaisyAPI daisyApi, UserService userService) {
+        return new DaisyUserSearchService(daisyApi, userService);
+    }
+
+    @Bean
+    DaisyConsentService daisyConsentService(DaisyAPI daisyApi) {
+        return new DaisyConsentService(daisyApi);
+    }
+}
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyModule.java b/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyModule.java
deleted file mode 100644
index ffedb6c3c1..0000000000
--- a/daisy-integration/src/main/java/se/su/dsv/scipro/integration/daisy/DaisyModule.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package se.su.dsv.scipro.integration.daisy;
-
-import com.google.inject.multibindings.Multibinder;
-import com.google.inject.multibindings.OptionalBinder;
-import com.google.inject.servlet.ServletModule;
-import se.su.dsv.scipro.daisyExternal.ExternalImporter;
-import se.su.dsv.scipro.daisyExternal.ImporterTransactions;
-import se.su.dsv.scipro.daisyExternal.impl.ExternalImporterDaisyImpl;
-import se.su.dsv.scipro.daisyExternal.impl.ImporterTransactionsImpl;
-import se.su.dsv.scipro.finalthesis.PublishingConsentService;
-import se.su.dsv.scipro.integration.daisy.workers.ProjectExporter;
-import se.su.dsv.scipro.integration.daisy.workers.UserImportWorker;
-import se.su.dsv.scipro.io.ExternalExporter;
-import se.su.dsv.scipro.io.impl.ExternalExporterDaisyImpl;
-import se.su.dsv.scipro.match.IdeaCreationJudge;
-import se.su.dsv.scipro.system.UserImportService;
-import se.su.dsv.scipro.system.UserSearchProvider;
-
-public class DaisyModule extends ServletModule {
-    @Override
-    protected void configureServlets() {
-        bind(ExternalImporter.class).to(ExternalImporterDaisyImpl.class);
-        bind(ImporterTransactions.class).to(ImporterTransactionsImpl.class);
-
-        OptionalBinder.newOptionalBinder(binder(), IdeaCreationJudge.class)
-                .setBinding().to(Daisy.class);
-
-        bind(ExternalExporter.class).to(ExternalExporterDaisyImpl.class);
-        bind(UserImportWorker.class);
-        bind(ProjectExporter.class);
-        bind(DaisyWorkerInitialization.class).asEagerSingleton();
-        bind(SyncReviewerWithDaisy.class).asEagerSingleton();
-
-        Multibinder.newSetBinder(binder(), UserImportService.class)
-                .addBinding().to(ExternalImporterDaisyImpl.class);
-
-        Multibinder.newSetBinder(binder(), UserSearchProvider.class)
-                .addBinding().to(DaisyUserSearchService.class);
-
-        OptionalBinder.newOptionalBinder(binder(), PublishingConsentService.class)
-                .setBinding().to(DaisyConsentService.class);
-    }
-
-}
diff --git a/daisy-integration/src/main/java/se/su/dsv/scipro/io/facade/ExporterFacade.java b/daisy-integration/src/main/java/se/su/dsv/scipro/io/facade/ExporterFacade.java
index 4d7c11803d..75400ab642 100755
--- a/daisy-integration/src/main/java/se/su/dsv/scipro/io/facade/ExporterFacade.java
+++ b/daisy-integration/src/main/java/se/su/dsv/scipro/io/facade/ExporterFacade.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.io.facade;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.io.ExternalExporter;
 import se.su.dsv.scipro.io.dto.Role;
 import se.su.dsv.scipro.io.exceptions.ExternalExportException;
diff --git a/daisy-integration/src/main/resources/META-INF/services/com.google.inject.Module b/daisy-integration/src/main/resources/META-INF/services/com.google.inject.Module
deleted file mode 100644
index f216e38b02..0000000000
--- a/daisy-integration/src/main/resources/META-INF/services/com.google.inject.Module
+++ /dev/null
@@ -1 +0,0 @@
-se.su.dsv.scipro.integration.daisy.DaisyModule
diff --git a/daisy-integration/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration b/daisy-integration/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration
new file mode 100644
index 0000000000..39148f50a9
--- /dev/null
+++ b/daisy-integration/src/main/resources/META-INF/services/se.su.dsv.scipro.war.PluginConfiguration
@@ -0,0 +1 @@
+se.su.dsv.scipro.integration.daisy.DaisyIntegrationConfiguration
diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java
index b583b29e21..f51c23592a 100644
--- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java
+++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/GradingCompletedMilestoneActivatorTest.java
@@ -1,10 +1,6 @@
 package se.su.dsv.scipro.integration.daisy.workers;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.persist.UnitOfWork;
-import com.google.inject.util.Providers;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.EntityTransaction;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -23,6 +19,7 @@ import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.system.UserService;
 import se.su.dsv.scipro.workerthreads.WorkerData;
 import se.su.dsv.scipro.workerthreads.WorkerDataService;
+import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
 
 import java.util.*;
 
@@ -47,22 +44,16 @@ public class GradingCompletedMilestoneActivatorTest {
     @Mock
     private EventBus eventBus;
     @Mock
-    private UnitOfWork unitOfWork;
-    @Mock
-    private EntityManager entityManager;
-    @Mock
-    private EntityTransaction entityTransaction;
+    private WorkerTransactionManager workerTransactionManager;
     @Mock
     private WorkerDataService workerDataService;
 
     @BeforeEach
     public void setup() {
         gradingCompletedMilestoneActivator = new GradingCompletedMilestoneActivator(projectService, daisyAPI, eventBus, userService);
-        gradingCompletedMilestoneActivator.setUnitOfWork(unitOfWork);
-        gradingCompletedMilestoneActivator.setTxManager(Providers.of(entityManager));
+        gradingCompletedMilestoneActivator.setTxManager(workerTransactionManager);
         gradingCompletedMilestoneActivator.setWorkerDataService(workerDataService);
         when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
-        when(entityManager.getTransaction()).thenReturn(entityTransaction);
 
         project.setId(3493L);
         project.setIdentifier(234);
diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java
index f4165842bb..dfd523ad4f 100644
--- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java
+++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectExporterTest.java
@@ -1,10 +1,6 @@
 package se.su.dsv.scipro.integration.daisy.workers;
 
-import com.google.inject.persist.UnitOfWork;
-import com.google.inject.util.Providers;
 import com.querydsl.core.types.Predicate;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.EntityTransaction;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -26,6 +22,7 @@ import se.su.dsv.scipro.system.Unit;
 import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.workerthreads.WorkerData;
 import se.su.dsv.scipro.workerthreads.WorkerDataService;
+import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
 
 import java.time.LocalDate;
 import java.util.*;
@@ -45,10 +42,8 @@ public class ProjectExporterTest {
     private @Mock ExporterFacade exporterFacade;
     private @Mock DaisyAPI daisyAPI;
     private @Mock ExternalExporter externalExporter;
-    private @Mock EntityManager entityManager;
-    private @Mock EntityTransaction entityTransaction;
+    private @Mock WorkerTransactionManager workerTransactionManager;
     private @Mock WorkerDataService workerDataService;
-    private @Mock UnitOfWork unitOfWork;
     private @Mock FinalSeminarService finalSeminarService;
     private @Mock FinalThesisService finalThesisService;
 
@@ -61,11 +56,9 @@ public class ProjectExporterTest {
     @BeforeEach
     public void setUp() {
         projectExporter = new ProjectExporter(projectRepo, exporterFacade, daisyAPI, externalExporter, finalSeminarService, finalThesisService);
-        projectExporter.setTxManager(Providers.of(entityManager));
+        projectExporter.setTxManager(workerTransactionManager);
         projectExporter.setWorkerDataService(workerDataService);
-        projectExporter.setUnitOfWork(unitOfWork);
         when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
-        when(entityManager.getTransaction()).thenReturn(entityTransaction);
 
         Unit unit = new Unit();
         unit.setIdentifier(239478);
diff --git a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java
index 576409136a..048d2165e7 100644
--- a/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java
+++ b/daisy-integration/src/test/java/se/su/dsv/scipro/integration/daisy/workers/ProjectFinalizerTest.java
@@ -1,7 +1,5 @@
 package se.su.dsv.scipro.integration.daisy.workers;
 
-import com.google.inject.persist.UnitOfWork;
-import com.google.inject.util.Providers;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -18,8 +16,8 @@ import se.su.dsv.scipro.project.QProject;
 import se.su.dsv.scipro.workerthreads.WorkerData;
 import se.su.dsv.scipro.workerthreads.WorkerDataService;
 
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.EntityTransaction;
+import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
+
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
@@ -38,9 +36,7 @@ public class ProjectFinalizerTest {
 
     private @Mock DaisyAPI daisyAPI;
     private @Mock ProjectService projectService;
-    private @Mock UnitOfWork unitOfWork;
-    private @Mock EntityManager entityManager;
-    private @Mock EntityTransaction entityTransaction;
+    private @Mock WorkerTransactionManager workerTransactionManager;
     private @Mock WorkerDataService workerDataService;
     private @Mock ThesisApprovedHistoryService thesisApprovedHistoryService;
 
@@ -48,11 +44,9 @@ public class ProjectFinalizerTest {
     @BeforeEach
     public void setup() {
         projectFinalizer = new ProjectFinalizer(projectService, daisyAPI, thesisApprovedHistoryService);
-        projectFinalizer.setUnitOfWork(unitOfWork);
-        projectFinalizer.setTxManager(Providers.of(entityManager));
+        projectFinalizer.setTxManager(workerTransactionManager);
         projectFinalizer.setWorkerDataService(workerDataService);
         when(workerDataService.save(any(WorkerData.class))).thenReturn(new WorkerData());
-        when(entityManager.getTransaction()).thenReturn(entityTransaction);
 
         project.setId(3493L);
         project.setIdentifier(234);
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000..637455a39e
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,15 @@
+services:
+  oauth2:
+    container_name: scipro-dev-oauth2
+    build:
+      context: https://github.com/dsv-su/toker.git
+      dockerfile: embedded.Dockerfile
+    restart: on-failure
+    ports:
+      - '59733:8080'
+    environment:
+      - CLIENT_ID=get-token
+      - CLIENT_SECRET=get-token-secret
+      - CLIENT_REDIRECT_URI=http://localhost:59732/
+      - RESOURCE_SERVER_ID=scipro-api-client
+      - RESOURCE_SERVER_SECRET=scipro-api-secret
diff --git a/pom.xml b/pom.xml
index d222069a72..c96cb4a122 100755
--- a/pom.xml
+++ b/pom.xml
@@ -13,6 +13,8 @@
         <module>core</module>
         <module>view</module>
         <module>daisy-integration</module>
+        <module>war</module>
+        <module>api</module>
     </modules>
 
     <properties>
@@ -36,7 +38,6 @@
         <mockito.version>5.3.1</mockito.version>
         <flyway.version>9.19.1</flyway.version>
         <jersey.version>3.1.6</jersey.version>
-        <guice.version>7.0.0</guice.version>
         <poi.version>5.2.5</poi.version>
         <jackson.version>2.17.0</jackson.version>
 
@@ -88,9 +89,22 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.wicket</groupId>
-                <artifactId>wicket-guice</artifactId>
+                <artifactId>wicket-ioc</artifactId>
                 <version>${wicket.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.wicket</groupId>
+                <artifactId>wicket-spring</artifactId>
+                <version>${wicket.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>3.2.5</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
 
             <!--  Servlet API, needed for compilation. -->
             <dependency>
@@ -189,13 +203,6 @@
                 <artifactId>guava</artifactId>
                 <version>32.0.1-jre</version>
             </dependency>
-            <dependency>
-                <groupId>com.google.inject</groupId>
-                <artifactId>guice-bom</artifactId>
-                <version>${guice.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
 
             <dependency>
                 <groupId>jakarta.mail</groupId>
@@ -260,6 +267,11 @@
                 <artifactId>flyway-mysql</artifactId>
                 <version>${flyway.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+                <version>2.5.0</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/view/pom.xml b/view/pom.xml
index c359d76206..9cdd26f28b 100644
--- a/view/pom.xml
+++ b/view/pom.xml
@@ -13,15 +13,6 @@
     <packaging>war</packaging>
 
     <dependencies>
-        <dependency>
-            <groupId>com.google.inject.extensions</groupId>
-            <artifactId>guice-servlet</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.inject.extensions</groupId>
-            <artifactId>guice-persist</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>se.su.dsv.scipro</groupId>
             <artifactId>core</artifactId>
@@ -44,7 +35,7 @@
         </dependency>
         <dependency>
             <groupId>org.apache.wicket</groupId>
-            <artifactId>wicket-guice</artifactId>
+            <artifactId>wicket-ioc</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.wicket</groupId>
@@ -55,6 +46,11 @@
             <artifactId>wicket-tester</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.wicket</groupId>
+            <artifactId>wicket-spring</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.wicketstuff</groupId>
             <artifactId>wicketstuff-jasperreports</artifactId>
@@ -144,6 +140,15 @@
     </dependencies>
 
     <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <configuration>
+                    <attachClasses>true</attachClasses>
+                </configuration>
+            </plugin>
+        </plugins>
         <resources>
             <resource>
                 <filtering>true</filtering>
diff --git a/view/src/main/java/ApplicationBootstrap.java b/view/src/main/java/ApplicationBootstrap.java
deleted file mode 100644
index d5631e6f1c..0000000000
--- a/view/src/main/java/ApplicationBootstrap.java
+++ /dev/null
@@ -1,68 +0,0 @@
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-import com.google.inject.name.Names;
-import com.google.inject.persist.PersistFilter;
-import com.google.inject.servlet.GuiceServletContextListener;
-import com.google.inject.servlet.ServletModule;
-import org.apache.wicket.guice.GuiceWebApplicationFactory;
-import org.apache.wicket.protocol.http.WicketFilter;
-
-import jakarta.servlet.ServletContext;
-import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.time.Clock;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.ServiceLoader;
-
-public class ApplicationBootstrap extends GuiceServletContextListener {
-    @Override
-    protected Injector getInjector() {
-        // preserve cookies when using Jersey which will use the logged in session cookie from
-        // Daisy API thus drastically speeding up the batch jobs
-        CookieHandler.setDefault(new CookieManager());
-        return Guice.createInjector(
-                new ServletModule() {
-                    @Override
-                    protected void configureServlets() {
-                        final ServletContext servletContext = getServletContext();
-                        final Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
-                        while (initParameterNames.hasMoreElements()) {
-                            final String parameterName = initParameterNames.nextElement();
-                            final String value = servletContext.getInitParameter(parameterName);
-                            bindConstant().annotatedWith(Names.named(parameterName)).to(value);
-                        }
-                    }
-                },
-                new ServletModule() {
-                    @Override
-                    protected void configureServlets() {
-                        filter("/*").through(PersistFilter.class);
-                    }
-                },
-                new AbstractModule() {
-                    @Override
-                    protected void configure() {
-                        bind(Clock.class).toInstance(Clock.systemDefaultZone());
-                        ServiceLoader<Module> modules = ServiceLoader.load(Module.class);
-                        for (Module module : modules) {
-                            install(module);
-                        }
-                    }
-                },
-                new ServletModule() {
-                    @Override
-                    protected void configureServlets() {
-                        Map<String,String> params = new HashMap<>();
-                        params.put(WicketFilter.FILTER_MAPPING_PARAM, "/*");
-                        params.put(WicketFilter.APP_FACT_PARAM, GuiceWebApplicationFactory.class.getName());
-                        params.put("injectorContextAttribute", Injector.class.getName());
-                        filter("/*").through(new WicketFilter(), params);
-                    }
-                }
-        );
-    }
-}
diff --git a/view/src/main/java/DatabaseMigration.java b/view/src/main/java/DatabaseMigration.java
deleted file mode 100644
index 442e3003e4..0000000000
--- a/view/src/main/java/DatabaseMigration.java
+++ /dev/null
@@ -1,35 +0,0 @@
-import org.flywaydb.core.Flyway;
-
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import jakarta.servlet.ServletContextEvent;
-import jakarta.servlet.ServletContextListener;
-import javax.sql.DataSource;
-
-public class DatabaseMigration implements ServletContextListener {
-    @Override
-    public void contextInitialized(ServletContextEvent sce) {
-        migrateDatabase();
-    }
-
-    private void migrateDatabase() {
-        Flyway flyway = Flyway.configure()
-                .dataSource(getDataSource())
-                .baselineOnMigrate(true)
-                .table("schema_version")
-                .load();
-        flyway.migrate();
-    }
-
-    private DataSource getDataSource() {
-        try {
-            return InitialContext.doLookup("java:comp/env/jdbc/sciproDS");
-        } catch (NamingException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public void contextDestroyed(ServletContextEvent sce) {
-    }
-}
diff --git a/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java b/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java
index 7e80019776..3d0eafc255 100644
--- a/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java
+++ b/view/src/main/java/se/su/dsv/scipro/FileSystemStore.java
@@ -12,7 +12,7 @@ import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardOpenOption;
 
-class FileSystemStore implements FileStore {
+public class FileSystemStore implements FileStore {
     private static final int FILES_PER_SUBDIRECTORY = 1000;
     private static final String FILE_ROOT = "/scipro-files";
 
diff --git a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
index ebbc21afa8..521595f7c3 100755
--- a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
+++ b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java
@@ -53,7 +53,6 @@ import se.su.dsv.scipro.milestones.pages.SupervisorMileStonePage;
 import se.su.dsv.scipro.nonworkdays.NonWorkDaysPage;
 import se.su.dsv.scipro.notes.NotesPage;
 import se.su.dsv.scipro.notifications.NotificationLoader;
-import se.su.dsv.scipro.notifications.NotificationModule;
 import se.su.dsv.scipro.notifications.pages.NotificationLandingPage;
 import se.su.dsv.scipro.notifications.pages.NotificationsPage;
 import se.su.dsv.scipro.notifications.pages.SupervisorNotificationSettingsPage;
@@ -347,7 +346,7 @@ public class SciProApplication extends LifecycleManagedWebApplication {
 
     private void mountNotificationAndSettingsPages() {
         mountPage("notes", NotesPage.class);
-        mountPage(NotificationModule.NOTIFICATION_RELATIVE_PAGE_URL, NotificationLandingPage.class);
+        mountPage("notification", NotificationLandingPage.class);
         mountPage("notifications", NotificationsPage.class);
         mountPage("settings", BasicProfilePage.class);
         mountPage("settings/supervisorprofile", SupervisorProfilePage.class);
diff --git a/view/src/main/java/se/su/dsv/scipro/SciProModule.java b/view/src/main/java/se/su/dsv/scipro/SciProModule.java
deleted file mode 100644
index 26e7f0a6ba..0000000000
--- a/view/src/main/java/se/su/dsv/scipro/SciProModule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package se.su.dsv.scipro;
-
-import com.google.inject.Singleton;
-import com.google.inject.multibindings.Multibinder;
-import com.google.inject.persist.jpa.JpaPersistModule;
-import com.google.inject.servlet.ServletModule;
-import com.google.inject.servlet.SessionScoped;
-import modules.CoreModule;
-import org.apache.wicket.protocol.http.WebApplication;
-import se.su.dsv.scipro.file.FileModule;
-import se.su.dsv.scipro.file.FileStore;
-import se.su.dsv.scipro.security.auth.AuthenticationModule;
-import se.su.dsv.scipro.system.CurrentUser;
-import se.su.dsv.scipro.system.Lifecycle;
-import se.su.dsv.scipro.workerthreads.WorkerModule;
-
-public class SciProModule extends ServletModule {
-    @Override
-    protected void configureServlets() {
-        install(new JpaPersistModule("defaultPersistenceUnit"));
-        install(new CoreModule());
-        install(new FileModule());
-        bind(FileStore.class).to(FileSystemStore.class);
-        install(new WorkerModule());
-        install(new AuthenticationModule());
-
-        bind(WebApplication.class).to(SciProApplication.class).in(Singleton.class);
-
-        bind(CurrentUser.class).to(CurrentUserFromWicketSession.class).in(SessionScoped.class);
-        Multibinder<Lifecycle> lifecycles = Multibinder.newSetBinder(binder(), Lifecycle.class);
-        lifecycles.addBinding().to(DataInitializer.class);
-    }
-}
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminCreateProjectConfirmationPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminCreateProjectConfirmationPage.java
index 5b1e8d22fe..0b3b45f8ae 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminCreateProjectConfirmationPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminCreateProjectConfirmationPage.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.admin.pages;
 
-import com.google.inject.Inject;
+import jakarta.inject.Inject;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.link.Link;
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/panels/AdminEditFooterAddressPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/panels/AdminEditFooterAddressPanel.java
index 48e268a040..eb36d66e9e 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/panels/AdminEditFooterAddressPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/panels/AdminEditFooterAddressPanel.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.admin.panels;
 
-import com.google.inject.persist.Transactional;
+import jakarta.transaction.Transactional;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.RequiredTextField;
 import org.apache.wicket.markup.html.panel.Panel;
diff --git a/view/src/main/java/se/su/dsv/scipro/crosscutting/CrosscuttingModule.java b/view/src/main/java/se/su/dsv/scipro/crosscutting/CrosscuttingModule.java
deleted file mode 100644
index fc50e7ff37..0000000000
--- a/view/src/main/java/se/su/dsv/scipro/crosscutting/CrosscuttingModule.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package se.su.dsv.scipro.crosscutting;
-
-import com.google.common.eventbus.EventBus;
-import com.google.inject.AbstractModule;
-import se.su.dsv.scipro.reviewing.ReviewerAssignedDeadline;
-
-public class CrosscuttingModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        requireBinding(EventBus.class);
-        bind(ReviewingNotifications.class).asEagerSingleton();
-        bind(ReviewerSupportMailer.class).asEagerSingleton();
-        bind(ReviewerAssignedNotifications.class).asEagerSingleton();
-        bind(ReviewerAssignedDeadline.class).asEagerSingleton();
-        bind(ForwardPhase2Feedback.class).asEagerSingleton();
-    }
-}
diff --git a/view/src/main/java/se/su/dsv/scipro/match/SupervisorIdeaDetailsPage.java b/view/src/main/java/se/su/dsv/scipro/match/SupervisorIdeaDetailsPage.java
index da362486f4..51f921ae60 100644
--- a/view/src/main/java/se/su/dsv/scipro/match/SupervisorIdeaDetailsPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/match/SupervisorIdeaDetailsPage.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.match;
 
-import com.google.inject.Inject;
+import jakarta.inject.Inject;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.string.StringValue;
 import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightSupervisorMyIdeas;
diff --git a/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewFilterDetailsPanel.java b/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewFilterDetailsPanel.java
index 5cca6a2310..144dd24f30 100644
--- a/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewFilterDetailsPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewFilterDetailsPanel.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import com.google.inject.Inject;
+import jakarta.inject.Inject;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.Model;
diff --git a/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewStatusStatisticsPanel.java b/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewStatusStatisticsPanel.java
index 18381874c6..2193555a56 100644
--- a/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewStatusStatisticsPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/peer/PeerReviewStatusStatisticsPanel.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.peer;
 
-import com.google.inject.Inject;
+import jakarta.inject.Inject;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorApprovalPanel.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorApprovalPanel.java
index 3a39a1088c..32920d25d7 100644
--- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorApprovalPanel.java
+++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorApprovalPanel.java
@@ -1,6 +1,6 @@
 package se.su.dsv.scipro.supervisor.panels;
 
-import com.google.inject.Inject;
+import jakarta.inject.Inject;
 import org.apache.wicket.feedback.FencedFeedbackPanel;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnEventHeaderItem;
diff --git a/view/src/main/resources/META-INF/services/com.google.inject.Module b/view/src/main/resources/META-INF/services/com.google.inject.Module
deleted file mode 100644
index c0c493be41..0000000000
--- a/view/src/main/resources/META-INF/services/com.google.inject.Module
+++ /dev/null
@@ -1,15 +0,0 @@
-se.su.dsv.scipro.SciProModule
-se.su.dsv.scipro.reviewing.ReviewingModule
-se.su.dsv.scipro.crosscutting.CrosscuttingModule
-se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarModule
-se.su.dsv.scipro.integration.activityforum.ActivityForumModule
-se.su.dsv.scipro.peer.PeerModule
-se.su.dsv.scipro.firstmeeting.FirstMeetingModule
-se.su.dsv.scipro.forum.notifications.ForumNotificationsModule
-se.su.dsv.scipro.daisyExternal.DaisyExternalModule
-se.su.dsv.scipro.plagiarism.urkund.UrkundModule
-se.su.dsv.scipro.sukat.SukatModule
-se.su.dsv.scipro.oauth.OAuthModule
-se.su.dsv.scipro.grading.GradingModule
-se.su.dsv.scipro.gdpr.GDPRModule
-se.su.dsv.scipro.survey.SurveyModule
diff --git a/view/src/main/webapp/WEB-INF/web.xml b/view/src/main/webapp/WEB-INF/web.xml
deleted file mode 100755
index bf2a938984..0000000000
--- a/view/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
-         version="4.0" metadata-complete="true">
-
-    <display-name>SciPro</display-name>
-
-    <filter>
-        <filter-name>guiceFilter</filter-name>
-        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
-    </filter>
-    <filter-mapping>
-        <filter-name>guiceFilter</filter-name>
-        <url-pattern>/*</url-pattern>
-    </filter-mapping>
-
-    <listener>
-        <listener-class>ApplicationBootstrap</listener-class>
-    </listener>
-
-    <listener>
-        <listener-class>DatabaseMigration</listener-class>
-    </listener>
-
-    <session-config>
-        <session-timeout>480</session-timeout>
-    </session-config>
-</web-app>
diff --git a/view/src/test/java/se/su/dsv/scipro/SciProTest.java b/view/src/test/java/se/su/dsv/scipro/SciProTest.java
index ee39fc8b41..e712c36790 100755
--- a/view/src/test/java/se/su/dsv/scipro/SciProTest.java
+++ b/view/src/test/java/se/su/dsv/scipro/SciProTest.java
@@ -1,17 +1,13 @@
 package se.su.dsv.scipro;
 
 import com.google.common.eventbus.EventBus;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.multibindings.Multibinder;
-import com.google.inject.name.Names;
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
 import org.apache.wicket.Session;
-import org.apache.wicket.guice.GuiceComponentInjector;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
+import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
+import org.apache.wicket.spring.test.ApplicationContextMock;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -364,9 +360,12 @@ public abstract class SciProTest {
     @BeforeEach
     public void setUpApplication() {
         final Class<? extends SciProTest> clazz = getClass();
-        final Injector injector = Guice.createInjector(new MockModule(clazz));
+        ApplicationContextMock applicationContext = new MockModule(clazz);
 
-        SciProApplication application = new SciProApplication(injector.getInstance(CurrentProfile.class)) {
+        CurrentProfile currentProfile = new CurrentProfile();
+        currentProfile.setCurrentProfileString("TEST");
+
+        SciProApplication application = new SciProApplication(currentProfile) {
 
             @Override
             public Session newSession(Request request, Response response) {
@@ -374,7 +373,7 @@ public abstract class SciProTest {
             }
         };
         application.getComponentInstantiationListeners().add(
-                new GuiceComponentInjector(application, injector));
+                new SpringComponentInjector(application, applicationContext));
         tester = new WicketTester(application, System.getProperty("user.dir") + "/src/main/webapp");
 
         setLoggedIn(true);
@@ -467,27 +466,23 @@ public abstract class SciProTest {
         return sb.toString();
     }
 
-    private class MockModule extends AbstractModule {
+    private class MockModule extends ApplicationContextMock {
         private final Class<? extends SciProTest> clazz;
 
         public MockModule(Class<? extends SciProTest> clazz) {
             this.clazz = clazz;
+            configure();
         }
 
         @SuppressWarnings("unchecked")
-        @Override
-        protected void configure() {
-            Multibinder.newSetBinder(binder(), Lifecycle.class);
-            Multibinder.newSetBinder(binder(), UserImportService.class)
-                    .addBinding().toInstance(userImportService);
-            bindConstant().annotatedWith(Names.named("profile")).to("TEST");
-            bind(Clock.class).toInstance(Clock.systemDefaultZone());
+        private void configure() {
+            putBean(Clock.systemDefaultZone());
             for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
                 for (Field f : c.getDeclaredFields()) {
                     if (f.getAnnotation(Mock.class) != null && f.trySetAccessible()) {
                         try {
                             Class<Object> type = (Class<Object>) f.getType();
-                            bind(type).toInstance(f.get(SciProTest.this));
+                            putBean(f.getName(), f.get(SciProTest.this));
                         } catch (IllegalAccessException e) {
                             throw new RuntimeException(e);
                         }
diff --git a/war/pom.xml b/war/pom.xml
new file mode 100644
index 0000000000..2c4ad17242
--- /dev/null
+++ b/war/pom.xml
@@ -0,0 +1,109 @@
+<?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"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>se.su.dsv.scipro</groupId>
+        <artifactId>SciPro</artifactId>
+        <version>0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>war</artifactId>
+    <packaging>war</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <!-- exclude default logging implementation (logback) since we want log4j2 -->
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-tomcat</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-orm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate.orm</groupId>
+            <artifactId>hibernate-core</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.wicket</groupId>
+            <artifactId>wicket-spring</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>se.su.dsv.scipro</groupId>
+            <artifactId>view</artifactId>
+            <version>${project.version}</version>
+            <type>war</type>
+        </dependency>
+        <dependency>
+            <!-- needed only at compile time -->
+            <!-- the classes will be moved as part of the overlay by the war plugin -->
+            <groupId>se.su.dsv.scipro</groupId>
+            <artifactId>view</artifactId>
+            <version>${project.version}</version>
+            <classifier>classes</classifier>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>se.su.dsv.scipro</groupId>
+            <artifactId>api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-war-plugin</artifactId>
+                <version>3.4.0</version>
+                <configuration>
+                    <overlays>
+                        <overlay>
+                            <groupId>se.su.dsv.scipro</groupId>
+                            <artifactId>view</artifactId>
+                            <type>war</type>
+                        </overlay>
+                    </overlays>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java b/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java
new file mode 100644
index 0000000000..f8b410b8aa
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/ApiConfig.java
@@ -0,0 +1,72 @@
+package se.su.dsv.scipro.war;
+
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
+
+import java.util.List;
+
+@Configuration(proxyBeanMethods = false)
+@ComponentScan("se.su.dsv.scipro.api")
+public class ApiConfig {
+    @Bean
+    @Order(1)
+    public SecurityFilterChain documentationSecurity(
+            HttpSecurity http,
+            HandlerMappingIntrospector introspector,
+            WebMvcProperties webMvcProperties)
+            throws Exception
+    {
+        MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspector)
+                .servletPath(webMvcProperties.getServlet().getPath());
+        return http
+                .securityMatchers(matchers -> matchers
+                        .requestMatchers(
+                                mvc.pattern("/v3/api-docs/**"),
+                                mvc.pattern("/swagger"),
+                                mvc.pattern("/swagger-ui/**")))
+                .authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll())
+                .build();
+    }
+
+    @Bean
+    @Order(2)
+    public SecurityFilterChain apiSecurity(
+            HttpSecurity http,
+            HandlerMappingIntrospector introspector,
+            WebMvcProperties webMvcProperties)
+            throws Exception
+    {
+        MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspector)
+                .servletPath(webMvcProperties.getServlet().getPath());
+        return http
+                .securityMatcher(mvc.pattern("/**"))
+                .authorizeHttpRequests(authorize -> authorize
+                        .anyRequest().authenticated())
+                .oauth2ResourceServer(oauth2 -> oauth2.opaqueToken(Customizer.withDefaults()))
+                .build();
+    }
+
+    @Bean
+    public OpenAPI customOpenAPI() {
+        return new OpenAPI()
+                .security(List.of(new SecurityRequirement().addList("access-token")))
+                .components(new Components()
+                        .addSecuritySchemes("access-token", new SecurityScheme()
+                                .type(SecurityScheme.Type.HTTP)
+                                .in(SecurityScheme.In.HEADER)
+                                .scheme("bearer")
+                                .bearerFormat("opaque")));
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/mail/MailModule.java b/war/src/main/java/se/su/dsv/scipro/war/MailConfig.java
similarity index 52%
rename from core/src/main/java/se/su/dsv/scipro/mail/MailModule.java
rename to war/src/main/java/se/su/dsv/scipro/war/MailConfig.java
index c23bc66811..a96c89f56c 100644
--- a/core/src/main/java/se/su/dsv/scipro/mail/MailModule.java
+++ b/war/src/main/java/se/su/dsv/scipro/war/MailConfig.java
@@ -1,38 +1,40 @@
-package se.su.dsv.scipro.mail;
-
-import com.google.inject.Exposed;
-import com.google.inject.PrivateModule;
-import com.google.inject.Provides;
-import se.su.dsv.scipro.file.FileService;
-import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
-import se.su.dsv.scipro.profiles.CurrentProfile;
+package se.su.dsv.scipro.war;
 
 import jakarta.mail.Session;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import se.su.dsv.scipro.data.facade.MailFacade;
+import se.su.dsv.scipro.file.FileService;
+import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
+import se.su.dsv.scipro.mail.Mail;
+import se.su.dsv.scipro.mail.Mailer;
+import se.su.dsv.scipro.mail.PrintingMailer;
+import se.su.dsv.scipro.mail.RedirectingMailer;
+import se.su.dsv.scipro.profiles.CurrentProfile;
+
 import java.util.Properties;
 
-public class MailModule extends PrivateModule {
-
-    private static final String TEST_MAIL_SINK = "scipro-mailtest@dsv.su.se";
-
-    @Override
-    protected void configure() {
-        requireBinding(CurrentProfile.class);
-        requireBinding(GeneralSystemSettingsService.class);
-        bind(MailEventService.class).to(MailEventServiceImpl.class);
-        expose(MailEventService.class);
+@Configuration(proxyBeanMethods = false)
+public class MailConfig {
+    @Bean
+    public MailFacade mailFacade() {
+        return new MailFacade();
     }
 
-    @Provides
-    @Exposed
-    public Mailer mailer(CurrentProfile currentProfile, Session session, FileService fileDescriptionService) {
+    @Bean
+    public Mailer mailer(
+            CurrentProfile currentProfile,
+            Session session,
+            FileService fileDescriptionService)
+    {
         return switch (currentProfile.getCurrentProfile()) {
             case DEV -> new PrintingMailer();
             case PROD -> new Mail(session, fileDescriptionService);
-            case TEST -> new RedirectingMailer(session, TEST_MAIL_SINK, fileDescriptionService);
+            case TEST -> new RedirectingMailer(session, "scipro-mailtest@dsv.su.se", fileDescriptionService);
         };
     }
 
-    @Provides
+    @Bean
     public Session session(GeneralSystemSettingsService generalSystemSettings) {
         String smtpHost = generalSystemSettings.getGeneralSystemSettingsInstance().getSmtpServer();
         Properties properties = new Properties();
diff --git a/war/src/main/java/se/su/dsv/scipro/war/Main.java b/war/src/main/java/se/su/dsv/scipro/war/Main.java
new file mode 100644
index 0000000000..026130dd7d
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/Main.java
@@ -0,0 +1,168 @@
+package se.su.dsv.scipro.war;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
+import jakarta.servlet.ServletContainerInitializer;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+import org.springframework.orm.jpa.SharedEntityManagerCreator;
+import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
+import se.su.dsv.scipro.CoreConfig;
+import se.su.dsv.scipro.CurrentUserFromWicketSession;
+import se.su.dsv.scipro.FileSystemStore;
+import se.su.dsv.scipro.RepositoryConfiguration;
+import se.su.dsv.scipro.file.FileStore;
+import se.su.dsv.scipro.finalthesis.PublishingConsentService;
+import se.su.dsv.scipro.finalthesis.PublishingConsentUnavailable;
+import se.su.dsv.scipro.profiles.CurrentProfile;
+import se.su.dsv.scipro.system.AggregateUserSearch;
+import se.su.dsv.scipro.system.Lifecycle;
+import se.su.dsv.scipro.system.ResearchArea;
+import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.system.UserImportService;
+import se.su.dsv.scipro.system.UserSearchProvider;
+import se.su.dsv.scipro.system.UserSearchService;
+import se.su.dsv.scipro.system.UserService;
+
+import java.time.Clock;
+import java.util.List;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+@SpringBootApplication(proxyBeanMethods = false)
+@EntityScan("se.su.dsv.scipro")
+@Import({
+        CoreConfig.class,
+        ApiConfig.class,
+        WorkerConfig.class,
+        MailConfig.class,
+        RepositoryConfiguration.class,
+        WicketConfiguration.class
+})
+public class Main extends SpringBootServletInitializer implements ServletContainerInitializer {
+    @Override
+    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
+        onStartup(ctx);
+    }
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+        for (PluginConfiguration pluginConfiguration : ServiceLoader.load(PluginConfiguration.class)) {
+            builder.sources(pluginConfiguration.getClass());
+        }
+        return builder.sources(Main.class);
+    }
+
+    @Bean
+    public EntityManagerFactoryBuilderCustomizer entityManagerFactoryCustomizer() {
+        return factoryBuilder -> {
+            factoryBuilder.setBootstrapExecutor(new SimpleAsyncTaskExecutor());
+        };
+    }
+
+    @Bean
+    public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
+        return new OpenEntityManagerInViewFilter();
+    }
+
+    @Bean
+    public CurrentProfile currentProfile(@Value("${profile}" ) String profile) {
+        CurrentProfile currentProfile = new CurrentProfile();
+        currentProfile.setCurrentProfileString(profile);
+        return currentProfile;
+    }
+
+    @Bean
+    public CurrentUserFromWicketSession currentUserFromWicketSession() {
+        return new CurrentUserFromWicketSession();
+    }
+
+    @Bean
+    public FileStore fileStore() {
+        return new FileSystemStore();
+    }
+
+    @Bean
+    public Clock clock() {
+        return Clock.systemDefaultZone();
+    }
+
+    /**
+     * Allow injecting of {@link EntityManager} directly instead of {@link EntityManagerFactory}
+     */
+    @Bean
+    public EntityManager entityManager(EntityManagerFactory emf) {
+        return SharedEntityManagerCreator.createSharedEntityManager(emf);
+    }
+
+    /**
+     * Exists because Spring refuses to inject a collection of beans if there are no beans of that type.
+     */
+    @Bean
+    public UserImportService dummyUserImportService() {
+        return new UserImportService() {
+            @Override
+            public Optional<User> importUser(String userName) {
+                return Optional.empty();
+            }
+
+            @Override
+            public List<ImportableUser> search(String searchTerm) {
+                return List.of();
+            }
+
+            @Override
+            public void importUser(ImportableUser importableUser) {
+                // do nothing
+            }
+
+            @Override
+            public Set<ResearchArea> importResearchAreasForSupervisor(User supervisor) {
+                return Set.of();
+            }
+        };
+    }
+
+    /**
+     * Exists because Spring refuses to inject a collection of beans if there are no beans of that type.
+     */
+    @Bean
+    public Lifecycle dummyLifecycle() {
+        return new Lifecycle() {
+            @Override
+            public void start() {
+                // do nothing
+            }
+
+            @Override
+            public void stop() {
+                // do nothing
+            }
+        };
+    }
+
+    @Bean
+    public UserSearchService aggregateSearchService(
+            Set<UserSearchProvider> userSearchProviders,
+            UserService userService)
+    {
+        return new AggregateUserSearch(userSearchProviders, userService);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(PublishingConsentService.class)
+    public PublishingConsentUnavailable publishingConsentService() {
+        return new PublishingConsentUnavailable();
+    }
+}
diff --git a/war/src/main/java/se/su/dsv/scipro/war/SpringManagedWorkerTransactions.java b/war/src/main/java/se/su/dsv/scipro/war/SpringManagedWorkerTransactions.java
new file mode 100644
index 0000000000..ae66a50e36
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/SpringManagedWorkerTransactions.java
@@ -0,0 +1,50 @@
+package se.su.dsv.scipro.war;
+
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
+
+public class SpringManagedWorkerTransactions implements WorkerTransactionManager {
+    private final PlatformTransactionManager platformTransactionManager;
+
+    private TransactionStatus activeTransaction;
+
+    public SpringManagedWorkerTransactions(PlatformTransactionManager platformTransactionManager) {
+        this.platformTransactionManager = platformTransactionManager;
+    }
+
+    @Override
+    public void rollbackIfActive() {
+        if (this.activeTransaction != null) {
+            platformTransactionManager.rollback(this.activeTransaction);
+            this.activeTransaction = null;
+        }
+    }
+
+    @Override
+    public void begin() {
+        if (this.activeTransaction != null) {
+            throw new IllegalStateException("A transaction is already active");
+        }
+        this.activeTransaction = platformTransactionManager.getTransaction(TransactionDefinition.withDefaults());
+    }
+
+    @Override
+    public void commit() {
+        if (this.activeTransaction == null) {
+            throw new IllegalStateException("A transaction is not active");
+        }
+        platformTransactionManager.commit(this.activeTransaction);
+        this.activeTransaction = null;
+    }
+
+    @Override
+    public void rollback() {
+        if (this.activeTransaction == null) {
+            throw new IllegalStateException("A transaction is not active");
+        }
+        platformTransactionManager.rollback(this.activeTransaction);
+        this.activeTransaction = null;
+    }
+}
diff --git a/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java
new file mode 100644
index 0000000000..f31d390c16
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/WicketConfiguration.java
@@ -0,0 +1,90 @@
+package se.su.dsv.scipro.war;
+
+import com.google.common.eventbus.EventBus;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.protocol.http.WicketFilter;
+import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import se.su.dsv.scipro.SciProApplication;
+import se.su.dsv.scipro.crosscutting.ForwardPhase2Feedback;
+import se.su.dsv.scipro.crosscutting.ReviewerAssignedNotifications;
+import se.su.dsv.scipro.crosscutting.ReviewerSupportMailer;
+import se.su.dsv.scipro.crosscutting.ReviewingNotifications;
+import se.su.dsv.scipro.forum.ProjectForumService;
+import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
+import se.su.dsv.scipro.mail.MailEventService;
+import se.su.dsv.scipro.notifications.NotificationController;
+import se.su.dsv.scipro.profiles.CurrentProfile;
+import se.su.dsv.scipro.reviewing.FinalSeminarApprovalService;
+import se.su.dsv.scipro.reviewing.RoughDraftApprovalService;
+
+@Configuration
+public class WicketConfiguration {
+    @Bean
+    public FilterRegistrationBean<WicketFilter> wicket(
+            WebApplication webApplication,
+            ApplicationContext applicationContext)
+    {
+        webApplication.getComponentInstantiationListeners()
+                .add(new SpringComponentInjector(webApplication, applicationContext));
+
+        WicketFilter filter = new WicketFilter(webApplication);
+        filter.setFilterPath("");
+
+        FilterRegistrationBean<WicketFilter> registration = new FilterRegistrationBean<>();
+        registration.setFilter(filter);
+        registration.addUrlPatterns("/*");
+        registration.setMatchAfter(true);
+        return registration;
+    }
+
+    @Bean
+    public WebApplication webApplication(CurrentProfile currentProfile) {
+        return new SciProApplication(currentProfile);
+    }
+
+    @Bean
+    public ReviewingNotifications reviewingNotifications(
+            EventBus eventBus,
+            NotificationController notificationController,
+            GeneralSystemSettingsService generalSystemSettingsService,
+            WebApplication webApplication)
+    {
+        return new ReviewingNotifications(eventBus, notificationController, generalSystemSettingsService,
+                webApplication);
+    }
+
+    @Bean
+    public ForwardPhase2Feedback forwardPhase2Feedback(
+            EventBus eventBus,
+            WebApplication webApplication,
+            ProjectForumService projectForumService)
+    {
+        return new ForwardPhase2Feedback(webApplication, projectForumService, eventBus);
+    }
+
+    // Not sure why this dependency lives in the view module
+    @Bean
+    public ReviewerSupportMailer reviewerSupportMailer(
+            EventBus eventBus,
+            GeneralSystemSettingsService generalSystemSettingsService,
+            MailEventService mailEventService)
+    {
+        return new ReviewerSupportMailer(eventBus, mailEventService, generalSystemSettingsService);
+    }
+
+    // Not sure why this dependency lives in the view module
+    @Bean
+    public ReviewerAssignedNotifications reviewerAssignedNotifications(
+            EventBus eventBus,
+            NotificationController notificationController,
+            RoughDraftApprovalService roughDraftApprovalService,
+            FinalSeminarApprovalService finalSeminarApprovalService)
+    {
+        return new ReviewerAssignedNotifications(roughDraftApprovalService,
+                finalSeminarApprovalService, notificationController, eventBus);
+    }
+}
diff --git a/war/src/main/java/se/su/dsv/scipro/war/WorkerConfig.java b/war/src/main/java/se/su/dsv/scipro/war/WorkerConfig.java
new file mode 100644
index 0000000000..076bc219fc
--- /dev/null
+++ b/war/src/main/java/se/su/dsv/scipro/war/WorkerConfig.java
@@ -0,0 +1,245 @@
+package se.su.dsv.scipro.war;
+
+import com.google.common.eventbus.EventBus;
+import jakarta.inject.Provider;
+import jakarta.persistence.EntityManagerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Scope;
+import org.springframework.transaction.PlatformTransactionManager;
+import se.su.dsv.scipro.file.FileService;
+import se.su.dsv.scipro.finalseminar.FinalSeminarService;
+import se.su.dsv.scipro.firstmeeting.FirstMeetingReminderWorker;
+import se.su.dsv.scipro.firstmeeting.FirstMeetingService;
+import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
+import se.su.dsv.scipro.mail.MailEventService;
+import se.su.dsv.scipro.mail.MailEventWorker;
+import se.su.dsv.scipro.mail.Mailer;
+import se.su.dsv.scipro.match.AllowAllIdeaCreationJudge;
+import se.su.dsv.scipro.match.ApplicationPeriodService;
+import se.su.dsv.scipro.match.IdeaCreationJudge;
+import se.su.dsv.scipro.match.IdeaService;
+import se.su.dsv.scipro.misc.DaysService;
+import se.su.dsv.scipro.peer.ExpiredRequestWorker;
+import se.su.dsv.scipro.peer.ExpiredReviewResetWorker;
+import se.su.dsv.scipro.peer.PeerWorkerSchedules;
+import se.su.dsv.scipro.plagiarism.PlagiarismRequestRepository;
+import se.su.dsv.scipro.plagiarism.PlagiarismSubmitter;
+import se.su.dsv.scipro.plagiarism.urkund.StatusPollingWorker;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundApi;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundService;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSettings;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSettingsRepository;
+import se.su.dsv.scipro.plagiarism.urkund.UrkundSubmissionRepository;
+import se.su.dsv.scipro.project.ProjectService;
+import se.su.dsv.scipro.projectpartner.ProjectPartnerRepository;
+import se.su.dsv.scipro.projectpartner.RemoveFulfilledPartnerAdsWorker;
+import se.su.dsv.scipro.reviewing.MyReviewService;
+import se.su.dsv.scipro.reviewing.ReviewerDecisionReminderWorker;
+import se.su.dsv.scipro.sukat.Sukat;
+import se.su.dsv.scipro.workerthreads.GradeFinalSeminarParticipantReminderWorker;
+import se.su.dsv.scipro.workerthreads.IdeaExportWorker;
+import se.su.dsv.scipro.workerthreads.ManualMatchRemindWorker;
+import se.su.dsv.scipro.workerthreads.NotificationCompilationWorker;
+import se.su.dsv.scipro.workerthreads.Scheduler;
+import se.su.dsv.scipro.workerthreads.SchedulerImpl;
+import se.su.dsv.scipro.workerthreads.TemporaryWorkerScheduler;
+import se.su.dsv.scipro.workerthreads.ThesisUploadDeadlineWorker;
+import se.su.dsv.scipro.workerthreads.ThesisUploadReminderWorker;
+import se.su.dsv.scipro.workerthreads.WorkerTransactionManager;
+
+import java.time.Clock;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+@Configuration(proxyBeanMethods = false)
+@Import(WorkerConfig.Workers.class)
+public class WorkerConfig {
+    private static final int NUMBER_OF_WORKER_THREADS = 4;
+
+    @Bean
+    public ScheduledExecutorService scheduledExecutorService() {
+        return Executors.newScheduledThreadPool(NUMBER_OF_WORKER_THREADS);
+    }
+
+    @Bean
+    public SchedulerImpl scheduler(
+            ScheduledExecutorService scheduledExecutorService,
+            EntityManagerFactory entityManagerFactory)
+    {
+        return new SchedulerImpl(scheduledExecutorService, entityManagerFactory);
+    }
+
+    @Bean
+    public TemporaryWorkerScheduler temporaryWorkerScheduler(
+            Scheduler scheduler,
+            Provider<MailEventWorker> mailEventWorker,
+            Provider<NotificationCompilationWorker> notificationCompilationWorker,
+            Provider<IdeaExportWorker> ideaExportWorker,
+            Provider<ThesisUploadReminderWorker> thesisUploadReminderWorker,
+            Provider<ThesisUploadDeadlineWorker> thesisUploadDeadlineWorker,
+            Provider<ManualMatchRemindWorker> manualMatchRemindWorkerProvider,
+            Provider<ReviewerDecisionReminderWorker> reviewerDecisionReminderWorker,
+            Provider<PlagiarismSubmitter> plagiarismSubmitter,
+            Provider<StatusPollingWorker> urkundPoller,
+            Provider<RemoveFulfilledPartnerAdsWorker> removeFulfilledPartnerAds,
+            Provider<GradeFinalSeminarParticipantReminderWorker> gradeFinalSeminarParticipantReminderWorkerProvider)
+    {
+        return new TemporaryWorkerScheduler(scheduler, mailEventWorker, notificationCompilationWorker, ideaExportWorker, thesisUploadReminderWorker, thesisUploadDeadlineWorker, manualMatchRemindWorkerProvider, reviewerDecisionReminderWorker, plagiarismSubmitter, urkundPoller, removeFulfilledPartnerAds, gradeFinalSeminarParticipantReminderWorkerProvider);
+    }
+
+    @Bean
+    public FirstMeetingReminderWorker.FirstMeetingReminderWorkerSchedule firstMeetingReminderWorkerScheduler(
+            Scheduler scheduler,
+            Provider<FirstMeetingReminderWorker> firstMeetingReminderWorkerProvider)
+    {
+        return new FirstMeetingReminderWorker.FirstMeetingReminderWorkerSchedule(scheduler, firstMeetingReminderWorkerProvider);
+    }
+
+    @Bean
+    public PeerWorkerSchedules peerWorkerSchedules(
+            Scheduler scheduler,
+            Provider<ExpiredRequestWorker> expiredRequestWorker,
+            Provider<ExpiredReviewResetWorker> expiredReviewResetWorker)
+    {
+        return new PeerWorkerSchedules(scheduler, expiredRequestWorker, expiredReviewResetWorker);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(IdeaCreationJudge.class)
+    public IdeaCreationJudge ideaCreationJudge() {
+        return new AllowAllIdeaCreationJudge();
+    }
+
+    @Bean
+    public PlagiarismSubmitter plagiarismSubmitter(
+            Provider<UrkundSettings> urkundSettings,
+            PlagiarismRequestRepository plagiarismRequestRepository,
+            UrkundService urkundService,
+            FileService fileService)
+    {
+        return new PlagiarismSubmitter(urkundSettings, plagiarismRequestRepository, urkundService, fileService);
+    }
+
+    @Bean
+    public UrkundSettings urkundSettings(UrkundSettingsRepository urkundSettingsRepository) {
+        return urkundSettingsRepository.getSettings();
+    }
+
+    @Bean
+    @Scope("prototype")
+    public WorkerTransactionManager workerTransactionManager(PlatformTransactionManager platformTransactionManager) {
+        return new SpringManagedWorkerTransactions(platformTransactionManager);
+    }
+
+    @Configuration
+    public static class Workers {
+        @Bean
+        public MailEventWorker mailEventWorker(
+                MailEventService mailEventService,
+                GeneralSystemSettingsService generalSystemSettingsService,
+                Mailer mailer)
+        {
+            return new MailEventWorker(mailEventService, generalSystemSettingsService, mailer);
+        }
+
+        @Bean
+        public NotificationCompilationWorker notificationCompilationWorker() {
+            return new NotificationCompilationWorker();
+        }
+
+        @Bean
+        public IdeaExportWorker ideaExportWorker(
+                IdeaService ideaService,
+                MailEventService mailEventService,
+                ProjectService projectService,
+                IdeaCreationJudge ideaCreationJudge,
+                EventBus eventBus,
+                FirstMeetingService firstMeetingService)
+        {
+            return new IdeaExportWorker(ideaService, mailEventService, projectService, ideaCreationJudge, eventBus,
+                    firstMeetingService);
+        }
+
+        @Bean
+        public ThesisUploadReminderWorker thesisUploadReminderWorker() {
+            return new ThesisUploadReminderWorker();
+        }
+
+        @Bean
+        public ThesisUploadDeadlineWorker thesisUploadDeadlineWorker() {
+            return new ThesisUploadDeadlineWorker();
+        }
+
+        @Bean
+        public ManualMatchRemindWorker manualMatchRemindWorker(
+                ApplicationPeriodService applicationPeriodService,
+                MailEventService mailEventService,
+                GeneralSystemSettingsService generalSystemSettingsService,
+                IdeaService ideaService,
+                Clock clock)
+        {
+            return new ManualMatchRemindWorker(applicationPeriodService, mailEventService, generalSystemSettingsService,
+                    ideaService, clock);
+        }
+
+        @Bean
+        public ReviewerDecisionReminderWorker reviewerDecisionReminderWorker(
+                MyReviewService myReviewService,
+                DaysService daysService,
+                MailEventService mailEventService)
+        {
+            return new ReviewerDecisionReminderWorker(myReviewService, daysService, mailEventService);
+        }
+
+        @Bean
+        public StatusPollingWorker statusPollingWorker(
+                UrkundSubmissionRepository urkundSubmissionRepository,
+                UrkundApi urkundApi,
+                Provider<UrkundSettings> urkundSettingsProvider,
+                Sukat sukat)
+        {
+            return new StatusPollingWorker(urkundSubmissionRepository, urkundApi, urkundSettingsProvider, sukat);
+        }
+
+        @Bean
+        public RemoveFulfilledPartnerAdsWorker removeFulfilledPartnerAdsWorker(
+                IdeaService ideaService,
+                ProjectPartnerRepository projectPartnerRepository)
+        {
+            return new RemoveFulfilledPartnerAdsWorker(ideaService, projectPartnerRepository);
+        }
+
+        @Bean
+        public GradeFinalSeminarParticipantReminderWorker gradeFinalSeminarParticipantReminderWorker(
+                MailEventService mailEventService,
+                GeneralSystemSettingsService generalSystemSettingsService,
+                FinalSeminarService finalSeminarService)
+        {
+            return new GradeFinalSeminarParticipantReminderWorker(finalSeminarService, mailEventService, generalSystemSettingsService);
+        }
+
+        @Bean
+        public FirstMeetingReminderWorker firstMeetingReminderWorker(
+                IdeaService ideaService,
+                DaysService daysService,
+                MailEventService mailEventService,
+                GeneralSystemSettingsService generalSystemSettingsService,
+                Clock clock)
+        {
+            return new FirstMeetingReminderWorker(ideaService, daysService, mailEventService, generalSystemSettingsService, clock);
+        }
+
+        @Bean
+        public ExpiredReviewResetWorker expiredReviewResetWorker() {
+            return new ExpiredReviewResetWorker();
+        }
+
+        @Bean
+        public ExpiredRequestWorker expiredRequestWorker() {
+            return new ExpiredRequestWorker();
+        }
+    }
+}
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java b/war/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java
similarity index 63%
rename from core/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java
rename to war/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java
index d8ecf35252..0acb56920c 100755
--- a/core/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java
+++ b/war/src/main/java/se/su/dsv/scipro/workerthreads/SchedulerImpl.java
@@ -1,12 +1,18 @@
 package se.su.dsv.scipro.workerthreads;
 
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.orm.jpa.EntityManagerFactoryUtils;
+import org.springframework.orm.jpa.EntityManagerHolder;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
 import se.su.dsv.scipro.system.Lifecycle;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.inject.Singleton;
+
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -19,13 +25,15 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
     private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerImpl.class);
 
     private final ScheduledExecutorService scheduledExecutorService;
+    private final EntityManagerFactory emf;
 
     private final Set<Task> tasks = new TreeSet<>(new Task.ByDescriptionComparator());
     private final Set<Task> runningWorkers = Collections.synchronizedSet(new HashSet<>());
 
     @Inject
-    public SchedulerImpl(ScheduledExecutorService scheduledExecutorService) {
+    public SchedulerImpl(ScheduledExecutorService scheduledExecutorService, EntityManagerFactory emf) {
         this.scheduledExecutorService = scheduledExecutorService;
+        this.emf = emf;
     }
 
     @Override
@@ -43,6 +51,14 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
     private Runnable tracked(final Task task) {
         return () -> {
             try {
+                // Since we're not in a web request when running a scheduled job, Spring's OpenEntityManagerInView
+                // filter is not active. Therefore, we need to manually bind a new EntityManager to the current thread
+                // to be shared by all DAOs. If we do not, we will get problems with detached entity passed to persist
+                // since every read and write from the database will be in a new EntityManager.
+                EntityManager em = emf.createEntityManager();
+                EntityManagerHolder emHolder = new EntityManagerHolder(em);
+                TransactionSynchronizationManager.bindResource(emf, emHolder);
+
                 runningWorkers.add(task);
                 task.execute();
             }
@@ -51,6 +67,11 @@ public class SchedulerImpl implements Lifecycle, Scheduler {
             }
             finally {
                 runningWorkers.remove(task);
+
+                // Clean up the shared EntityManager
+                EntityManagerHolder emHolder = (EntityManagerHolder)
+                        TransactionSynchronizationManager.unbindResource(emf);
+                EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
             }
         };
     }
diff --git a/core/src/main/java/se/su/dsv/scipro/workerthreads/TaskScheduling.java b/war/src/main/java/se/su/dsv/scipro/workerthreads/TaskScheduling.java
similarity index 100%
rename from core/src/main/java/se/su/dsv/scipro/workerthreads/TaskScheduling.java
rename to war/src/main/java/se/su/dsv/scipro/workerthreads/TaskScheduling.java
diff --git a/war/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/war/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
new file mode 100644
index 0000000000..ad4ffa3bbd
--- /dev/null
+++ b/war/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
@@ -0,0 +1 @@
+se.su.dsv.scipro.war.Main
diff --git a/war/src/main/resources/application.properties b/war/src/main/resources/application.properties
new file mode 100644
index 0000000000..753d0e323c
--- /dev/null
+++ b/war/src/main/resources/application.properties
@@ -0,0 +1,20 @@
+spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS
+spring.flyway.table=schema_version
+
+# Spring Boot changes Hibernates default naming strategy to convert camelCase to snake_case
+# We currently rely on the names to be treated as they are in Java, so we need to set it back to the default
+spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+
+# We also need to set the implicit strategy to be JPA compliant, as we rely on this naming strategy for certain
+# join tables (idea_Keyword vs idea_keyword)
+spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
+
+spring.mvc.servlet.path=/api
+
+springdoc.swagger-ui.path=/swagger
+springdoc.swagger-ui.persist-authorization=true
+
+# These will be overwritten by configuration in the environment of servers it is deployed to
+spring.security.oauth2.resourceserver.opaquetoken.client-id=scipro-api-client
+spring.security.oauth2.resourceserver.opaquetoken.client-secret=scipro-api-secret
+spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:59733/introspect
diff --git a/war/src/main/webapp/WEB-INF/web.xml b/war/src/main/webapp/WEB-INF/web.xml
new file mode 100755
index 0000000000..b346f3b49c
--- /dev/null
+++ b/war/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
+         version="4.0" metadata-complete="true">
+
+    <display-name>SciPro</display-name>
+
+    <!-- disables Spring's built in ServletContainerInitializer which causes a slow class path scanning -->
+    <absolute-ordering/>
+
+    <session-config>
+        <session-timeout>480</session-timeout>
+    </session-config>
+</web-app>
diff --git a/core/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java b/war/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java
similarity index 83%
rename from core/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java
rename to war/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java
index 45e9ec4f67..f79a05cd26 100644
--- a/core/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java
+++ b/war/src/test/java/se/su/dsv/scipro/workerthreads/SchedulerImplTest.java
@@ -1,8 +1,10 @@
 package se.su.dsv.scipro.workerthreads;
 
-import com.google.inject.util.Providers;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityManagerFactory;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
@@ -24,6 +26,8 @@ public class SchedulerImplTest {
     private Worker abstractWorker;
     @Mock
     private Task task;
+    @Mock(answer = Answers.RETURNS_MOCKS)
+    private EntityManagerFactory emf;
     @InjectMocks
     private SchedulerImpl scheduler;
 
@@ -31,7 +35,7 @@ public class SchedulerImplTest {
     public void schedules_correctly() {
         long period = 1;
         TimeUnit timeUnit = TimeUnit.SECONDS;
-        scheduler.schedule("My worker").runBy(Providers.of(abstractWorker)).every(period, timeUnit);
+        scheduler.schedule("My worker").runBy(() -> abstractWorker).every(period, timeUnit);
         verify(scheduledExecutorService).scheduleAtFixedRate(any(Runnable.class), eq(period), eq(period), eq(timeUnit));
     }