Bevor wir in den Axon-Pool springen, lassen Sie uns erst einmal die Grundlagen klären. Event Sourcing und CQRS sind nicht nur schicke Abkürzungen, die man bei Entwicklertreffen herumwirft (obwohl sie beeindruckend klingen). Es sind mächtige Muster, die die Art und Weise, wie wir Anwendungen entwickeln und skalieren, verändern können.

Event Sourcing: Die Zeitmaschine Ihrer Anwendung

Stellen Sie sich vor, Ihre Anwendung hätte eine eingebaute Zeitmaschine. Genau das macht Event Sourcing. Anstatt nur den aktuellen Zustand zu speichern, wird eine Aufzeichnung aller Änderungen als Ereignisse gespeichert. Es ist wie Git für Ihre Daten – Sie können in der Zeit zurückreisen, Ereignisse erneut abspielen und sogar alternative Zeitlinien erstellen (Zweige, jemand?).

CQRS: Die Atomspaltung der Datenoperationen

CQRS, oder Command Query Responsibility Segregation (versuchen Sie das fünfmal schnell zu sagen), dreht sich um die Trennung von Verantwortlichkeiten. Es teilt die Operationen Ihrer Anwendung in zwei Lager:

  • Commands: Diese ändern Daten (Schreiboperationen)
  • Queries: Diese rufen Daten ab (Leseoperationen)

Durch die Trennung dieser Verantwortlichkeiten können Sie jede Seite unabhängig optimieren. Es ist, als hätte man eine spezialisierte Taskforce für jede Operation – Effizienz in ihrer besten Form.

Axon Framework: Ihr ES/CQRS Sidekick

Die Implementierung von Event Sourcing und CQRS von Grund auf mag so viel Spaß machen wie das Debuggen einer Nullzeiger-Ausnahme. Hier kommt das Axon Framework ins Spiel wie ein koffeinierter Superheld. Es ist ein Java-Framework, das ein robustes Toolkit für die Implementierung dieser Muster bietet, ohne dass Sie sich die Haare raufen müssen.

Axons Superkräfte:

  • Aggregate-Verwaltung
  • Befehls- und Ereignisverarbeitung
  • Ereignisspeicherung
  • CQRS-Unterstützung von Haus aus

Denken Sie an Axon als Ihren persönlichen Trainer für Event Sourcing und CQRS. Es führt Sie durch den Prozess, übernimmt die schwere Arbeit und bewahrt Sie vor Anfängerfehlern.

Loslegen: Axon-Setup in 3... 2... 1...

Lassen Sie uns loslegen. Zuerst müssen wir unser Projekt einrichten. Wir verwenden Spring Boot, denn mal ehrlich, wer liebt Spring Boot nicht?

Schritt 1: Abhängigkeiten

Fügen Sie diese zu Ihrer pom.xml (für Maven-Nutzer) oder build.gradle (für Gradle-Fans) hinzu:


<dependency>
    <groupId>org.axonframework</groupId>
    <artifactId>axon-spring-boot-starter</artifactId>
    <version>4.5.0</version>
</dependency>

Schritt 2: Konfiguration

Das Schöne an Spring Boot ist, dass es die meiste Arbeit für Sie erledigt. Aber lassen Sie uns eine einfache Konfigurationsklasse hinzufügen, um sicherzustellen, dass alles korrekt verdrahtet ist:


@Configuration
public class AxonConfig {
    @Bean
    public CommandBus commandBus() {
        return SimpleCommandBus.builder().build();
    }

    @Bean
    public EventStorageEngine eventStorageEngine() {
        return new InMemoryEventStorageEngine();
    }
}

Dies richtet einen einfachen In-Memory-Ereignisspeicher ein. In einer realen Umgebung würden Sie eine robustere Lösung verwenden, aber das funktioniert, um uns den Einstieg zu erleichtern.

Befehle implementieren

Jetzt, da wir unser Axon-Setup haben, erstellen wir unseren ersten Befehl. Wir verwenden ein einfaches Bankbeispiel, denn Geld ist immer interessant.


public class CreateAccountCommand {
    @TargetAggregateIdentifier
    private final String accountId;
    private final BigDecimal initialBalance;

    // Konstruktor, Getter, etc.
}

Beachten Sie die @TargetAggregateIdentifier-Annotation? Das ist Axons Art zu sagen: "Hey, dieses Feld identifiziert das Ziel-Aggregat."

Befehle wie ein Profi handhaben

Erstellen wir nun ein Aggregat, um diesen Befehl zu verarbeiten:


@Aggregate
public class AccountAggregate {
    @AggregateIdentifier
    private String id;
    private BigDecimal balance;

    @CommandHandler
    public AccountAggregate(CreateAccountCommand command) {
        apply(new AccountCreatedEvent(command.getAccountId(), command.getInitialBalance()));
    }

    @EventSourcingHandler
    public void on(AccountCreatedEvent event) {
        this.id = event.getAccountId();
        this.balance = event.getInitialBalance();
    }
}

Hier die Erklärung:

  • @Aggregate sagt Axon "Diese Klasse ist eine Aggregatwurzel"
  • @AggregateIdentifier markiert das Feld, das dieses Aggregat eindeutig identifiziert
  • @CommandHandler sagt "Verwende diese Methode, um CreateAccountCommand zu verarbeiten"
  • @EventSourcingHandler wird verwendet, um den Zustand des Aggregats basierend auf Ereignissen zu aktualisieren

