Skip to content

Commit cbc598a

Browse files
committed
Le design pattern Command Bus
1 parent 7781eec commit cbc598a

File tree

8 files changed

+85
-9
lines changed

8 files changed

+85
-9
lines changed
27.5 KB
Loading

content/blog/command-bus-design-pattern/bus.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.
Loading

content/blog/command-bus-design-pattern/command-bus.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.
Loading

content/blog/command-bus-design-pattern/event-bus.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

content/blog/command-bus-design-pattern/index.en.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,29 @@ In the next sections, we will speak about the command bus which is often associa
2727

2828
{{< page-link page="command-handler-patterns" >}}
2929

30-
31-
{{< training-link >}}
32-
3330
Now, we will build a command bus following the same architecture that I described in the previous section. The only difference is that the command bus will return void. As commands canmight be handled asynchronously, we don’t want to wait for the result of the command processing.
3431

3532
{{< image src="command-bus.svg" alt="A command bus" >}}
3633

37-
Let’s see the most common middleware used to build a command bus. The first one is probably the “logging middleware”. It helps to make your application observable and it is really useful for bug hunting. Then the “validation middleware” ensures that the command is valid before giving it to the handler. Its purpose is to stop the command processing if data is invalid. It is pretty convenient because it avoids validating them manually. When your application uses a database, the “transaction middleware” wraps the handler execution into a SQL transaction. It makes sure all database changes are done, otherwise it rollbacks the transaction. Finally, the last middleware is responsible for finding and executing the handler that matches the command.
34+
Let’s see the most common middleware used to build a command bus. The first one is probably the “logging middleware”. It helps to make your application observable and it is really useful for bug hunting.
35+
36+
Then the “validation middleware” ensures that the command is valid before giving it to the handler. Its purpose is to stop the command processing if data is invalid. It is pretty convenient because it avoids validating them manually.
37+
38+
{{< training-link >}}
39+
40+
When your application uses a database, the “transaction middleware” wraps the handler execution into a SQL transaction. It makes sure all database changes are done, otherwise it rollbacks the transaction.
41+
42+
Finally, the last middleware is responsible for finding and executing the handler that matches the command.
3843

3944
## The event bus
4045

4146
An event represents something that happened in the application. Unlike a command, an event can be handled by several handlers. Listening to events allows us to enhance existing features or add new ones very quickly. Several teams can listen to the same event to perform additional tasks depending on business needs. It makes applications more evolutive without adding accidental complexity and lets your team work isolated from each other.
4247

4348
**Tip:** I would like to encourage you to mainly use business-oriented events instead of technical ones. They clearly describe what happened from a business point of view. For example, `NewAccountHasBeenCreated` is more understandable than `ResourceCreated` with a resource property equal to 'Account'
4449

45-
Even if the event bus is built the same way as the command bus we don't need all the different middleware. We don’t need the validation middleware because events are generated by the aggregates with value objects. Those objects ensure domain invariant which means they are always valid. We also don't need the transactional middleware because the event will be processed into the transaction begun during the command processing. Moreover, depending on your business needs you may not want to process events into this transaction. Let's take a simple example. After creating an account, an event `AccountCreated` is dispatched, then a welcome email is sent during `AccountCreated` processing. If the email sending fails, do we want to roll back the account creation? Not sure! In that case, you need to speak with your product manager to decide how to process this event.
50+
Even if the event bus is built the same way as the command bus we don't need all the different middleware. We don’t need the validation middleware because events are generated by the aggregates with value objects. Those objects ensure domain invariant which means they are always valid.
51+
52+
We also don't need the transactional middleware because the event will be processed into the transaction begun during the command processing. Moreover, depending on your business needs you may not want to process events into this transaction. Let's take a simple example. After creating an account, an event `AccountCreated` is dispatched, then a welcome email is sent during `AccountCreated` processing. If the email sending fails, do we want to roll back the account creation? Not sure! In that case, you need to speak with your product manager to decide how to process this event.
4653

4754
As I said previously, events are recorded by aggregates and they should be business oriented. I will share with you two ways to dispatch events into the event bus.
4855

@@ -52,7 +59,9 @@ As I said previously, events are recorded by aggregates and they should be busin
5259

5360
**Solution 2:** The other solution is to collect events from the command handler. The handler can return them, and then the “handle command” middleware catches and dispatches them.
5461

