Dans le monde de l'architecture microservices moderne, la gestion des processus métier à longue durée est l'un des défis les plus persistants. Qu'il s'agisse d'un pipeline de traitement de commandes, d'un workflow d'approbation complexe ou d'une séquence de gestion de dispositifs IoT, les modèles synchrones classiques de type requête-réponse montrent souvent leurs limites. Ils peinent à gérer les délais d'attente, la persistance de l'état et la résilience face aux pannes transitoires.
Entre en scène Temporal.io, une plateforme open-source pour des applications distribuées et fiables. Bien que Temporal prenne en charge plusieurs langages, le SDK Java offre des avantages uniques pour les développeurs d'entreprise, notamment un typage fort, une intégration avec des écosystèmes familiers comme Spring Boot, et des outils robustes. Cet article explore les patterns spécifiques à Java pour construire des workflows résilients et à longue durée d'exécution à l'aide de Temporal.
L'abstraction fondamentale : Workflow vs Activity
Avant de plonger dans le code, il est crucial de comprendre le changement de modèle mental que Temporal introduit. Dans les architectures orientées services traditionnelles, vous écrivez une séquence d'appels de services. Dans Temporal, vous définissez un Workflow (la machine à états) et des Activities (les fonctions produisant des effets de bord). La logique du Workflow est déterministe et sûre pour la rejouabilité, tandis que les Activities gèrent les opérations d'E/S, telles que les mises à jour de base de données ou les appels API.
Le pattern Java spécifique clé ici est la séparation de la logique. Vos méthodes de Workflow doivent être légères, contenant uniquement le flux de contrôle. Toute la charge de travail lourde est transférée vers les Activities.
Mise en œuvre d'un workflow d'approbation résilient
Regardons un exemple pratique : un processus d'approbation des achats. Ce workflow nécessite d'attendre une entrée utilisateur (approbation/rejet) tout en garantissant que le système reste réactif et conserve son état.
D'abord, définissez l'interface Activity. Cela représente l'action externe.
public interface ProcurementActivities {
@ActivityMethod
void submitOrderForApproval(String orderId, double amount);
@ActivityMethod
void processApprovedOrder(String orderId);
@ActivityMethod
void rejectOrder(String orderId, String reason);
}
Maintenant, implémentez le Workflow. En Java, vous étendez WorkflowInterface et utilisez Workflow.newChildWorkflowOptions() pour les workflows imbriqués ou Workflow.await() pour les signaux. Un signal permet au workflow de se mettre en pause et d'attendre une entrée externe.
@WorkflowInterface
public interface ProcurementWorkflow {
@WorkflowMethod
void processOrder(String orderId, double amount);
@SignalMethod
void approve(String orderId);
@SignalMethod
void reject(String orderId, String reason);
}
@WorkflowImplementation
public class ProcurementWorkflowImpl implements ProcurementWorkflow {
private final ProcurementActivities activities = Workflow.newActivityStub(
ProcurementActivities.class,
Workflow.newActivityOptions(
ActivityOptions.newBuilder()
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
.build()
)
);
private volatile boolean approved = false;
private String rejectionReason = null;
@Override
public void processOrder(String orderId, double amount) {
activities.submitOrderForApproval(orderId, amount);
// Attendre indéfiniment un signal
Workflow.await(() -> approved || rejectionReason != null);
if (approved) {
activities.processApprovedOrder(orderId);
} else {
activities.rejectOrder(orderId, rejectionReason);
}
}
@Override
public void approve(String orderId) {
this.approved = true;
}
@Override
public void reject(String orderId, String reason) {
this.rejectionReason = reason;
}
}
Patterns Java clés pour la production
Lors de la mise en œuvre de ces patterns en Java, gardez trois meilleures pratiques à l'esprit :
- Le déterminisme est non négociable : N'utilisez jamais
System.currentTimeMillis()ou de générateurs de nombres aléatoires à l'intérieur de la méthode Workflow. Utilisez plutôtWorkflow.currentTimeMillis(). La logique du workflow rejoue depuis le début à chaque invocation pour garantir la cohérence. - Sécurité des threads : Puisque les workflows peuvent rejouer, l'état partagé doit être géré avec soin. Le mot-clé volatile dans l'exemple ci-dessus assure la visibilité entre les threads, mais en général, vous devez traiter l'état du Workflow comme immuable ou strictement contrôlé.
- Gestion des exceptions : Laissez les Activities lancer des exceptions en cas d'échec. Le Workflow peut intercepter ces exceptions et mettre en œuvre une logique de nouvelle tentative ou des chemins de repli, gardant ainsi votre logique métier propre et ciblée.
Conclusion
Les développeurs Java disposent d'une boîte à outils puissante pour construire des processus métier à longue durée avec Temporal.io. En découplant la gestion de l'état des E/S et en tirant parti de la rejouabilité déterministe, vous pouvez construire des systèmes qui sont non seulement résilients, mais aussi observables et débogables. À mesure que vos processus métier deviennent plus complexes, le SDK Java de Temporal fournit l'intégrité structurelle nécessaire pour évoluer sans sacrifier la fiabilité. Commencez petit, définissez des Activities claires, et laissez le framework gérer l'orchestration.