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 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-08 20:04:21 +02:00
parent 7ba6342a84
commit 4a0fed617a
3 changed files with 19 additions and 19 deletions

View File

@@ -3,13 +3,15 @@ package org.raddatz.familienarchiv.geschichte;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
/** /**
* Thin read-only service owning {@link GeschichteRepository}. * Thin read-only service owning {@link GeschichteRepository}.
* Exists so that {@code JourneyItemService} can check Geschichte existence * Exists so that {@code JourneyItemService} can check Geschichte existence
* without holding a direct reference to the Geschichte repository * and load Geschichte instances without holding a direct reference to the
* (cross-domain repository access is not allowed per layering rules). * Geschichte repository (cross-domain repository access is not allowed per
* layering rules).
*/ */
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -20,4 +22,8 @@ public class GeschichteQueryService {
public boolean existsById(UUID id) { public boolean existsById(UUID id) {
return geschichteRepository.existsById(id); return geschichteRepository.existsById(id);
} }
public Optional<Geschichte> findById(UUID id) {
return geschichteRepository.findById(id);
}
} }

View File

@@ -11,7 +11,6 @@ import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode; import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.geschichte.Geschichte; import org.raddatz.familienarchiv.geschichte.Geschichte;
import org.raddatz.familienarchiv.geschichte.GeschichteQueryService; import org.raddatz.familienarchiv.geschichte.GeschichteQueryService;
import org.raddatz.familienarchiv.geschichte.GeschichteRepository;
import org.raddatz.familienarchiv.geschichte.GeschichteType; import org.raddatz.familienarchiv.geschichte.GeschichteType;
import org.raddatz.familienarchiv.geschichte.DocumentSummary; import org.raddatz.familienarchiv.geschichte.DocumentSummary;
import org.raddatz.familienarchiv.person.Person; import org.raddatz.familienarchiv.person.Person;
@@ -34,7 +33,6 @@ public class JourneyItemService {
static final int MAX_NOTE_LENGTH = 5000; static final int MAX_NOTE_LENGTH = 5000;
private final JourneyItemRepository journeyItemRepository; private final JourneyItemRepository journeyItemRepository;
private final GeschichteRepository geschichteRepository;
private final GeschichteQueryService geschichteQueryService; private final GeschichteQueryService geschichteQueryService;
private final DocumentService documentService; private final DocumentService documentService;
private final AuditService auditService; private final AuditService auditService;
@@ -42,7 +40,7 @@ public class JourneyItemService {
@Transactional @Transactional
public JourneyItemView append(UUID geschichteId, JourneyItemCreateDTO dto) { public JourneyItemView append(UUID geschichteId, JourneyItemCreateDTO dto) {
Geschichte g = geschichteRepository.findById(geschichteId) Geschichte g = geschichteQueryService.findById(geschichteId)
.orElseThrow(() -> DomainException.notFound(ErrorCode.GESCHICHTE_NOT_FOUND, .orElseThrow(() -> DomainException.notFound(ErrorCode.GESCHICHTE_NOT_FOUND,
"Journey not found: " + geschichteId)); "Journey not found: " + geschichteId));

View File

@@ -16,7 +16,6 @@ import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode; import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.geschichte.Geschichte; import org.raddatz.familienarchiv.geschichte.Geschichte;
import org.raddatz.familienarchiv.geschichte.GeschichteQueryService; import org.raddatz.familienarchiv.geschichte.GeschichteQueryService;
import org.raddatz.familienarchiv.geschichte.GeschichteRepository;
import org.raddatz.familienarchiv.geschichte.GeschichteStatus; import org.raddatz.familienarchiv.geschichte.GeschichteStatus;
import org.raddatz.familienarchiv.geschichte.GeschichteType; import org.raddatz.familienarchiv.geschichte.GeschichteType;
import org.raddatz.familienarchiv.person.Person; 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.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import java.time.LocalDate;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; 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.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
@@ -48,7 +45,6 @@ import static org.mockito.Mockito.when;
class JourneyItemServiceTest { class JourneyItemServiceTest {
@Mock JourneyItemRepository journeyItemRepository; @Mock JourneyItemRepository journeyItemRepository;
@Mock GeschichteRepository geschichteRepository;
@Mock GeschichteQueryService geschichteQueryService; @Mock GeschichteQueryService geschichteQueryService;
@Mock DocumentService documentService; @Mock DocumentService documentService;
@Mock AuditService auditService; @Mock AuditService auditService;
@@ -148,7 +144,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_to_empty_journey_starts_at_10() { void append_to_empty_journey_starts_at_10() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty()); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty());
JourneyItem saved = savedItem(itemId, journey, 10, null, "Note"); JourneyItem saved = savedItem(itemId, journey, 10, null, "Note");
@@ -165,7 +161,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_after_reorder_continues_from_max_position() { void append_after_reorder_continues_from_max_position() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(2L);
when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(40)); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(40));
JourneyItem saved = savedItem(itemId, journey, 50, null, "Note"); JourneyItem saved = savedItem(itemId, journey, 50, null, "Note");
@@ -182,7 +178,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_returns400_when_neither_documentId_nor_note() { void append_returns400_when_neither_documentId_nor_note() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
@@ -195,7 +191,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_returns400_when_note_trims_to_empty_and_no_document() { void append_returns400_when_note_trims_to_empty_and_no_document() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
@@ -208,7 +204,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_returns400_when_note_exceeds_5000_chars() { void append_returns400_when_note_exceeds_5000_chars() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
@@ -228,7 +224,7 @@ class JourneyItemServiceTest {
.type(GeschichteType.STORY) .type(GeschichteType.STORY)
.status(GeschichteStatus.DRAFT) .status(GeschichteStatus.DRAFT)
.build(); .build();
when(geschichteRepository.findById(geschichteId)).thenReturn(Optional.of(story)); when(geschichteQueryService.findById(geschichteId)).thenReturn(Optional.of(story));
JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
dto.setNote("Note"); dto.setNote("Note");
@@ -242,7 +238,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_returns404_when_documentId_does_not_exist() { void append_returns404_when_documentId_does_not_exist() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
when(documentService.getSummaryById(docId)) when(documentService.getSummaryById(docId))
.thenThrow(DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "not found")); .thenThrow(DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "not found"));
@@ -259,7 +255,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_returns409_when_100_items_exist() { void append_returns409_when_100_items_exist() {
Geschichte journey = journey(geschichteId); 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); when(journeyItemRepository.countByGeschichteId(geschichteId)).thenReturn(100L);
JourneyItemCreateDTO dto = new JourneyItemCreateDTO(); JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
@@ -275,7 +271,7 @@ class JourneyItemServiceTest {
void cap_is_COUNT_based_not_MAX_position_based() { void cap_is_COUNT_based_not_MAX_position_based() {
// 99 rows with MAX(position)=2000 should still accept the 100th append // 99 rows with MAX(position)=2000 should still accept the 100th append
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(99L);
when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(2000)); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.of(2000));
JourneyItem saved = savedItem(itemId, journey, 2010, null, "Note"); JourneyItem saved = savedItem(itemId, journey, 2010, null, "Note");
@@ -290,7 +286,7 @@ class JourneyItemServiceTest {
@Test @Test
void append_audits_JOURNEY_ITEM_ADDED() { void append_audits_JOURNEY_ITEM_ADDED() {
Geschichte journey = journey(geschichteId); 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.countByGeschichteId(geschichteId)).thenReturn(0L);
when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty()); when(journeyItemRepository.findMaxPositionByGeschichteId(geschichteId)).thenReturn(Optional.empty());
JourneyItem saved = savedItem(itemId, journey, 10, null, "Note"); JourneyItem saved = savedItem(itemId, journey, 10, null, "Note");