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:
@@ -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<Geschichte> findById(UUID id) {
|
||||
return geschichteRepository.findById(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user