55-
**Note:** My previous [blog post](http://arnolanglade.github.io/command-handler-patterns.html) about the command and command handler pattern said that a command handler should return void. Here, the idea is that the command bus should return void only the “handle command” should be aware of the result of the handler execution.
62+
As I mentioned earlier, an event reports something that happened within the application. Events must be dispatched at the end of the transaction. You should not dispatch them until the transaction is committed, otherwise, you may dispatch events that do not make sense if something goes wrong. To illustrate what I said, let’s take the same example as earlier: account creation. If you dispatch an `AccountCreated` event before the transaction is committed and an error occurs after the event has been dispatched, you will send an email to users, but they won’t have an account."
63+
64+
**Note:** My previous [blog post](https://www.arnaudlanglade.com/command-handler-patterns/) about the command and command handler pattern said that a command handler should return void. Here, the idea is that the command bus should return void only the “handle command” should be aware of the result of the handler execution.
5665

5766
I've written a bunch of articles about how to handle a command, validate its data, handle user permissions, and so on. Take a look at these articles:
5867

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
title: Le design pattern Command Bus
3+
date: 2022-09-12
4+
image_credit: rockstaar_
5+
url: command-bus-design-pattern
6+
aliases:
7+
- "command-bus-design-pattern.html"
8+
description: "Explorez le design pattern de bus de commande (command bus) : un guide complet pour comprendre son rôle dans l'architecture logicielle, ses avantages et comment il fonctionne avec le bus d'événements (event bus) pour rendre votre application plus modulaire et évolutive"
9+
keywords: "command bus design pattern,event bus design pattern,software architecture,command bus,event bus,middleware design pattern,bus de command, bus d'évènement,middleware en conception logicielle,gestion des commandes et des événements"
10+
tags: [command-bus, design-patterns, software-architecture]
11+
---
12+
13+
## Qu'est-ce qu'un bus ?
14+
15+
Commençons par les bases, qu'est-ce qu'un bus ? En informatique, un bus est un système qui connecte plusieurs composants et facilite le transfert de données entre eux. Dans le contexte logiciel, ces composants sont appelés middleware. Un middleware reçoit une requête, la traite, puis renvoie une réponse. Comme illustré dans le schéma ci-dessous, le principal avantage de l'utilisation d'un bus est qu’il est facilement personnalisable. Vous pouvez ajouter autant de middlewares que nécessaire.
16+
17+
{{< image src="bus.png" alt="A bus" >}}
18+
19+
Dans les sections suivantes, j'expliquerai ce qu'est le bus de commande (command bus), qui est souvent associé à un bus d'événement (event bus). Bien que l'utilisation d'un bus d'événement ne soit pas obligatoire, nous verrons comment cela améliore la modularité des applications et les rend plus facile à faire évoluer. Le but de ces bus est de router une commande ou un événement vers le(s) gestionnaire(s) approprié(s) (handler). Les événements et les commandes sont des objets conçus pour encapsuler les informations nécessaires pour effectuer une action (une commande) ou pour informer de ce qui s'est produit au sein du système (un événement).
20+
21+
## Le bus de commande (Command Bus)
22+
23+
**Rappel rapide sur le Command & Command Handler :** Une commande représente l'intention d'un utilisateur. Les données portées par la commande doivent être valides. Elle doit être traitée par un seul handler, qui est un “callable” qui effectue toutes les actions nécessaires pour valider le cas d'utilisation.
24+
25+
**Remarque :** Je vous recommande l'article de blog que j'ai écrit sur ces design patterns si vous n'êtes pas familier avec eux. Cela vous aidera à comprendre la suite de cet article :
26+
27+
{{< page-link page="command-handler-patterns" >}}
28+
29+
Maintenant, nous allons construire un bus de commande avec l'architecture décrite dans la section précédente. La seule différence est que le bus de commande retournera void. Comme les commandes peuvent être traitées de manière asynchrone, nous ne pouvons pas prévoir quand le traitement sera terminé donc ne pouvons pas attendre de résultat de la part du bus de commande.
30+
31+
{{< image src="command-bus.png" alt="A command bus" >}}
32+
33+
Regardons les middleware les plus couramment utilisés dans la construction d'un bus de commande. Le plus utilisé et probablement le plus utile est le 'middleware de logging'. Ce middleware rend votre application observable et facilite à la fois le débogage et la chasse aux bugs.
34+
35+
Le 'middleware de validation' assure la validité de la commande avant de la passer au handler et bloque son exécution si les données sont invalides. Cet middleware est assez utile car il évite une validation de la commande manuelle.
36+
37+
{{< training-link >}}
38+
39+
Lorsque votre application utilise une base de données, le 'middleware de transaction' exécute le handler au sein d'une transaction SQL pour s'assurer que toutes les modifications sont correctement enregistrées en base de données. En cas d'erreur, la transaction est annulée.
40+
41+
Enfin, le ‘middleware de recherche de handler’ joue le rôle d'identifier et d'exécuter le handler approprié pour la commande.
42+
43+
## Le bus d'événement (Event Bus)
44+
45+
Un événement correspond à un fait qui s'est produit dans l'application. À la différence d'une commande, un événement peut être pris en charge par plusieurs handlers. Grâce aux événements, il est facile d'améliorer les fonctionnalités existantes ou d'en créer de nouvelles. Les équipes peuvent observer ce qui se passe dans le système et agir en fonction des besoins. Cette approche facilite l'évolution de l'application sans ajouter de complexité accidentelle tout en offrant aux équipes la possibilité de travailler de manière autonome.
46+
47+
48+
**Astuce :** Je vous encourage à vous concentrer principalement sur les événements orientés métier plutôt que sur les événements techniques. Les événements orientés métier fournissent une description claire de ce qui s'est passé du point de vue du métier. Par exemple, `NewAccountHasBeenCreated` est bien plus intuitif que `ResourceCreated` avec une propriété de ressource définie sur 'Compte'.
49+
50+
Même si le bus d'événements est conçu de manière similaire au bus de commandes, tous les middlewares ne sont pas indispensables. Par exemple, le middleware de validation n’est pas nécessaire, car les événements sont générés par des agrégats. Ces agrégats garantissent les invariants (règles métier), ce qui signifie que les événements sont toujours créés avec des données valides.
51+
52+
Le middleware transactionnel n'est pas indispensable, car les événements peuvent être pris en charge dans la transaction ouverte lors du traitement de la commande (cela dépend aussi de la configuration de votre bus). Toutefois, selon vos besoins métiers, vous pourriez choisir de traiter les événements en dehors de cette transaction. Considérons un scénario simple : Après la création d'un compte, un événement `AccountCreated` est dispatché, suivi de l'envoi d'un email de bienvenue lors du traitement de l'événement `AccountCreated`. Si l'envoi de l’email échoue, devrions-nous annuler la création du compte ? Pas sûr ! Dans ce cas, vous devez discuter avec votre chef de produit pour décider comment traiter cet événement.
53+
54+
Maintenant, je vais partager avec vous deux méthodes pour dispatcher des événements dans le bus d'événement.
55+
56+
{{< image src="event-bus.png" alt="A event bus" >}}
57+
58+
**Solution 1:** Les événements peuvent être collectés à partir de votre repository. Si aucune erreur ne s'est produite pendant la persistance de l'agrégat, ils peuvent ensuite être dispatchés.
59+
60+
**Solution 2:** L'autre solution consiste à collecter les événements à partir du command handler. Le handler peut les retourner, puis le middleware ‘middleware de recherche de handler’ les récupère et les dispatche.
61+
62+
Comme je l'ai expliqué plus tôt, un événement informe quelque chose qui s’est passé au sein de l'application. Les événements doivent être dispatchés à la fin de la transaction. Vous ne devriez pas les dispatcher tant que la transaction n’est pas fini parce que si quelque chose tourne mal, vous dispatcherez des événements qui n'ont pas de sens. Pour illustrer ce que je viens de dire, prenons le même exemple qu'auparavant : la création de compte. Si vous dispatchez un événement `AccountCreated` avant que la transaction soit validée et qu'une erreur survient après que l'événement a été dispatché, vous enverrez un email aux utilisateurs, mais ils n'auront pas de compte.
63+
64+
65+
**Note:** Dans mon précédent [article de blog](https://www.arnaudlanglade.com/fr/command-handler-patterns/) sur le pattern des commandes et des command handlers, j'ai dis qu'un command handler devait retourner void. L'idée ici est que seul le bus de commandes doit retourner void. Seul le “handler finder middleware” doit être conscient du résultat de l'exécution du handler.
66+
67+
J'ai écrit plusieurs articles sur comment gérer une commande, valider ses données, gérer les permissions des utilisateurs, etc. Jetez un œil à ces articles :
68+
69+
{{< page-link page="tags/command-bus/" >}}
70+

0 commit comments

Comments
 (0)