Architecture Technique
Backend : Spring Boot
Spring Boot simplifie considérablement le développement d'applications Java en éliminant la nécessité de configurations XML fastidieuses. Grâce à son mécanisme d'auto-configuration et à ses sreveurs embarqués (Tomcat, Undertow), il permet un déploiement rapide et autonome. L'écosystème Spring offre également une intégration transparente avec des modules de sécurité, de gestion des données et de microservices, garantissant une architecture robuste et évolutive pour les systèmes de gestion de flux de travail.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class WorkflowEngineApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowEngineApplication.class, args);
}
}
@RestController
@RequestMapping("/api/v1/workflows")
class WorkflowController {
@GetMapping("/status")
public String checkEngineStatus() {
return "Moteur de flux de travail opérationnel";
}
}
Cette structure sépare le point d'entrée de l'application du contrôleur REST. L'annotation @RequestMapping au niveau de la classe définit un préfixe d'URI clair, tandis que la méthode checkEngineStatus expose un point de terminaison pour vérifier la santé du service.
Frontend : Vue.js
Vue.js utilise un système de réactivité basé sur des proxies (dans sa version 3) et un DOM virtuel pour optimiser les rendus d'interface. Son approche axée sur les composants permet de découpler la logique métier de la présentation, ce qui est idéal pour construire des tableaux de bord de flux de travail interactifs et complexes.
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Démonstration Vue Workflow</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="workflow-app">
<h2>{{ taskTitle }}</h2>
<p>Statut actuel : {{ isCompleted ? 'Terminé' : 'En cours' }}</p>
<button @click="toggleStatus">Basculer le statut</button>
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const taskTitle = ref('Révision du document technique');
const isCompleted = ref(false);
const toggleStatus = () => {
isCompleted.value = !isCompleted.value;
};
return { taskTitle, isCompleted, toggleStatus };
}
}).mount('#workflow-app');
</script>
</body>
</html>
Cet exemple utilise l'API de Composition de Vue 3. La fonction setup encapsule l'état réactif de la tâche et la logique de mutation, offrant une meilleure organisation du code par rapport à l'API d'options traditionnelle.
Couche de Persistance : MyBatis
MyBatis agit comme une couche de mappage objet-relationnel (ORM) légère. Contrairement aux frameworks ORM complets qui masquent le SQL, MyBatis donne aux développeurs un contrôle total sur les requêtes SQL, ce qui est crucial pour optimiser les requêtes complexes impliquant plusieurs jointures dans les systèmes de flux de travail. Il prend en charge le SQL dynamique, la mise en cache multi-niveaux et s'intègre parfaitement avec Spring Boot via le projet MyBatis-Spring.
Stratégie de Test et Assurance Qualité
Le cycle de développement intègre une phase de test rigoureuse pour garantir la fiabilité du système de gestion. L'approche combine des tests en boîte noire pour valider les fonctionnalités métier et des tests d'intégration pour vérifier les flux de données.
- Validation de l'Authentification : Les scénarios de test vérifient la robustesse du processus de connexion, y compris la gestion des identifiants incorrects, l'expiration des sessions et la validation des champs obligatoires.
- Gestion des Rôles et Permissions : Des cas de test spécifiques assurent que les utilisateurs ne peuvent accéder qu'aux ressources autorisées par leur rôle (RBAC). Les tentatives d'accès non autorisé sont correctement interceptées et renvoient les codes HTTP appropriés.
- Opérations CRUD : Les fonctionnalités de création, lecture, mise à jour et suppression des entités du flux de travail sont testées avec des valeurs limites et des données invalides pour garantir la résilience du système face aux entrées utilisateur imprévues.
Implémentation de l'Authentification et des Intercepteurs
La sécurité de l'API est gérée via des tokens de session et un intercepteur HTTP qui valide chaque requête entrante.
// Contrôleur d'authentification
@SkipAuthentication
@PostMapping("/api/auth/login")
public ResponseEntity<ApiResponse> authenticateUser(@RequestBody LoginRequest req, HttpServletRequest httpRequest) {
UserAccount account = accountService.findByUsername(req.getUsername());
if (account == null || !passwordEncoder.matches(req.getPassword(), account.getPasswordHash())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ApiResponse.error("Identifiants invalides"));
}
String jwt = jwtService.createJwt(account.getId(), account.getUsername(), account.getRole());
return ResponseEntity.ok(ApiResponse.success(jwt));
}
// Service de gestion des tokens
public String createJwt(Long userId, String username, String role) {
Optional<ActiveSession> existingSession = sessionRepository.findByUserIdAndRole(userId, role);
String newJwt = UUID.randomUUID().toString().replace("-", "");
Instant expiry = Instant.now().plus(1, ChronoUnit.HOURS);
if (existingSession.isPresent()) {
ActiveSession session = existingSession.get();
session.setToken(newJwt);
session.setExpiresAt(expiry);
sessionRepository.save(session);
} else {
sessionRepository.save(new ActiveSession(userId, username, role, newJwt, expiry));
}
return newJwt;
}
// Intercepteur de sécurité
@Component
public class SecurityInterceptor implements HandlerInterceptor {
private static final String AUTH_HEADER = "Authorization";
@Autowired
private JwtService jwtService;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
return false;
}
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
if (method.hasMethodAnnotation(SkipAuthentication.class)) {
return true;
}
}
String token = req.getHeader(AUTH_HEADER);
if (token != null && !token.isEmpty()) {
ActiveSession session = jwtService.validateAndGetSession(token);
if (session != null) {
req.setAttribute("currentUserId", session.getUserId());
req.setAttribute("currentUserRole", session.getRole());
return true;
}
}
res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
res.setContentType("application/json;charset=UTF-8");
res.getWriter().write("{\"error\":\"Authentification requise\"}");
return false;
}
}
Le mécanisme d'interception analyse l'en-tête Authorization. Si l'annotation personnalisée @SkipAuthentication est présente sur la méthode cible, la validation est contournée. Sinon, le token est vérifié par rapport à la base de données des sestions actives. En cas d'échec, une réponse JSON standardisée avec un statut 401 est renvoyée au client.
Modèle de Données Relationnel
La base de données sous-jacente stocke les entités du flux de travail. Voici la structure de la table principale des tâches, conçue pour suivre l'état et l'attribution des processus.
CREATE TABLE `workflow_task` (
`task_id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(150) NOT NULL,
`assignee_id` bigint(20) NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'PENDING',
`details` text,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`task_id`),
KEY `idx_assignee` (`assignee_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `workflow_task` (`title`, `assignee_id`, `status`, `details`)
VALUES ('Approbation du budget Q3', 101, 'PENDING', 'Révision des dépenses prévues pour le troisième trimestre.');
INSERT INTO `workflow_task` (`title`, `assignee_id`, `status`, `details`)
VALUES ('Validation du déploiement', 102, 'IN_PROGRESS', 'Vérification des métriques de performance post-déploiement.');