Skip to main content

Architecture d'un module

Introduction

L'architecture d'un module Open ENT v3 repose sur une organisation structurée des packages et répertoires. Cette organisation facilite la maintenance, la compréhension du code et la séparation des responsabilités. Voici une description des principaux packages et répertoires standard dans un module typique en technologie Java/Quarkus, ainsi que leur rôle et contenu.

Organisation des packages

Voici une explication de ce que chaque package contient dans un module Open ENT typique avec les répertoires config, service, resource, dto, et exception :

1. service (Services)

Le package service contient les classes qui implémentent la logique métier de l'application. Ces services sont utilisés pour gérer les interactions entre les différentes couches, telles que les accès aux bases de données ou l'application de règles métier.

  • Exemples de fichiers :
    • UserService.java : Gère la logique métier pour les utilisateurs (par exemple, la création, la mise à jour, la suppression d'utilisateurs).

2. resource (Resources)

Le package resource (ou parfois appelé controller ou endpoint) contient les classes qui exposent les API RESTful de l'application. Ces classes reçoivent les requêtes HTTP et renvoient les réponses au client en fonction des actions réalisées via les services.

  • Exemples de fichiers :
    • UserResource.java : Expose les endpoints REST pour les opérations sur les utilisateurs (ex. GET, POST, PUT, DELETE sur les utilisateurs).

3. dto (Data Transfer Object)

Le package dto contient des classes simples qui sont utilisées pour transférer des données entre les différentes couches de l'application ou entre le backend et le frontend via les API REST. Ces classes encapsulent les données des entités et facilitent leur sérialisation/désérialisation.

  • Exemples de fichiers :
    • UserDTO.java : Un objet contenant les informations sur les utilisateurs à envoyer ou recevoir via les API (ex. nom, email, âge).

4. exception (Exceptions)

Ce package contient les classes d'exception personnalisées qui peuvent être utilisées dans l'application pour gérer les erreurs spécifiques. Cela permet de mieux structurer et capturer les erreurs de manière appropriée, puis de renvoyer des messages ou statuts d'erreur clairs via les API.

  • Exemples de fichiers :
    • UserNotFoundException.java : Exception levée lorsque l'utilisateur recherché n'est pas trouvé.

5. resources (Ressources)

Ce répertoire (qui n'est pas un package, mais un répertoire distinct) contient les fichiers de configuration de l'application, tels que les propriétés de configuration, les fichiers XML, et d'autres ressources statiques ou fichiers de configuration.

  • Exemples de fichiers :
    • application.properties : Contient les paramètres de configuration comme les informations de base de données, les ports de serveurs, etc.

6. config (Configuration)

Ce package contient une classe de configuration pour le module (ayant pour nom généralement le module suivi de Config) permettant d'initialiser les différentes dépendances nécessaires pour les ressources et les services.


Injection de données et Singleton

Les classes resources sont instanciées pour chaque requete alors que les services sont des singletons annotés par @ApplicationScoped

Appel du service dans une resource

    @Inject
private UserService userService;

Déclaration du service avec son annotation

@ApplicationScoped
public class DefaultUserService implements UserService {
}

Tous les singletons d'Open ENT NG non pas été migré en singleton avec @ApplicationScoped.

Ils sont dans ce cas initalisé dans un verticle Vert.x ou initialisé dans une classe avec le suffix ConfigService.

Chaque module contient au moins un Verticle Vert.X ou une classe ConfigService permettant d'initaliser ces singletons.

La création d'un Verticle est gérée par des factories définies dans la classe de base fr.tech.openent.common.vertx.BaseVerticle à partir de laquelle chaque verticle hérite.

info

Dans le cadre du launcher, tous les verticles sont lancés dans une seule JVM. Afin d'éviter que les singletons ne soient initialisés plusieurs fois, la variable de configuration openent.launcher.mode.enabled permet de contrôler l'activation ou la désactivation du lancement des services communs.

Liste des singletons gérés dans les verticles

Certains singletons sont réellement commun à tous les modules. Ils seront alors gérés dans la classe BaseVerticle, d'autres singletons sont specifiques à un module et ils seront alors initialisés dans le verticle du module.

Voici la liste des singletons, leurs fonctions et où il doivent être initialisés

ComposantDescriptionTypeInitialisation
I18nInternationalisationServiceConfiguré dans le le verticle spécifique au module
FileResolverGère la résolution de fichiersServiceConfiguré dans le verticle spécifique au module
EventStoreFactoryFabrique pour le stockage d'événementsFactoryConfiguré dans le verticle de base
UserValidationFactoryFabrique pour valider les utilisateursFactoryConfiguré dans le verticle de base
MongoDbBase de données NoSQLDatabaseConfiguré dans le verticle spécifique au module
Neo4jBase de données de graphesDatabaseConfiguré dans le verticle spécifique au module
info

Les singletons initialisés dans les verticles sont appelés dans les classes par le nom du singleton suivi de la méthode getInstance() :

Exemple :

I18n.getInstance()

Liste des singletons gérés dans le bean de configuration

Le bean configuration (classe avec le suffix ConfigService) disponible dans le package Config permet d'initialiser les différentes dépendances nécessaires pour les ressources et les services (exemple de bean fr.tech.openent.workspace.config.WorkspaceConfigService).

Il permet de mettre à disposition un bean JsonObject contenant les propriétés specifiques au module.

Déclaration du bean de configuation avec son annotation (Exemple pour le module Workspace)

@ApplicationScoped
@Startup
public class WorkspaceConfigService extends BaseConfigService {

private JsonObject config;

@Inject
public WorkspaceConfigService() {
super(DefaultModuleName.WORKSPACE_MODULE_NAME);
this.loadConfig();
}

private void loadConfig() {
this.config = ConfigManager.getQuarkusConfig(WORKSPACE_MODULE_NAME);
}

@Produces
@OpenENT(WORKSPACE_MODULE_NAME)
public JsonObject getConfig() {
return config;
}
}

Le bean config pourra être utilisé par injection dans les autres classes avec l'annotation

@OpenENT(DefaultModuleName.WORKSPACE_MODULE_NAME) JsonObject config

Le bean de configuration permet aussi la création de singleton. Les singletons peuvent dans certains cas être démarré différemment suivant que le module fonctionne dans une JVM unique pour Open ENT avec le launcher ou bien dans une JVM séparé dans le cas d'un module fonctionnant en autonome en tant que micro-service.

Exemple de service pour envoyer les mails démarrés uniquement dans le cas d'un microservice :

    @Produces
@OpenENT("emailSender")
@UnlessBuildProperty(name = "openent.launcher.mode.enabled", stringValue = "true", enableIfMissing = true)
public EmailSender getEmailSender(Vertx vertx, @OpenENT(FEEDER_MODULE_NAME)JsonObject config) {
EmailFactory emailFactory = new EmailFactory(vertx, config);
return emailFactory.getSender();
}

Le service emailSender sera créé dans le launcher pour tous les modules.

Annotation

Par défaut, les beans créés dans les fichiers ConfigService utilisent l'annotation @OpenENT pour qualifier le bean. Cependant, lorsque le bean peut être initialisé avec le constructeur par défaut sans nécessiter de paramétrage particulier, l'annotation n'est pas utilisée.

Le constructeur d'une ressource peut ainsi être défini de la manière suivante :

public WorkspaceResource(Vertx vertx, 
@OpenENT(DefaultModuleName.WORKSPACE_MODULE_NAME) JsonObject config,
@OpenENT("storage") Storage storage,
@OpenENT("workspaceService") WorkspaceService workspaceService,
@OpenENT("shareService") final ShareService shareService,
@OpenENT("pdfGenerator") final PdfGenerator aPdfGenerator,
@OpenENT("timelineHelper") final TimelineHelper timelineHelper,
@OpenENT("folderManager") final FolderManager fm,
@OpenENT("eventHelper") final EventHelper eventHelper) {
// Initialisation de la ressource
}

Ce modèle permet de distinguer clairement les beans initialisés par le ConfigService de ceux directement injectés par le mécanisme d'injection de dépendances de Quarkus.

Exposition des beans entre les différents modules pour le launcher

Pour le launcher, il est nécessaire que les beans annotés par les annotations (Verticle, Resource, ou autre) soient disponibles.

Il est nécessaire d'ajouter le fichier suivant resources/META-INF/beans.xml qui permettra que tous les objets annotés seront disponible dans le launcher qui aggrége les modules

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>

Gestion des paramètres

Chaque module est paramètrable par des fichiers de configuration avec l'extension .properties. Toutes les propriétés peuvent être surchargées par variable d'environnement.