Conception et mise en place d'une plateforme de gestion santé avec architecture modulaire Maven, Dubbo et ElementUI

1.1 Objectifs pédagogiques

Ce guide accompagne la conception et le démarrage d'une solusion de gestion destinée aux établissements de santé. À l'issue de la mise en place, vous serez en mesure de :

  • Identifier les besoins métiers et l'architecture logicielle d'une plateforme de télésuivi ;
  • Utiliser PowerDesigner pour produire un modèle physique de données cohérent ;
  • Construire une application Java découpée en modules Maven et exposatn des services via Dubbo ;
  • Intégrer la bibliothèque de composants ElementUI dans l'interface d'administration.

1.2 Présentation fonctionnelle

La plateforme cible s'adresse aux centres de prévention et aux cabinets souhaitant dématérialiser le suivi de leurs patients. Elle offre les capacités suivantes :

  • Visualisation du parcours de soin et des rendez-vous ;
  • Gestion des adhérents et de leurs dossiers médicaux ;
  • Évaluation automatique des indicateurs de santé ;
  • Planification d'interventions personnalisées ;
  • Mise à disposition d'une base de connaissances médicale.

L'objectif est d'améliorer la productivité des praticiens, de renforcer les échanges avec les patients et de donner aux responsables d'établissement une vision claire de l'activité.

1.3 Stack technique

Le projet repose sur un écosystème Java orienté services. Les briques principales sont :

  • Git : gestion de versions, création de branches, fusion et résolution de conflits, utilisation des tags ;
  • Maven : construction multimodule et gestion centralisée des dépendances ;
  • Spring Framework / Spring MVC : injection de dépendances, couches web et transactionnelles ;
  • MyBatis : mapping objet-relationnel et accès aux données ;
  • Dubbo + ZooKeeper : communication inter-services et registre de services ;
  • Spring Security : authentification et autorisation ;
  • Redis : mise en cache des données fréquemment sollicitées ;
  • ElementUI + Vue.js : interface administrateur responsive ;
  • PowerDesigner : modélisation de la base de données.

1.4 Architecture fonctionnelle

Le système se décompose en deux grandes portées :

  • Portail administrateur : réservé aux gestionnaires et aux praticiens, il permet la configuration, le suivi des adhérents et la production de rapports ;
  • Portail mobile : destiné aux patients, accessible depuis un navigateur ou une application légère, pour consulter les rendez-vous et recevoir des recommandations.

1.5 Méthodologies de développement

Deux approches sont couramment employées :

  • Modèle en cascade : les phases (spécification, conception, développement, tests, mise en production) s'enchaînent de manière linéaire. Il est utile lorsque le périmètre est figé ;
  • Méthodes agiles : le développement se fait par itérations courtes, avec des livraisons incrémentales et un ajustement continu des priorités.
  1. Mise en place de l'environnement

2.1 Découpage modulaire Maven

Le projet est organisé sous la forme d'un projet parent regroupant plusieurs modules enfants. Cette séparation facilite la réutilisation, les déploiements indépendants et la maintenance.

Module Packaging Rôle
sante_parent pom Projet racine : verrouillage des versions de dépendances et agrégation des modules
sante_commun jar Utilitaires, entités, constantes et objets de résultat partagés
sante_interfaces jar Interfaces de service publiées par les fournisseurs et consommées par les clients
sante_service_provider war Implémentations de services, interfaces DAO et mappers MyBatis ; déployé sous Tomcat
sante_backend war Console d'administration : contrôleurs, pages HTML, ressources statiques et fichiers Spring
sante_mobile war Frontal mobile : contrôleurs et pages dédiées aux patients
sante_jobs war/jar Tâches planifiées (Quartz) et traitements batch

2.2 Configuration du projet parent