Ereignisse verarbeiten

Ereignisse sind das Herzstück von Event Sourcing. Erstellen wir einen Ereignis-Handler, um etwas zu tun, wenn ein Konto erstellt wird:


@Component
public class AccountEventHandler {
    @EventHandler
    public void on(AccountCreatedEvent event) {
        System.out.println("Konto erstellt: " + event.getAccountId() + 
                           " mit Guthaben: " + event.getInitialBalance());
        // In einer echten App könnten Sie hier ein Lese-Modell aktualisieren
    }
}

Dieser Handler wird automatisch von Axon erkannt und aufgerufen, wann immer ein AccountCreatedEvent veröffentlicht wird.

Abfragen implementieren

Jetzt, da wir unsere Befehlsseite eingerichtet haben, implementieren wir eine einfache Abfrage, um Kontodetails abzurufen:


public class GetAccountBalanceQuery {
    private final String accountId;
    // Konstruktor, Getter
}

@Component
public class AccountQueryHandler {
    @QueryHandler
    public BigDecimal handle(GetAccountBalanceQuery query) {
        // In einer echten App würden Sie dies aus einem Lese-Modell abrufen
        return BigDecimal.TEN; // Jeder startet mit 10 €, warum nicht?
    }
}

Um diese Abfrage zu verwenden:


@Autowired
private QueryGateway queryGateway;

public void someMethod() {
    BigDecimal balance = queryGateway.query(
        new GetAccountBalanceQuery("account123"), 
        BigDecimal.class
    ).join();
}

Sagas und verteilte Systeme

Sagas in Axon sind wie die Regisseure eines komplexen Stücks. Sie koordinieren langlaufende Geschäftstransaktionen über mehrere Aggregate hinweg. Erstellen wir eine einfache Saga, die auf die Kontoerstellung reagiert:


@Saga
public class AccountManagementSaga {
    @StartSaga
    @SagaEventHandler(associationProperty = "accountId")
    public void handle(AccountCreatedEvent event) {
        // Starten Sie einen Geschäftsprozess
        System.out.println("Prozess für neues Konto starten: " + event.getAccountId());
    }

    @EndSaga
    @SagaEventHandler(associationProperty = "accountId")
    public void handle(AccountClosedEvent event) {
        // Beenden Sie die Saga
        System.out.println("Konto geschlossen, Saga beenden: " + event.getAccountId());
    }
}

Debugging: Wenn Ereignisse aus der Reihe tanzen

Das Debuggen von eventbasierten Systemen kann knifflig sein. Axon Server (Axons optionales, aber leistungsstarkes Ereignisspeicher- und Nachrichten-Routing-Plattform) bietet eine schöne Benutzeroberfläche zum Inspizieren von Befehlen, Ereignissen und Abfragen. Aber auch ohne es können Sie Ihrem Ereignis-Handler Logging hinzufügen:


@EventHandler
public void on(AccountCreatedEvent event) {
    log.info("Konto erstellt: {}", event);
    // Rest Ihrer Handler-Logik
}

Best Practices: Die Fallstricke vermeiden

  1. Halten Sie Aggregate klein: Sie sollten ein einzelnes Geschäftskonzept darstellen.
  2. Seien Sie vorsichtig mit der Ereignisversionierung: Sobald ein Ereignis gespeichert ist, sollte sich seine Struktur nicht ändern.
  3. Verwenden Sie Snapshots für große Ereignisströme: Axon unterstützt dies von Haus aus.
  4. Setzen Sie keine Geschäftslogik in Ereignis-Handler: Sie sollten sich auf die Aktualisierung des Lese-Modells konzentrieren.
  5. Verwenden Sie aussagekräftige Ereignisnamen: UserRegistered ist besser als UserEvent1.

Zusammenfassung: Sie sind jetzt ein Axon-Anwender!

Herzlichen Glückwunsch! Sie haben gerade Ihre ersten Schritte in die Welt von Event Sourcing und CQRS mit dem Axon Framework gemacht. Wir haben die Grundlagen behandelt, aber es gibt noch so viel mehr zu entdecken. Axon bietet eine robuste Grundlage für den Aufbau skalierbarer, ereignisgesteuerter Systeme, die sich mit den Anforderungen Ihres Unternehmens weiterentwickeln können.

Denken Sie daran, mit großer Macht kommt große Verantwortung. Event Sourcing und CQRS sind keine Allheilmittel – sie fügen Komplexität hinzu, die möglicherweise nicht für jede Anwendung erforderlich ist. Aber für die richtigen Anwendungsfälle können sie bahnbrechend sein.

Gehen Sie nun verantwortungsbewusst mit Event Sourcing um! Und wenn Sie sich dabei ertappen, wie Sie mit Ihrer Gummiente über Aggregatgrenzen sprechen, machen Sie sich keine Sorgen – das ist in der Axon-Welt völlig normal.

"In der Welt der ereignisgesteuerten Systeme ist jeder Fehler nur eine Gelegenheit für ein neues Ereignis." - Anonymer Entwickler (wahrscheinlich)

Viel Spaß beim Programmieren, und mögen Ihre Ereignisse immer in der richtigen Reihenfolge sein!