[JAVA] CQRS + Event Sourcing Framework Axon

Eine grobe Einführung in CQRS und Event Sourcing und Implementierung in Axon

CQRS (Command Query Responsibility Segregation) und Event Sourcing

Vergleich mit der allgemeinen CRUD-Architektur

Eine typische CRUD-Architektur interagiert auf folgende Weise mit dem System:

CRUD.png

  1. Reflektieren Sie die aus der Datenquelle erhaltenen Informationen im Modell und zeigen Sie die darauf basierende Benutzeroberfläche an
  2. Der Benutzer ändert die Informationen über die Benutzeroberfläche
  3. Reflektieren Sie die Änderungen im Modell
  4. Das Modell führt eine Validierung und indirekte Logik durch
  5. Modelländerungen in der Datenquelle widerspiegeln

Diese Architektur ist einfach, vielseitig und allgemein anerkannt. In dieser Architektur kann das Verhalten des Domänenmodells jedoch nicht gut ausgedrückt werden, da es sich um einen datenzentrierten Dialog handelt, bei dem DTO gesendet und empfangen wird.

CQRS und Event Sourcing

Die Anwendung von CQRS und Event Sourcing kann folgende Form annehmen: CQRS.png

Durch Anwenden von CQRS und klare Trennung von Aktualisierungsmodell und Referenzmodell ist es möglich, die Absicht des Benutzers als Befehl und nicht als datenzentrierte Interaktion zu vermitteln. Zum Beispiel kann das, was in der CRUD-Architektur nur als "Benutzerinformationen aktualisieren" bezeichnet werden kann, mit einer klareren Absicht ausgedrückt werden, z. B. durch Ausgeben eines Befehls "Benutzeradresse ändern". Außerdem kann ausgedrückt werden, ob die Adressänderung eine Korrektur eines Tippfehlers oder eine Bewegung ist. Das Domänenmodell kann sein Verhalten durch Verarbeiten von Befehlen und Generieren von Ereignissen ausdrücken. Durch Anwenden der Ereignisbeschaffung wird der aktuelle Status durch Speichern von "Was ist passiert (Ereignis) selbst" anstatt des Status "des Ergebnisses eines Ereignisses" wie CRUD. Neben der Möglichkeit, den Hintergrund und die Gründe dafür beizubehalten, können die Komponenten über Ereignisse lose miteinander verbunden werden, wodurch sie in hohem Maße erweiterbar sind.

Axon

Axon ist ein Framework, das auf CQRS und Event Sourcing basiert. Die diesmal eingeführte Version ist 2.4.5. Die Architektur von Axon ist in der folgenden Abbildung dargestellt.

detailed-architecture-overview (1).png

Die Statusänderung für die Anwendung beginnt mit Befehl. Wird von CommandHandler verarbeitet, um den Status des Domänenobjekts (Aggregat) zu ändern. Das Domänenereignis wird durch die Statusänderung des Domänenobjekts generiert. Ereignisse, die für Domänenobjekte auftreten, werden über das Repository im Ereignisspeicher gespeichert. Ereignisse werden auch über EventBus ausgelöst, wo Ereignishandler die für Abfragen verwendete Datenquelle aktualisieren und Nachrichten an externe Systeme senden.

Lassen Sie uns sehen, wie Sie es in Axon mithilfe einer einfachen Anwendung implementieren, die nur Aufgaben als Beispiel registriert und markiert.

Befehl und Ereignis

Befehl ist ein Objekt, das die Absicht für die Anwendung ausdrückt und über die Daten verfügt, die für die Verarbeitung basierend auf dieser Absicht erforderlich sind. Ereignis ist ein Objekt, das darstellt, was in der Anwendung passiert ist. Die von Todo erstellten Befehle und Ereignisse lauten wie folgt.

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" gibt das Feld (oder die Methode) an, mit dem die Zielaggregatinstanz identifiziert wird. Erstellen Sie auf ähnliche Weise einen Befehl und ein Ereignis, um den Abschluss zu markieren.

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

Domänenmodell

Das Domänenmodell in Axon verhält sich wie ein Aggregat, das einen Befehl empfängt, den Status ändert und ein Ereignis an ihn ausgibt. Wenn Sie ein ToDoItem implementieren, das ein ToDo darstellt, sieht es wie folgt aus.

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 bietet Funktionen wie Ereignispersistenz, Versand an EventBus und Initialisierung von Domänenobjekten (Aggregat) basierend auf dem aus dem Ereignisspeicher erhaltenen Ereignisstrom.

Zuerst möchte ich eine neue Instanz von "ToDoItem" mit "CreateToDoItemCommand" erstellen, also "@ CommandHandler" Erstellen Sie einen Konstruktor mit. Durch Aufrufen von apply () im Konstruktor wird "ToDoItemCreatedEvent" ausgegeben und im Ereignisspeicher beibehalten. Außerdem wird das generierte Ereignis über EventBus an den an "ToDoItemCreatedEvent" interessierten Ereignis-Listener gesendet. Wenn Sie eine Abschlussmarke hinzufügen, möchten Sie "ToDoItemCompletedEvent" auslösen. Erstellen Sie daher eine "markCompleted" -Methode. Wenn "MarkCompletedCommand" ausgegeben wird, wird markCompleted () für die ToDoItem-Instanz aufgerufen, in der der aus dem Ereignisspeicher geladene Ereignisstrom angewendet und der Wert festgelegt wird.

Erstellen Sie außerdem einen Ereignishandler mit "@ EventSourcingHandler", um den Status der Instanz beim Erstellen einer ToDoItem-Instanz zu initialisieren.

Ereignis-Listener

Sie können problemlos Aktionen für Ereignisse ausführen, indem Sie einen Ereignis-Listener erstellen. Schreiben Sie beispielsweise den aktuellen Status von ToDoItem wie unten gezeigt in die Referenz-DB.

public class ToDoEventListener {
 
    @EventHandler
    public void handle(ToDoItemCreatedEvent event) {
        //Aktualisieren Sie die Datenbank als Referenz
    }
 
    @EventHandler
    public void handle(ToDoItemCompletedEvent event) {
        //Aktualisieren Sie die Datenbank als Referenz
    }
}

Sie können auch eine Funktion hinzufügen, um den Abschluss wie folgt zu benachrichtigen.

public class ToDoEventNotifyListener {
    @EventHandler
    public void handle(ToDoItemCompletedEvent event) {
        //Benachrichtigen Sie den Abschluss von ToDo
    }
}

Recommended Posts

CQRS + Event Sourcing Framework Axon
Versuchen Sie es mit dem Axon Framework