Le pom.xml parent centralise les versions. Il est important de s'assurer que l'encodage source est bien défini en UTF-8.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>fr.formation.sante</groupId>
    <artifactId>sante_parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>sante_commun</module>
        <module>sante_interfaces</module>
        <module>sante_service_provider</module>
        <module>sante_backend</module>
        <module>sante_jobs</module>
        <module>sante_mobile</module>
    </modules>

    <properties>
        <junit.version>4.13.2</junit.version>
        <spring.version>5.3.21</spring.version>
        <mybatis.version>3.5.9</mybatis.version>
        <mybatis.spring.version>2.0.7</mybatis.spring.version>
        <pagehelper.version>5.3.0</pagehelper.version>
        <dubbo.version>2.7.15</dubbo.version>
        <zookeeper.version>3.4.14</zookeeper.version>
        <curator.version>4.2.0</curator.version>
        <mysql.version>8.0.29</mysql.version>
        <druid.version>1.2.11</druid.version>
        <security.version>5.7.1</security.version>
        <poi.version>5.2.2</poi.version>
        <jedis.version>3.7.1</jedis.version>
        <quartz.version>2.3.2</quartz.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>${zookeeper.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-framework</artifactId>
                <version>${curator.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>${mybatis.spring.version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>${pagehelper.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>${poi.version}</version>
            </dependency>
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>${jedis.version}</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>${quartz.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.3 Module commun

Ce module contient le code partagé entre les autres modules. Il déclare les dépendances effectivement utilisées pour la persistance, la sécurité et les échanges.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>fr.formation.sante</groupId>
        <artifactId>sante_parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>sante_commun</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>
    </dependencies>
</project>

2.4 Module interfaces

Il expose uniquement les contrats de service. Le fait de séparer les interfaces des implémentations permet aux consommateurs de ne dépendre que de ce module léger.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>fr.formation.sante</groupId>
        <artifactId>sante_parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>sante_interfaces</artifactId>
    <dependencies>
        <dependency>
            <groupId>fr.formation.sante</groupId>
            <artifactId>sante_commun</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

2.5 Fournisseur de services

Ce module, packagé en war, héberge les implémentations de services et la couche d'accès aux données. Il s'enregistre auprès de ZooKeeper pour être appelé par Dubbo.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>fr.formation.sante</groupId>
        <artifactId>sante_parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>sante_service_provider</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>fr.formation.sante</groupId>
            <artifactId>sante_interfaces</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>81</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

La configuration Spring/Dubbo (spring-provider.xml) ressemble à ceci :


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns:context="http://www.springframework.org/schema/context">

    <dubbo:application name="sante-provider"/>
    <dubbo:protocol name="dubbo" port="20887"/>
    <dubbo:registry address="zookeeper://192.168.1.50:2181"/>
    <dubbo:annotation package="fr.formation.sante.service"/>

    <context:property-placeholder location="classpath:application.properties"/>
</beans>

La couche MyBatis est configurée dans spring-dao.xml :


<beans xmlns="http://www.springframework.org/schema/beans">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="username" value="root"/>
        <property name="password" value="secret"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/clinic_db?characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:MyBatisConfig.xml"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="fr.formation.sante.dao"/>
    </bean>
</beans>

La gestion des transactions est activée via spring-tx.xml :


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx">

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
</beans>

2.6 Console d'administration

Le module backend consomme les services exposés par le fournisseur. Il expose des contrôleurs REST et intègre Spring Security ainsi que Redis.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>fr.formation.sante</groupId>
        <artifactId>sante_parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>sante_backend</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>fr.formation.sante</groupId>
            <artifactId>sante_interfaces</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>82</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

La configuration Spring MVC (spring-mvc.xml) :


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns:context="http://www.springframework.org/schema/context">

    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes" value="application/json"/>
                <property name="features">
                    <list>
                        <value>WriteMapNullValue</value>
                        <value>WriteDateUseDateFormat</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <dubbo:application name="sante-backend"/>
    <dubbo:registry address="zookeeper://192.168.1.50:2181"/>
    <dubbo:annotation package="fr.formation.sante"/>
    <dubbo:consumer timeout="600000" check="false"/>

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="104857600"/>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>

    <import resource="spring-redis.xml"/>
    <import resource="spring-security.xml"/>
</beans>

2.7 Frontal mobile

Le portail mobile partage la même infrastructure que le backend mais utilise un port différent et une sécurité allégée.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>fr.formation.sante</groupId>
        <artifactId>sante_parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>sante_mobile</artifactId>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>fr.formation.sante</groupId>
            <artifactId>sante_interfaces</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.8 Bonnes pratiques de configuration

  • Vérifiez que l'encodage UTF-8 est appliqué aux sources Maven, aux fichiers de propriétés et aux déclarations XML ;
  • Chaque module doit posséder un rôle unique : ne mélangez pas les entités, les implémentations de service et les contrôleurs dans un même artefact ;
  • Le provider et les consommateurs doivent pointer vers le même registre ZooKeeper ;
  • Préférez les variables de propriétés aux versions en dur dans les POM.
  1. Modélisation de la base avec PowerDesigner

PowerDesigner est un outil de modélisation qui permet de concevoir des modèles de données conceptuels, logiques et physiques. Dans ce projet, il sert à :

  • Dessiner le modèle physique de données (PDM) représentant les tables, colonnes, clés primaires et relations ;
  • Générer automatiquement le script SQL de création de la base ;
  • Effectuer une rétro-ingénierie à partir d'un script SQL existant pour visualiser les liens entre tables.

Points d'attention :

  • Activez l'auto-incrémentation sur les clés primaires techniques ;
  • Évitez les mots réservés SQL (comme ORDER, USER, GROUP) comme noms de tables ;
  • Vérifiez les contraintes de clé étrangère générées et ajustez-les si nécessaire.
  1. Construction de l'interface avec ElementUI

ElementUI est une bibliothèque de composants pour Vue.js 2. Elle permet de construire rapidement une interface d'administration homogène. Pour l'utiliser, chargez les ressources dans vos pages HTML :


<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

4.1 Mise en page avec Container

Le composant el-container permet d'organiser la page en en-tête, barre latérale, zone principale et pied de page.


<div id="app">
  <el-container>
    <el-header>En-tête</el-header>
    <el-container>
      <el-aside width="220px">Menu</el-aside>
      <el-container>
        <el-main>Contenu principal</el-main>
        <el-footer>Pied de page</el-footer>
      </el-container>
    </el-container>
  </el-container>
</div>

<script>
  new Vue({ el: '#app' });
</script>

<style>
  .el-header, .el-footer {
    background-color: #B3C0D1;
    line-height: 60px;
  }
  .el-aside {
    background-color: #D3DCE6;
    line-height: 200px;
  }
  .el-main {
    background-color: #E9EEF3;
    line-height: 590px;
  }
</style>

4.2 Menu de navigation

Le composant el-menu permet de créer une arborescence de navigation.


<el-menu default-active="1">
  <el-submenu index="1">
    <template slot="title">
      <i class="el-icon-location"></i>
      <span>Patients</span>
    </template>
    <el-menu-item index="1-1">Liste des patients</el-menu-item>
    <el-menu-item index="1-2">Ajouter un patient</el-menu-item>
  </el-submenu>
  <el-submenu index="2">
    <template slot="title">
      <i class="el-icon-menu"></i>
      <span>Rendez-vous</span>
    </template>
    <el-menu-item index="2-1">Calendrier</el-menu-item>
    <el-menu-item index="2-2">Statistiques</el-menu-item>
  </el-submenu>
</el-menu>

4.3 Tableau de données

Le composant el-table affiche une liste d'objets et permet d'ajouter des actions personnalisées.


<el-table :data="patients" stripe>
  <el-table-column prop="id" label="ID"></el-table-column>
  <el-table-column prop="nom" label="Nom"></el-table-column>
  <el-table-column prop="email" label="Courriel"></el-table-column>
  <el-table-column label="Actions" align="center">
    <template slot-scope="scope">
      <el-button type="primary" size="mini" @click="modifier(scope.row)">Modifier</el-button>
      <el-button type="danger" size="mini" @click="supprimer(scope.row)">Supprimer</el-button>
    </template>
  </el-table-column>
</el-table>

<script>
  new Vue({
    el: '#app',
    data: {
      patients: [
        { id: 1, nom: 'Durand', email: 'durand@clinic.fr' },
        { id: 2, nom: 'Martin', email: 'martin@clinic.fr' }
      ]
    },
    methods: {
      modifier(ligne) {
        alert('Modification de ' + ligne.nom);
      },
      supprimer(ligne) {
        alert('Suppression de ' + ligne.nom);
      }
    }
  });
</script>

4.4 Pagination


<el-pagination
  @current-change="changerPage"
  :current-page="pageCourante"
  :page-size="10"
  layout="total, prev, pager, next, jumper"
  :total="305">
</el-pagination>

<script>
  new Vue({
    el: '#app',
    data: { pageCourante: 1 },
    methods: {
      changerPage(page) {
        console.log('Page demandée : ' + page);
      }
    }
  });
</script>

4.5 Messages et formulaires

ElementUI fournit des mécanismes de feedback ($message) et de validation de formulaires.


<el-button @click="notifier('success')">Succès</el-button>
<el-button @click="notifier('warning')">Avertissement</el-button>

<el-form ref="formulaire" :model="consultation" :rules="regles" label-width="120px">
  <el-form-item label="Motif" prop="motif">
    <el-input v-model="consultation.motif"></el-input>
  </el-form-item>
  <el-form-item label="Médecin" prop="medecin">
    <el-select v-model="consultation.medecin" placeholder="Choisir un médecin">
      <el-option label="Dr. Lemoine" value="lemoine"></el-option>
      <el-option label="Dr. Petit" value="petit"></el-option>
    </el-select>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="soumettre">Enregistrer</el-button>
  </el-form-item>
</el-form>

<script>
  new Vue({
    el: '#app',
    data: {
      consultation: { motif: '', medecin: '' },
      regles: {
        motif: [
          { required: true, message: 'Le motif est obligatoire', trigger: 'blur' },
          { min: 3, max: 50, message: 'Entre 3 et 50 caractères', trigger: 'blur' }
        ],
        medecin: [
          { required: true, message: 'Veuillez choisir un médecin', trigger: 'change' }
        ]
      }
    },
    methods: {
      notifier(type) {
        this.$message({ message: 'Notification de test', type: type });
      },
      soumettre() {
        this.$refs['formulaire'].validate((valide) => {
          if (valide) {
            alert('Envoi des données');
          } else {
            console.error('Validation échouée');
            return false;
          }
        });
      }
    }
  });
</script>

Étiquettes: Maven dubbo ZooKeeper MyBatis Spring MVC

Publié le 30 juillet à 16h44