Pod > Guide de développement¶
Ce guide présente les conventions de code du Pod SUMARiS (Java, Spring Boot, JPA/Hibernate, GraphQL), pour un développeur qui rejoint le projet.
Il est rédigé à partir des règles des agents IA du dépôt sumaris-pod (.agents/rules/), qui font foi :
chaque section indique sa règle source, sur GitLab et dans sa copie synchronisée sur ce site.
Pour mettre à jour ces copies et savoir quelles sections relire : npm run site:sync-rules.
Voir aussi : installation de l'environnement, API du Pod, conventions de l'App (entités TypeScript, services GraphQL côté client).
Règles communes¶
- Utiliser les utilitaires du module
sumaris-shared(net.sumaris.core.util) plutôt que du code ad hoc :- dates : uniquement
Dates(Dates.addDays(),Dates.safeParseDate()...), jamaisCalendarniSimpleDateFormat; - collections et chaînes :
CollectionUtils.isEmpty(),ArrayUtils.isEmpty(),StringUtils.isBlank(); - streams :
Beans.getStream(collection), qui accepte une collection nulle.
- dates : uniquement
- Logs avec SLF4J, via l'annotation de classe Lombok
@Slf4j. - Options de configuration lues une seule fois (voir Configuration), jamais dans une boucle.
- Messages destinés à l'utilisateur toujours traduits (voir Internationalisation).
Entités JPA, VO et repositories¶
Chaque entité du modèle se décline en plusieurs classes :
| Classe | Conventions |
|---|---|
| Entité JPA | Lombok @FieldNameConstants, @Getter, @Setter, @EqualsAndHashCode(onlyExplicitlyIncluded = true) ; implémente IEntity<ID> ; table en snake_case, séquence *_seq, clés étrangères *_fk, colonnes nullable = false par défaut |
Value Object (*VO) |
Lombok @Data, @FieldNameConstants ; implémente IValueObject<ID> (au minimum id et updateDate) ; pour chaque relation, le VO lié et son identifiant (otherEntity + otherEntityId) ; tableaux pour les types simples, List<> pour les VO |
*Specifications |
étend IEntitySpecifications<ID, E> ; jointures avec Daos.composePath() / composeJoin() ; champs toujours via Entity.Fields.NOM |
*Repository |
étend SumarisJpaRepository<E, ID, V> et l'interface Specifications |
*RepositoryImpl |
étend SumarisJpaRepositoryImpl<E, ID, V> ; conversions toVO() et toEntity(), en s'appuyant sur les repositories des entités liées |
protected MyEntityRepositoryImpl(EntityManager entityManager) {
super(MyEntity.class, MyEntityVO.class, entityManager);
}
Source :
jpa-entities-and-vos.md(copie)
Services métier¶
*FilterVO(Lombok@Data,@Builder,@FieldNameConstants, implémenteIDataFilter) et*FetchOptions(implémenteIFetchOptions) contrôlent finement les données renvoyées.-
Interface
*Serviceannotée@Transactional, avec les méthodes usuelles :List<E> findByFilter(F filter, Page page, FO fetchOptions); Optional<E> findById(ID id, FO fetchOptions); E save(E source); void delete(ID id); -
Implémentation
@Service("myEntityService"), qui délègue au repository.
Source :
service.md(copie)
API GraphQL et REST¶
- GraphQL : une classe
*GraphQLService(@GraphQLApi) qui délègue au service métier :- lecture :
@GraphQLQuery(name, description)+@Transactional(readOnly = true); - écriture :
@GraphQLMutation(name, description)+@Transactional; - chaque paramètre annoté
@GraphQLArgument(name = "..."); listes filtrées parFilterVOetFetchOptions.
- lecture :
- REST : chemins centralisés dans
RestPaths,produces/consumesexplicites, JSON par des POJO dédiés (Lombok,@JsonInclude(NON_NULL),@JsonPropertysi besoin), sécurité viaAuthService.isLogin()/hasUserProfile(). - Pour l'utilisation de ces API par un client, voir API du Pod.
Source :
graphql-rest-api.md(copie)
Sécurité¶
-
Contrôle d'accès des méthodes par les méta-annotations du paquet
net.sumaris.server.http.security, plutôt que@Secureddirectement :Annotation Profils autorisés @IsAdminadministrateurs @IsSupervisorsuperviseurs et administrateurs @IsUsertout utilisateur authentifié (hors invité) @IsGuesttout utilisateur authentifié, invités inclus -
Contrôle fin (programmes, lieux, données d'autres utilisateurs) avec
DataAccessControlService:checkCanRead(),checkCanWrite(),getAuthorizedProgramIds()... (NO_ACCESS_FAKE_IDSpour « aucun accès »). - Clés publiques ED25519 (
issuer,sender) :SYSTEMpour le système,SystemRecipientEnumplutôt que des chaînes en dur, affichage parCryptoUtils.minifyPubkey().
Source :
security-and-crypto.md(copie)
Options de configuration¶
- Options côté client dans
SumarisConfigurationOption, côté serveur dansSumarisServerConfigurationOption. -
Ne jamais lire une option dans une méthode métier : la lire une fois et la garder dans un champ, rafraîchi quand la configuration change :
@PostConstruct @EventListener({ConfigurationReadyEvent.class, ConfigurationUpdatedEvent.class}) public void onConfigurationReady() { this.enabled = configuration.isMyFeatureEnabled(); }
Source :
configuration-option.md(copie)
Cache¶
- Spring Cache avec JCache (Ehcache) ; une classe
*CacheConfigurationpar module, avec les noms de cache en constantes d'une interface interneNames. - Caches créés avec
Caches.createHeapCache()(et variantes), duréesCacheTTL;@Cacheablesur les lectures coûteuses,@CacheEvictsur les mises à jour.
Base de données (Liquibase)¶
- Auteur d'un changeset : l'adresse e-mail du développeur ; identifiants
<timestamp_ms>-NNN. - Préconditions systématiques (par exemple
tableExistsenonFail="MARK_RAN"). - Tables en
snake_case, séquences*_seq, colonnes de clé étrangère*_fk, contraintes*_fkc. - Un changelog par base (HSQLDB, PostgreSQL, Oracle), avec les mêmes identifiants si possible.
Source :
database-liquibase.md(copie)
Internationalisation¶
I18n.t("sumaris.module.categorie.message", args...)(import statiqueorg.nuiton.i18n.I18n.t), paramètres{0},{1}...- Clés dans
<module>/src/main/resources/i18n/, fichiers*_en_GB.propertieset*_fr_FR.properties.
Tests¶
- JUnit 4 et DBUnit, sur une base HSQLDB gérée par
DatabaseResource. - Jeux de données en XML dans
sumaris-test-shared/src/main/resources; la base de test est générée une fois parInitTests(depuissumaris-core) et doit être régénérée quand ces fichiers changent. - Une
TestConfigurationpar module (DaoTestConfiguration,ServiceTestConfiguration), à garder alignée surApplication.java. - Pour tester une fonctionnalité activable : un test avec la configuration par défaut, un autre avec un fichier de propriétés
dédié (
@TestPropertySource).
Source :
unit-testing.md(copie)
Sujets spécifiques¶
| Sujet | Règle |
|---|---|
Elasticsearch (index, analyseurs, ElasticsearchSpecification) |
elasticsearch.md (copie) |
Fichiers Parquet (@ParquetColumn, repositories) |
parquet-entities.md (copie) |
| Données FishBase / SeaLifeBase | fishbase.md (copie) |
| Usage de GitLab (tickets, branches, MR) | gitlab.md (copie) |