Récapitulons rapidement pourquoi les API REST basées sur CRUD traditionnelles ne sont pas adaptées aux flux de travail complexes :
- Absence de représentation de l'état
- Difficulté à gérer les processus de longue durée
- Aucun support intégré pour les annulations ou les transactions compensatoires
- Capacité limitée à représenter une logique métier complexe
Ces limitations deviennent évidentes lorsque vous essayez de modéliser des processus réels comme la gestion des commandes, les flux de travail d'approbation en plusieurs étapes, ou tout scénario nécessitant de maintenir un état et de gérer les échecs avec élégance.
Introduction aux API REST pilotées par événements
Alors, comment relever ces défis tout en respectant les principes RESTful ? La réponse réside dans l'adoption d'architectures pilotées par événements dans la conception de nos API. Voici comment nous pouvons repenser notre approche :
1. Machines à états orientées ressources
Au lieu de penser en termes d'opérations CRUD, considérez vos ressources comme des machines à états. Chaque ressource peut avoir un ensemble d'états valides et de transitions entre eux.
{
"id": "order-123",
"state": "pending",
"allowedTransitions": ["confirm", "cancel"]
}
Dans ce modèle, les transitions d'état deviennent le principal moyen d'interagir avec les ressources. Vous pouvez exposer ces transitions comme des sous-ressources ou via des actions personnalisées.
2. Opérations asynchrones
Pour les processus de longue durée, implémentez des opérations asynchrones. Lorsqu'un client initie un flux de travail complexe, retournez un statut 202 Accepted avec une ressource représentant le statut de l'opération.
POST /orders/123/fulfill HTTP/1.1
Host: api.example.com
HTTP/1.1 202 Accepted
Location: /operations/456
Le client peut ensuite interroger la ressource de l'opération pour vérifier son statut :
{
"id": "operation-456",
"status": "in_progress",
"percentComplete": 75,
"result": null
}
3. Sourcing d'événements
Implémentez le sourcing d'événements pour maintenir un historique complet des changements d'état. Cette approche permet une meilleure auditabilité et facilite les scénarios de retour en arrière complexes.
{
"id": "order-123",
"events": [
{"type": "OrderCreated", "timestamp": "2023-05-01T10:00:00Z"},
{"type": "PaymentReceived", "timestamp": "2023-05-01T10:05:00Z"},
{"type": "ShippingArranged", "timestamp": "2023-05-01T10:10:00Z"}
],
"currentState": "shipped"
}
4. Retours en arrière basés sur la compensation
Pour les processus en plusieurs étapes, implémentez des retours en arrière basés sur la compensation. Chaque étape du processus doit avoir une action compensatoire correspondante qui peut annuler ses effets.
{
"id": "workflow-789",
"steps": [
{"action": "reserveInventory", "compensation": "releaseInventory", "status": "completed"},
{"action": "chargeCreditCard", "compensation": "refundPayment", "status": "failed"}
],
"currentStep": 1,
"status": "rolling_back"
}
Conseils pratiques pour l'implémentation
Maintenant que nous avons couvert la théorie, examinons quelques conseils pratiques pour implémenter ces concepts :
1. Utilisez des contrôles hypermédias
Exploitez HATEOAS (Hypertext As The Engine Of Application State) pour guider les clients à travers des flux de travail complexes. Incluez des liens vers les actions possibles en fonction de l'état actuel d'une ressource.
{
"id": "order-123",
"state": "pending",
"links": [
{"rel": "confirm", "href": "/orders/123/confirm", "method": "POST"},
{"rel": "cancel", "href": "/orders/123/cancel", "method": "POST"}
]
}
2. Implémentez des webhooks pour des mises à jour en temps réel
Pour les processus de longue durée, envisagez d'implémenter des webhooks pour notifier les clients des changements d'état, plutôt que de les obliger à interroger continuellement pour des mises à jour.
3. Utilisez des clés d'idempotence
Lors de la gestion d'opérations asynchrones, utilisez des clés d'idempotence pour vous assurer que les opérations ne sont pas accidentellement dupliquées en raison de problèmes de réseau ou de nouvelles tentatives du client.
POST /orders/123/fulfill HTTP/1.1
Host: api.example.com
Idempotency-Key: 5eb63bbbe01eeed093cb22bb8f5acdc3
4. Implémentez le modèle Saga pour les transactions distribuées
Pour les flux de travail complexes impliquant plusieurs services, envisagez d'implémenter le modèle Saga pour gérer les transactions distribuées et les retours en arrière.
Pièges potentiels
Avant de vous précipiter pour refactoriser toutes vos API, soyez conscient de ces défis potentiels :
- Complexité accrue dans la conception et l'implémentation de l'API
- Courbe d'apprentissage plus élevée pour les consommateurs de l'API
- Surcharge potentielle de performance due au stockage et au traitement des événements
- Nécessité de mécanismes robustes de gestion des erreurs et de nouvelles tentatives
Conclusion
Concevoir des API REST pour des flux de travail pilotés par événements nécessite un changement de pensée, passant des simples opérations CRUD à une approche plus nuancée qui prend en compte l'état, les processus asynchrones et la logique métier complexe. En adoptant des concepts comme les machines à états, le sourcing d'événements et les retours en arrière basés sur la compensation, nous pouvons créer des API plus robustes et flexibles qui représentent mieux les processus réels.
Rappelez-vous, l'objectif n'est pas de rendre les choses inutilement complexes, mais de créer des API capables de gérer les complexités de votre domaine métier tout en respectant les principes RESTful. Comme pour toute décision architecturale, considérez votre cas d'utilisation spécifique et vos exigences avant de vous lancer.
Maintenant, allez concevoir des API pilotées par événements impressionnantes ! Et si vous vous retrouvez à regretter la simplicité de CRUD... eh bien, il y a toujours GraphQL. Mais c'est une histoire pour un autre jour.
"Le secret pour construire de grandes applications est de ne jamais construire de grandes applications. Divisez vos applications en petits morceaux. Ensuite, assemblez ces morceaux testables et faciles à gérer dans votre grande application."— Justin Meyer
Bon codage !