terminal Claude Marketplace Browser
SKILL v1.2.0

java-backend

Développement Java/Spring Boot selon les préconisations DDD hexagonal, Spring Boot 4, Java 25. Détecte automatiquement le type de projet et charge les spécificités correspondantes.

Category Development
Author Mathieu Durand
Version 1.2.0

Components

bolt
java-backend Skill
expand_more

java-backend

Développement Java/Spring Boot selon les préconisations Additi (DDD hexagonal, Spring Boot 4, Java 25).

Usage

Ce skill détecte automatiquement le type de projet (API Web ou Worker) et charge les spécificités correspondantes.

/java-backend <description de la tâche>

Ressources complémentaires :

  • API Web (WebMVC) : Voir references/application-web.md pour les spécificités REST
  • Persistance JPA : Voir references/infrastructure-jpa.md pour JPA/Spring Data et Testcontainers

Principes appliqués

  • Architecture hexagonale : séparation stricte domain / application / infrastructure
  • DDD : agrégats, value objects, domain events
  • Spring Boot 4 : configuration par convention, actuator, observability
  • Java 25 : records, sealed classes, pattern matching

Structure de projet attendue

src/
  main/
    java/
      com.example/
        domain/          # Entités, value objects, ports
        application/     # Use cases, command handlers
        infrastructure/  # Adapters, repositories, config
  test/
    java/
      com.example/
        domain/          # Tests unitaires du domaine
        application/     # Tests d'intégration use cases

Conventions

  • Les ports sont des interfaces dans le domaine
  • Les adapters implémentent les ports dans l'infrastructure
  • Les use cases ne dépendent que du domaine
  • Tests unitaires sans Spring context pour le domaine

description Ressources référencées

references/application-web.md expand_more

Application Web (WebMVC)

Spécificités pour les projets exposant une API REST avec Spring WebMVC.

Structure application/

application/
└── rest/
    ├── <Resource>Controller.java   # @RestController
    ├── <Resource>Request.java      # record (validation @jakarta)
    └── <Resource>Response.java     # record

Controller

@RestController
@RequestMapping("/api/v1/commandes")
@Validated
public class CommandeController {

    private final CommandeService commandeService;

    public CommandeController(CommandeService commandeService) {
        this.commandeService = commandeService;
    }

    @GetMapping("/{id}")
    public CommandeResponse getById(@PathVariable UUID id) {
        return commandeService.findById(id)
            .map(CommandeResponse::from)
            .orElseThrow(() -> new CommandeNotFoundException(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public CommandeResponse create(@RequestBody @Valid CommandeRequest request) {
        return CommandeResponse.from(commandeService.create(request.toDomain()));
    }
}

Gestion des erreurs

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CommandeNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ProblemDetail handleNotFound(CommandeNotFoundException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        final var detail = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        detail.setProperty("errors", ex.getBindingResult().getFieldErrors()
            .stream().map(e -> e.getField() + ": " + e.getDefaultMessage()).toList());
        return detail;
    }
}

Configuration actuator

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    health:
      show-details: always
references/infrastructure-jpa.md expand_more

Infrastructure JPA / PostgreSQL

Spécificités pour la persistance avec Spring Data JPA et PostgreSQL.

Dépendances Maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

Configuration

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate
    open-in-view: false
  flyway:
    locations: classpath:db/migration

Entité JPA

@Entity
@Table(name = "commandes")
public class CommandeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @Column(nullable = false)
    private String reference;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private StatutCommande statut;

    // Getters, setters ou record pattern
}

Repository

// Interface dans le domain
public interface CommandeRepository {
    Optional<Commande> findById(UUID id);
    Commande save(Commande commande);
}

// Implémentation dans l'infrastructure
@Repository
public class CommandeJpaRepository implements CommandeRepository {

    private final CommandeJpaSpringRepository springRepository;
    private final CommandeMapper mapper;

    @Override
    public Optional<Commande> findById(UUID id) {
        return springRepository.findById(id).map(mapper::toDomain);
    }
}

Tests avec Testcontainers

@SpringBootTest
@Testcontainers
class CommandeJpaRepositoryTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
}