Une introduction approximative au CQRS et à la recherche et à l'implémentation d'événements dans Axon
Une architecture CRUD typique interagit avec le système des manières suivantes:
Cette architecture est simple, polyvalente et largement acceptée. Cependant, dans cette architecture, le comportement du modèle de domaine ne peut pas être bien exprimé car il s'agit d'un dialogue centré sur les données par émission et réception de DTO.
L'application du CQRS et de l'approvisionnement d'événements peut prendre la forme suivante:
En appliquant le CQRS et en séparant clairement le modèle de mise à jour et le modèle de référence, il est possible de transmettre l'intention de l'utilisateur sous forme de commande plutôt que d'interaction centrée sur les données. Par exemple, ce qui pourrait seulement être décrit comme "mettre à jour les informations d'utilisateur" dans l'architecture CRUD peut être exprimé avec une intention plus claire, telle que l'émission d'une commande "changer l'adresse de l'utilisateur". De plus, il sera possible d'exprimer si le changement d'adresse est une correction d'une faute de frappe ou un déménagement. Le modèle de domaine peut exprimer son comportement en traitant des commandes et en générant des événements. De plus, en appliquant l'approvisionnement d'événements, il devient l'état actuel en sauvegardant "ce qui s'est passé (événement) lui-même" au lieu de sauvegarder "l'état du résultat de quelque chose qui se passe" comme CRUD. En plus de pouvoir conserver l'arrière-plan et les raisons de cela, les composants peuvent être connectés de manière lâche via des événements, ce qui le rend hautement extensible.
Axon
Axon est un framework basé sur le CQRS et le sourcing événementiel. La version introduite cette fois est la 2.4.5. L'architecture d'Axon est illustrée dans la figure ci-dessous.
Le changement d'état de l'application commence par Command. Traité par CommandHandler pour modifier l'état de l'objet de domaine (agrégat). Et l'événement de domaine est généré par le changement d'état de l'objet de domaine. Les événements qui se produisent sur les objets de domaine sont conservés dans le magasin d'événements via le référentiel. Les événements sont également distribués via EventBus, où les gestionnaires d'événements mettent à jour la source de données utilisée pour les requêtes et envoient des messages aux systèmes externes.
Prenons un exemple d'une application simple qui enregistre et marque uniquement les tâches et comment l'implémenter dans Axon.
La commande est un objet qui exprime l'intention de l'application et dispose des données nécessaires au traitement basé sur cette intention. L'événement est un objet qui représente ce qui s'est passé dans l'application. Les commandes et événements créés par Todo sont les suivants.
public class CreateToDoItemCommand {
@TargetAggregateIdentifier
private final String todoId;
private final String description;
public CreateToDoItemCommand(String todoId, String description) {
this.todoId = todoId;
this.description = description;
}
public String getTodoId() {
return todoId;
}
public String getDescription() {
return description;
}
}
public class ToDoItemCreatedEvent {
private final String todoId;
private final String description;
public ToDoItemCreatedEvent(String todoId, String description) {
this.todoId = todoId;
this.description = description;
}
public String getTodoId() {
return todoId;
}
public String getDescription() {
return description;
}
}
«@ TargetAggregateIdentifier» indique le champ (ou la méthode) utilisé pour identifier l'instance Aggregate cible. De même, créez une commande et un événement pour marquer la fin.
public class MarkCompletedCommand {
@TargetAggregateIdentifier
private final String todoId;
public MarkCompletedCommand(String todoId) {
this.todoId = todoId;
}
public String getTodoId() {
return todoId;
}
}
public class ToDoItemCompletedEvent {
private final String todoId;
public ToDoItemCompletedEvent(String todoId) {
this.todoId = todoId;
}
public String getTodoId() {
return todoId;
}
}
Le modèle de domaine dans Axon se comporte comme un agrégat qui reçoit une commande, modifie l'état et lui envoie un événement. Si vous implémentez un ToDoItem qui représente un ToDo, ce sera comme suit.
public class ToDoItem extends AbstractAnnotatedAggregateRoot {
@AggregateIdentifier
private String id;
private String description;
private boolean completed;
public ToDoItem() {
}
@CommandHandler
public ToDoItem(CreateToDoItemCommand command) {
apply(new ToDoItemCreatedEvent(command.getTodoId(), command.getDescription()));
}
@CommandHandler
public void markCompleted(MarkCompletedCommand command) {
apply(new ToDoItemCompletedEvent(id));
}
@EventSourcingHandler
public void on(ToDoItemCreatedEvent event) {
this.id = event.getTodoId();
this.desc = event.getDescription();
}
@EventSourcingHandler
public void on(ToDoItemCompletedEvent event) {
this.completed = true;
}
}
ʻAbstractAnnotatedAggregateRoot` fournit des fonctions telles que la persistance des événements, la distribution vers EventBus et l'initialisation des objets de domaine (Aggregate) en fonction du flux d'événements obtenu à partir du magasin d'événements.
Tout d'abord, je veux créer une nouvelle instance de ToDoItem
avec CreateToDoItemCommand
, donc @ CommandHandler
Créez un constructeur avec. En appelant apply () dans le constructeur, ToDoItemCreatedEvent
est émis et conservé dans le magasin d'événements. En outre, l'événement généré est distribué à l'écouteur d'événements intéressé par ToDoItemCreatedEvent
via EventBus.
De même, si vous ajoutez une marque d'achèvement, vous souhaitez générer un ToDoItemCompletedEvent
, alors créez une méthodemarkCompleted
.
Lorsque MarkCompletedCommand
est émis, markCompleted () est appelé pour l'instance ToDoItem avec le flux d'événements appliqué et valorisé chargé depuis le magasin d'événements.
Créez également un gestionnaire d'événements avec @ EventSourcingHandler
pour initialiser l'état de l'instance lors de la création d'une instance ToDoItem.
Vous pouvez facilement effectuer des actions sur les événements en créant un écouteur d'événements. Par exemple, écrivez l'état actuel de ToDoItem dans le DB de référence comme indiqué ci-dessous.
public class ToDoEventListener {
@EventHandler
public void handle(ToDoItemCreatedEvent event) {
//Mettre à jour la base de données pour référence
}
@EventHandler
public void handle(ToDoItemCompletedEvent event) {
//Mettre à jour la base de données pour référence
}
}
Vous pouvez également ajouter une fonction pour notifier l'achèvement comme suit.
public class ToDoEventNotifyListener {
@EventHandler
public void handle(ToDoItemCompletedEvent event) {
//Notifier la fin de la tâche
}
}