[JAVA] Axon de cadre de sourcing d'événements CQRS +

Une introduction approximative au CQRS et à la recherche et à l'implémentation d'événements dans Axon

CQRS (Command Query Responsibility Segregation) et source d'événements

Comparaison avec l'architecture CRUD générale

Une architecture CRUD typique interagit avec le système des manières suivantes:

CRUD.png

  1. Refléter les informations obtenues à partir de la source de données dans le modèle et afficher l'interface utilisateur en fonction de celles-ci
  2. L'utilisateur modifie les informations via l'interface utilisateur
  3. Refléter les changements dans le modèle
  4. Le modèle effectue la validation et la logique indirecte
  5. Refléter les changements de modèle dans la source de données

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.

CQRS et sourcing événementiel

L'application du CQRS et de l'approvisionnement d'événements peut prendre la forme suivante: CQRS.png

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.

detailed-architecture-overview (1).png

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.

Commande et événement

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;
    }
}

Modèle de domaine

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.

Écouteur d'événements

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
    }
}

Recommended Posts

Axon de cadre de sourcing d'événements CQRS +
Essayez d'utiliser le Framework Axon