clean arch

This commit is contained in:
2026-05-24 07:37:26 +02:00
parent 7032627153
commit bc86c57d78
10 changed files with 209 additions and 0 deletions
@@ -0,0 +1,43 @@
package fr.bonsai.api.adapter.in.web;
import fr.bonsai.api.adapter.in.web.dto.BonsaiRequest;
import fr.bonsai.api.adapter.in.web.dto.BonsaiResponse;
import fr.bonsai.api.application.port.in.CreateBonsaiUseCase;
import fr.bonsai.api.application.port.in.GetBonsaiUseCase;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/api/bonsais")
public class BonsaiController {
private final GetBonsaiUseCase getBonsaiUseCase;
private final CreateBonsaiUseCase createBonsaiUseCase;
public BonsaiController(GetBonsaiUseCase getBonsaiUseCase, CreateBonsaiUseCase createBonsaiUseCase) {
this.getBonsaiUseCase = getBonsaiUseCase;
this.createBonsaiUseCase = createBonsaiUseCase;
}
@GetMapping
public List<BonsaiResponse> getAll() {
return getBonsaiUseCase.getAll().stream()
.map(BonsaiResponse::from)
.toList();
}
@GetMapping("/{id}")
public BonsaiResponse getById(@PathVariable UUID id) {
return BonsaiResponse.from(getBonsaiUseCase.getById(id));
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public BonsaiResponse create(@RequestBody BonsaiRequest request) {
var command = new CreateBonsaiUseCase.Command(request.name(), request.species(), request.ageYears());
return BonsaiResponse.from(createBonsaiUseCase.create(command));
}
}
@@ -0,0 +1,3 @@
package fr.bonsai.api.adapter.in.web.dto;
public record BonsaiRequest(String name, String species, int ageYears) {}
@@ -0,0 +1,12 @@
package fr.bonsai.api.adapter.in.web.dto;
import fr.bonsai.api.domain.model.Bonsai;
import java.util.UUID;
public record BonsaiResponse(UUID id, String name, String species, int ageYears) {
public static BonsaiResponse from(Bonsai bonsai) {
return new BonsaiResponse(bonsai.getId(), bonsai.getName(), bonsai.getSpecies(), bonsai.getAgeYears());
}
}
@@ -0,0 +1,28 @@
package fr.bonsai.api.adapter.out.persistence;
import fr.bonsai.api.application.port.out.BonsaiRepository;
import fr.bonsai.api.domain.model.Bonsai;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class InMemoryBonsaiRepository implements BonsaiRepository {
private final Map<UUID, Bonsai> store = new ConcurrentHashMap<>();
@Override
public List<Bonsai> findAll() {
return List.copyOf(store.values());
}
@Override
public Optional<Bonsai> findById(UUID id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Bonsai save(Bonsai bonsai) {
store.put(bonsai.getId(), bonsai);
return bonsai;
}
}
@@ -0,0 +1,10 @@
package fr.bonsai.api.application.port.in;
import fr.bonsai.api.domain.model.Bonsai;
public interface CreateBonsaiUseCase {
record Command(String name, String species, int ageYears) {}
Bonsai create(Command command);
}
@@ -0,0 +1,13 @@
package fr.bonsai.api.application.port.in;
import fr.bonsai.api.domain.model.Bonsai;
import java.util.List;
import java.util.UUID;
public interface GetBonsaiUseCase {
List<Bonsai> getAll();
Bonsai getById(UUID id);
}
@@ -0,0 +1,16 @@
package fr.bonsai.api.application.port.out;
import fr.bonsai.api.domain.model.Bonsai;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface BonsaiRepository {
List<Bonsai> findAll();
Optional<Bonsai> findById(UUID id);
Bonsai save(Bonsai bonsai);
}
@@ -0,0 +1,36 @@
package fr.bonsai.api.application.usecase;
import fr.bonsai.api.application.port.in.CreateBonsaiUseCase;
import fr.bonsai.api.application.port.in.GetBonsaiUseCase;
import fr.bonsai.api.application.port.out.BonsaiRepository;
import fr.bonsai.api.domain.model.Bonsai;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
public class BonsaiService implements GetBonsaiUseCase, CreateBonsaiUseCase {
private final BonsaiRepository repository;
public BonsaiService(BonsaiRepository repository) {
this.repository = repository;
}
@Override
public List<Bonsai> getAll() {
return repository.findAll();
}
@Override
public Bonsai getById(UUID id) {
return repository.findById(id)
.orElseThrow(() -> new NoSuchElementException("Bonsai not found: " + id));
}
@Override
public Bonsai create(Command command) {
Bonsai bonsai = Bonsai.create(command.name(), command.species(), command.ageYears());
return repository.save(bonsai);
}
}
@@ -0,0 +1,21 @@
package fr.bonsai.api.config;
import fr.bonsai.api.adapter.out.persistence.InMemoryBonsaiRepository;
import fr.bonsai.api.application.port.out.BonsaiRepository;
import fr.bonsai.api.application.usecase.BonsaiService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public BonsaiRepository bonsaiRepository() {
return new InMemoryBonsaiRepository();
}
@Bean
public BonsaiService bonsaiService(BonsaiRepository repository) {
return new BonsaiService(repository);
}
}
@@ -0,0 +1,27 @@
package fr.bonsai.api.domain.model;
import java.util.UUID;
public class Bonsai {
private final UUID id;
private String name;
private String species;
private int ageYears;
public Bonsai(UUID id, String name, String species, int ageYears) {
this.id = id;
this.name = name;
this.species = species;
this.ageYears = ageYears;
}
public static Bonsai create(String name, String species, int ageYears) {
return new Bonsai(UUID.randomUUID(), name, species, ageYears);
}
public UUID getId() { return id; }
public String getName() { return name; }
public String getSpecies() { return species; }
public int getAgeYears() { return ageYears; }
}