From 4a0fed617af02fcd04812242f39ae619fe3c91a9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 8 Jun 2026 20:04:21 +0200 Subject: [PATCH] refactor(geschichte): route all reads through GeschichteQueryService JourneyItemService no longer injects GeschichteRepository directly. GeschichteQueryService gains findById() so JourneyItemService.append() can load the Geschichte entity via the service layer, satisfying the cross-domain layering rule. Co-Authored-By: Claude Sonnet 4.6 --- .../geschichte/GeschichteQueryService.java | 10 ++++++-- .../journeyitem/JourneyItemService.java | 4 +--- .../journeyitem/JourneyItemServiceTest.java | 24 ++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteQueryService.java b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteQueryService.java index 488221db..cde834bd 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteQueryService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteQueryService.java @@ -3,13 +3,15 @@ package org.raddatz.familienarchiv.geschichte; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Optional; import java.util.UUID; /** * Thin read-only service owning {@link GeschichteRepository}. * Exists so that {@code JourneyItemService} can check Geschichte existence - * without holding a direct reference to the Geschichte repository - * (cross-domain repository access is not allowed per layering rules). + * and load Geschichte instances without holding a direct reference to the + * Geschichte repository (cross-domain repository access is not allowed per + * layering rules). */ @Service @RequiredArgsConstructor @@ -20,4 +22,8 @@ public class GeschichteQueryService { public boolean existsById(UUID id) { return geschichteRepository.existsById(id); } + + public Optional findById(UUID id) { + return geschichteRepository.findById(id); + } } diff --git a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemService.java b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemService.java index 5b83af6a..d6fc0daf 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemService.java @@ -11,7 +11,6 @@ import org.raddatz.familienarchiv.exception.DomainException; import org.raddatz.familienarchiv.exception.ErrorCode; import org.raddatz.familienarchiv.geschichte.Geschichte; import org.raddatz.familienarchiv.geschichte.GeschichteQueryService; -import org.raddatz.familienarchiv.geschichte.GeschichteRepository; import org.raddatz.familienarchiv.geschichte.GeschichteType; import org.raddatz.familienarchiv.geschichte.DocumentSummary; import org.raddatz.familienarchiv.person.Person; @@ -34,7 +33,6 @@ public class JourneyItemService { static final int MAX_NOTE_LENGTH = 5000; private final JourneyItemRepository journeyItemRepository; - private final GeschichteRepository geschichteRepository; private final GeschichteQueryService geschichteQueryService; private final DocumentService documentService; private final AuditService auditService; @@ -42,7 +40,7 @@ public class JourneyItemService { @Transactional public JourneyItemView append(UUID geschichteId, JourneyItemCreateDTO dto) { - Geschichte g = geschichteRepository.findById(geschichteId) + Geschichte g = geschichteQueryService.findById(geschichteId) .orElseThrow(() -> DomainException.notFound(ErrorCode.GESCHICHTE_NOT_FOUND, "Journey not found: " + geschichteId)); diff --git a/backend/src/test/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemServiceTest.java index 14e299d9..8576141e 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/geschichte/journeyitem/JourneyItemServiceTest.java @@ -16,7 +16,6 @@ import org.raddatz.familienarchiv.exception.DomainException; import org.raddatz.familienarchiv.exception.ErrorCode; import org.raddatz.familienarchiv.geschichte.Geschichte; import org.raddatz.familienarchiv.geschichte.GeschichteQueryService; -import org.raddatz.familienarchiv.geschichte.GeschichteRepository; import org.raddatz.familienarchiv.geschichte.GeschichteStatus; import org.raddatz.familienarchiv.geschichte.GeschichteType; import org.raddatz.familienarchiv.person.Person; @@ -26,7 +25,6 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import java.time.LocalDate; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -36,7 +34,6 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.lenient; @@ -48,7 +45,6 @@ import static org.mockito.Mockito.when; class JourneyItemServiceTest { @Mock JourneyItemRepository journeyItemRepository; - @Mock GeschichteRepository geschichteRepository; @Mock GeschichteQueryService geschichteQueryService; @Mock DocumentService documentService; @Mock AuditService auditService; @@ -148,7 +144,7 @@ class JourneyItemServiceTest { @Test void append_to_empty_journey_starts_at_10() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty()); JourneyItem saved = savedItem(itemId, journey, 10, null, "Note"); @@ -165,7 +161,7 @@ class JourneyItemServiceTest { @Test void append_after_reorder_continues_from_max_position() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(2L); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(40)); JourneyItem saved = savedItem(itemId, journey, 50, null, "Note"); @@ -182,7 +178,7 @@ class JourneyItemServiceTest { @Test void append_returns400_when_neither_documentId_nor_note() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); @@ -195,7 +191,7 @@ class JourneyItemServiceTest { @Test void append_returns400_when_note_trims_to_empty_and_no_document() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); @@ -208,7 +204,7 @@ class JourneyItemServiceTest { @Test void append_returns400_when_note_exceeds_5000_chars() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); @@ -228,7 +224,7 @@ class JourneyItemServiceTest { .type(GeschichteType.STORY) .status(GeschichteStatus.DRAFT) .build(); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(story)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(story)); JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); dto.setNote("Note"); @@ -242,7 +238,7 @@ class JourneyItemServiceTest { @Test void append_returns404_when_documentId_does_not_exist() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); when(documentService.getSummaryById(docId)) .thenThrow(DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "not found")); @@ -259,7 +255,7 @@ class JourneyItemServiceTest { @Test void append_returns409_when_100_items_exist() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(100L); JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); @@ -275,7 +271,7 @@ class JourneyItemServiceTest { void cap_is_COUNT_based_not_MAX_position_based() { // 99 rows with MAX(position)=2000 should still accept the 100th append Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(99L); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(2000)); JourneyItem saved = savedItem(itemId, journey, 2010, null, "Note"); @@ -290,7 +286,7 @@ class JourneyItemServiceTest { @Test void append_audits_JOURNEY_ITEM_ADDED() { Geschichte journey = journey(geschichteId); - when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(journey)); + when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(journey)); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(0L); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty()); JourneyItem saved = savedItem(itemId, journey, 10, null, "Note");