refactor: move createPerson logic into PersonService
Controller was directly calling personRepository.save() for person creation. Extracted into PersonService.createPerson() to enforce Controller → Service → Repository layering. Also documented the layering rules in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
14
CLAUDE.md
14
CLAUDE.md
@@ -89,6 +89,20 @@ Database migrations live in `src/main/resources/db/migration/` (Flyway). Configu
|
|||||||
|
|
||||||
Authentication: form login → backend sets session → `auth_token` cookie → hooks.server.ts injects into all backend requests.
|
Authentication: form login → backend sets session → `auth_token` cookie → hooks.server.ts injects into all backend requests.
|
||||||
|
|
||||||
|
### Backend Layering Rules
|
||||||
|
|
||||||
|
Strict layering must be respected at all times:
|
||||||
|
|
||||||
|
```
|
||||||
|
Controller → Service → Repository → DB
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Controllers** must never inject or call repositories directly. All business logic goes through a service.
|
||||||
|
- **Services** must never reach into another domain's repository directly. If Service A needs data owned by domain B, it calls Service B — not Repository B.
|
||||||
|
- ✅ `DocumentService` → `PersonService.findById()` → `PersonRepository`
|
||||||
|
- ❌ `DocumentService` → `PersonRepository` (bypasses the service layer)
|
||||||
|
- This keeps domain boundaries clear and business logic testable in isolation.
|
||||||
|
|
||||||
### Key Design Patterns
|
### Key Design Patterns
|
||||||
|
|
||||||
- **Search**: `DocumentSpecifications` (Spring Data JPA Specification pattern) enables composable, dynamic query building for the document search endpoint
|
- **Search**: `DocumentSpecifications` (Spring Data JPA Specification pattern) enables composable, dynamic query building for the document search endpoint
|
||||||
|
|||||||
@@ -51,12 +51,7 @@ public class PersonController {
|
|||||||
if (firstName == null || firstName.isBlank() || lastName == null || lastName.isBlank()) {
|
if (firstName == null || firstName.isBlank() || lastName == null || lastName.isBlank()) {
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vor- und Nachname sind Pflichtfelder");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vor- und Nachname sind Pflichtfelder");
|
||||||
}
|
}
|
||||||
Person person = Person.builder()
|
return ResponseEntity.ok(personService.createPerson(firstName.trim(), lastName.trim(), body.get("alias")));
|
||||||
.firstName(firstName.trim())
|
|
||||||
.lastName(lastName.trim())
|
|
||||||
.alias(body.get("alias"))
|
|
||||||
.build();
|
|
||||||
return ResponseEntity.ok(personRepository.save(person));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ public class PersonService {
|
|||||||
|
|
||||||
private final PersonRepository personRepository;
|
private final PersonRepository personRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Person createPerson(String firstName, String lastName, String alias) {
|
||||||
|
Person person = Person.builder()
|
||||||
|
.firstName(firstName)
|
||||||
|
.lastName(lastName)
|
||||||
|
.alias(alias == null || alias.isBlank() ? null : alias.trim())
|
||||||
|
.build();
|
||||||
|
return personRepository.save(person);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Person updatePerson(UUID id, String firstName, String lastName, String alias) {
|
public Person updatePerson(UUID id, String firstName, String lastName, String alias) {
|
||||||
Person person = personRepository.findById(id)
|
Person person = personRepository.findById(id)
|
||||||
|
|||||||
Reference in New Issue
Block a user