201 lines
8.7 KiB
Markdown
201 lines
8.7 KiB
Markdown
<!-- file: docs/rules/520-JAVA.md -->
|
|
<!-- version: 4 -->
|
|
|
|
# Profil Java
|
|
|
|
## Rôle
|
|
|
|
Ce document possède les règles réutilisables propres au langage Java.
|
|
|
|
Il complète [`500-LANGUAGES.md`](500-LANGUAGES.md). Les règles de gouvernance, documentation et validation restent possédées par leurs familles générales.
|
|
|
|
Maven et Gradle sont des spécialisations de build distinctes possédées respectivement par [`521-MAVEN.md`](521-MAVEN.md) et [`522-GRADLE.md`](522-GRADLE.md). Un projet Java n'applique que le profil du build tool qu'il utilise réellement.
|
|
|
|
Android n'est pas défini ici. Une contrainte AGP, SDK Android, `minSdk`, `targetSdk`, `versionCode`, NDK, ABI/JNI, `applicationId`, `namespace`, manifest/resources, signing, packaging APK/AAB, device/AVD ou smoke Android appartient au profil plateforme [`610-ANDROID.md`](610-ANDROID.md) ou à un owner spécialisé et peut raffiner localement les règles Java applicables.
|
|
|
|
Une compatibilité JDK plus étroite imposée par une plateforme, un framework ou un build tool reste un override de ce scope plus spécifique. Elle MUST NOT être remontée comme minimum Java général uniquement parce qu'un projet l'exerce.
|
|
|
|
## Politique JDK et compatibilité Java
|
|
|
|
Pour un projet Java général suivant le profil PGR :
|
|
|
|
- le JDK de développement SHOULD être une version LTS ;
|
|
- la dernière LTS compatible disponible dans l'environnement SHOULD être utilisée par défaut ;
|
|
- une LTS plus ancienne MAY être sélectionnée lorsqu'un outil, framework, serveur, CI ou environnement d'exécution l'exige ;
|
|
- une version Java non-LTS SHOULD NOT être introduite sans besoin technique explicite.
|
|
|
|
PGR MUST distinguer :
|
|
|
|
```text
|
|
JDK utilisé pour construire
|
|
≠
|
|
niveau Java du code source
|
|
≠
|
|
niveau de bytecode/runtime minimal supporté
|
|
```
|
|
|
|
Le niveau Java ciblé MUST être déclaré explicitement par le build lorsqu'il affecte la compatibilité. Il ne doit pas dépendre implicitement du JDK actif sur la machine.
|
|
|
|
Un JDK de build plus récent MAY compiler un projet qui cible un niveau source ou bytecode plus ancien lorsque le build exprime explicitement cette cible et que la toolchain choisie la supporte. Le simple fait qu'une compilation réussisse avec le JDK actif ne constitue pas une déclaration de compatibilité.
|
|
|
|
Selon le build, ce contrat peut être matérialisé par une option de release, une toolchain, des niveaux source/target ou un mécanisme équivalent. PGR ne généralise pas un mécanisme particulier à tous les builds Java.
|
|
|
|
Une hausse de la version Java minimale supportée constitue un changement de compatibilité et MUST être justifiée comme telle.
|
|
|
|
PGR MUST NOT imposer une version exacte de JDK uniquement parce qu'un corpus historique ou un autre projet utilise cette version.
|
|
|
|
## Structure source Java
|
|
|
|
Un module Java classique SHOULD conserver la structure reconnue par l'écosystème :
|
|
|
|
```text
|
|
src/
|
|
├── main/
|
|
│ ├── java/
|
|
│ └── resources/
|
|
└── test/
|
|
├── java/
|
|
└── resources/
|
|
```
|
|
|
|
PGR SHOULD NOT remplacer cette convention par une arborescence propriétaire telle que `sources/`, `java-code/` ou `tests-java/` sans contrainte réelle.
|
|
|
|
Les chemins canoniques correspondants sont donc notamment `src/main/java/`, `src/main/resources/`, `src/test/java/` et `src/test/resources/`.
|
|
|
|
Lorsqu'un projet distingue des tests d'intégration, environnementaux ou live, il SHOULD utiliser un source set ou un module explicitement déclaré par le build plutôt que détourner `src/test/`.
|
|
|
|
## Packages et ownership
|
|
|
|
Un package Java représente une responsabilité architecturale, pas seulement un dossier.
|
|
|
|
La racine de package SHOULD être stable et appartenir au projet ou à l'organisation. Les sous-packages SHOULD refléter des responsabilités telles que `config`, `transport`, `store`, `logging` ou `protocol`.
|
|
|
|
Les packages génériques tels que `misc`, `stuff`, `helpers`, `common` ou `util` SHOULD NOT devenir des dépotoirs. Lorsqu'un élément est partagé, son propriétaire architectural doit d'abord être identifié.
|
|
|
|
Un package utilitaire petit et cohérent MAY exister lorsque sa responsabilité est claire.
|
|
|
|
## Nommage des modules
|
|
|
|
Lorsqu'un dépôt multi-langage ou multi-module utilise déjà une nomenclature de rôles commune, les modules Java SHOULD conserver les mêmes suffixes conceptuels afin de rester lisibles à l'échelle du dépôt.
|
|
|
|
Exemples usuels :
|
|
|
|
```text
|
|
foo-api
|
|
foo-lib
|
|
foo-app
|
|
foo-cli
|
|
foo-server
|
|
foo-service
|
|
foo-worker
|
|
foo-tool
|
|
foo-demo
|
|
foo-smoke
|
|
foo-poc
|
|
```
|
|
|
|
Ce vocabulaire est une convention de rôle et non une obligation de créer artificiellement des modules séparés.
|
|
|
|
## Imports
|
|
|
|
Les imports wildcard SHOULD NOT être utilisés :
|
|
|
|
```java
|
|
import foo.*;
|
|
import static foo.Bar.*;
|
|
```
|
|
|
|
Les imports explicites sont préférés afin de conserver l'origine des symboles visible.
|
|
|
|
Les imports statiques SHOULD être évités par défaut dans le code produit lorsqu'une forme qualifiée reste lisible. Une règle de test ou de framework plus spécifique MAY autoriser une convention différente lorsqu'elle est réellement idiomatique et bornée.
|
|
|
|
## Visibilité et API
|
|
|
|
La visibilité minimale appropriée est la règle par défaut :
|
|
|
|
```text
|
|
private
|
|
→ package-private
|
|
→ protected
|
|
→ public
|
|
```
|
|
|
|
Une méthode ou un type MUST NOT devenir `public` uniquement pour faciliter un test.
|
|
|
|
Une interface Java SHOULD exister lorsqu'elle représente une vraie frontière : plusieurs implémentations, contrat consommé indépendamment, adaptation de plateforme, API publique ou autre séparation architecturale réelle.
|
|
|
|
PGR SHOULD NOT généraliser le pattern `Foo` / `FooImpl` lorsqu'une seule implémentation existe sans frontière utile.
|
|
|
|
## Annotations et génération implicite
|
|
|
|
PGR n'interdit pas les annotations en tant que mécanisme Java.
|
|
|
|
Les annotations fonctionnelles faisant partie du contrat officiel d'un framework ou d'une API sont légitimes, par exemple pour la persistance, la validation, l'injection, le routage ou d'autres mécanismes officiellement documentés.
|
|
|
|
En revanche, Lombok et les outils équivalents de convenience/codegen implicite SHOULD NOT être utilisés par défaut lorsqu'ils servent principalement à masquer du boilerplate, générer des constructeurs/getters/setters ou modifier implicitement l'API visible dans la source.
|
|
|
|
Une exception exige une justification explicite et reste attachée au projet ou framework concerné.
|
|
|
|
## Nullabilité
|
|
|
|
`null` SHOULD apparaître uniquement lorsqu'il appartient explicitement au contrat.
|
|
|
|
Une API ne devrait pas laisser le caller deviner si l'absence est possible. Selon le cas, le contrat peut employer :
|
|
|
|
- `Optional<T>` ;
|
|
- une collection vide ;
|
|
- une exception appropriée ;
|
|
- un type résultat plus explicite ;
|
|
- une annotation de nullabilité reconnue par le projet.
|
|
|
|
PGR MUST NOT imposer `Optional` partout. L'objectif est l'explicitation du contrat, pas une cérémonie syntaxique.
|
|
|
|
## Exceptions
|
|
|
|
Java possède un mécanisme natif d'exceptions ; PGR ne recrée pas automatiquement un modèle `Result` autour de toutes les opérations.
|
|
|
|
La distinction checked/unchecked appartient au contrat de la couche concernée.
|
|
|
|
Un `catch` SHOULD exister uniquement lorsque la couche sait réellement traiter, traduire ou enrichir l'erreur. Les captures silencieuses de `Exception` ou `Throwable` sont interdites hors mécanisme de frontière explicitement spécialisé.
|
|
|
|
Lorsqu'une erreur est traduite, sa cause SHOULD être préservée lorsque cette information reste pertinente.
|
|
|
|
Une couche SHOULD exposer ses propres erreurs de contrat plutôt que propager sans contrôle les détails techniques d'une dépendance interne.
|
|
|
|
## Tests Java
|
|
|
|
Les tests comportementaux et structurels gardent des responsabilités distinctes.
|
|
|
|
Les tests unitaires résident normalement sous `src/test/java/`. Les tests d'intégration, environnementaux ou live SHOULD être séparés par source set, module ou tâche lorsque leur exécution possède des préconditions différentes.
|
|
|
|
Les tests MUST NOT :
|
|
|
|
- rendre publique une API uniquement pour l'atteindre ;
|
|
- rechercher des chaînes dans les sources pour simuler un audit d'architecture ;
|
|
- vérifier une prerelease en codant en dur la version courante ;
|
|
- ouvrir `VERSION`, `pom.xml` ou `build.gradle` uniquement pour confirmer leur synchronisation.
|
|
|
|
Les contraintes structurelles appartiennent au build system, au compilateur, à un linter adapté ou à un audit read-only.
|
|
|
|
## Composition avec les build tools
|
|
|
|
Un projet Java construit avec Maven compose normalement :
|
|
|
|
```text
|
|
GENERAL
|
|
+ 500 languages / ecosystems
|
|
+ 520 Java
|
|
+ 521 Maven
|
|
```
|
|
|
|
Un projet Java construit avec Gradle compose normalement :
|
|
|
|
```text
|
|
GENERAL
|
|
+ 500 languages / ecosystems
|
|
+ 520 Java
|
|
+ 522 Gradle
|
|
```
|
|
|
|
Une plateforme ou un framework applicable se compose ensuite au-dessus de ces owners. Le choix d'un build tool ne rend pas l'autre profil applicable.
|