Aufsetzten eines CQRS Projektes mit dem Axon-Framework

Eingetragen bei: Java | 0

Das Axon-Framework ist eine Implementierung des CQRS – Command Query Responsiblity Segregation – Pattern, welches zur Erstellung von skalierbaren und erweiterbaren Software-Architekturen dient. Zum Einsatz kommen hier Commands, Events und Querys, die in verschiedenen Bereichen der Software-Architektur verwendet werden.

Will der Benutzer über die UI mit dem System interagieren, stehen zunächst nur Commands zur Verfügung, die mit Hilfe von einem CommandBus ausgeführt werden. Für die Darstellung von Informationen werden wiederum Querys verwendet, die unveränderbare DTOs – Data Transfer Objects – liefern. Für diesen Teilbereich sind im Axon-Framework keine Implementierungen verfügbar, diese müssen aufgrund verschiedenster Einsatzbereiche selbst erstellt werden.

Die Commands werden in den CommandHandlern behandelt, die sich in einem von der UI getrennten Bereich der Software-Architektur befinden.

Hier können nun entweder direkt Events über einen EventBus verbreitet werden, wobei im Gegensatz zu Commands mehrere EventHandler auf ein Event reagieren können. Die Alternative zur direkten Verwendung von Events ist die Benutzung von Aggregates. Diese können entweder neu erzeugt und anschließend in einem Repository gespeichert werden oder es wird ein bestehendes Aggregate aus einem Repository geladen. Erst nachdem alle Operationen auf einem Aggregate ausgeführt wurden, werden die dazugehörigen Events veröffentlicht und von den EventHandlern verarbeitet. Dies passiert automatisch, sofern das Aggregate aus einem Repository geladen wurde, bzw. sobald ein neu erzeugtes Aggregate gespeichert wurde.

Die EventHandler sind unter anderem für die Speicherung von Daten in einer Datenbank zuständig. Dem Benutzer werden über diesen Weg allerdings keine Daten angezeigt, sondern lediglich manipuliert. Zum Anzeigen von Datenbank-Inhalten werden die anfangs erwähnten Querys und DTOs verwendet.

Kommen wir nun zu einem Beispiel, bei dem zunächst ein Benutzer angelegt und in einem zweiten Schritt manipuliert werden. Es wird hierbei ein UserAggregate eingesetzt, d.h. die entsprechenden CommandHandler Arbeiten nicht direkt mit Events, sondern verwenden stattdessen das Domain Model.

Zunächst einmal wird folgende Dependency benötigt:

<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>1.0</version>
</dependency>

Für die Integration des Axon-Frameworks in die Anwendung wird in diesem Beispiel Spring verwendet, wobei außer den Spring Bibliotheken keine weiteren Abhängigkeiten benötigt werden.

Hierfür wird der AnnotationCommandHandlerBeanPostProcessor zur Erkennung von Java-Beans mit der CommandHandler-Annotation, der CommandBus und zwei CommandHandler im Spring Kontext registriert. Letztere dienen zum Anlegen bzw. Aktualisieren des Benutzers.

<bean class=“org…AnnotationCommandHandlerBeanPostProcessor“ />

<bean id=“commandBus“ class=“org…SimpleCommandBus“/>
<bean id=“createHandler“ class=“..CreateUserHandler“ />
<bean id=“updateHandler“ class=“..UpdateUserHandler“ />

Der CreateUserHandler erzeugt zunächst ein neues UserAggregate, führt auf diesem die save-Operation aus und speichert es im UserRepository. Events die beim Erzeugen und innerhalb der save-Operation generiert werden, werden erst publiziert wenn das UserAggregate dem Repository mittels add hinzugefügt wird. Es folgt die Implementierung des CreateUserHandlers, der auf das CreateUserCommand reagiert:

@Component
public class CreateUserHandler {

@Autowired
private UserRepository userRepository;

@CommandHandler
public void handle(CreateUserCommand command) {
UserAggregate userAggregate = new UserAggregate(command.getUser());
userAggregate.save(command.getUser());
userRepository.add(userAggregate);
}
}

Das CreateUserCommand wird auf dem CommandBus ausgeführt, welcher mit Hilfe der Inject-Annotation in die gewünschte Klasse injiziert wird. Das Axon-Framework sorgt dafür, dass der gewünschte CommandHandler mit dem Command  als Übergabe-Parameter aufgerufen wird. Zum Ausführen des Commands wird folgende Zeile benötigt:

commandBus.dispatch(new CreateUserCommand(new User()));

Nun wird das UserAggregate erzeugt, dass von der Klasse AbstractEventSourcedAggregateRoot erbt. Wird ein UserAggregate erzeugt, wird gleichzeitig ein AggregateIdentifier angelegt, anhand dessen das Aggregate beim Speichern im UserRepository identifiziert wird. Jede Änderung des Aggregates die im Repository gespeichert wird, erhält eine neue Version, die von der eigenen Repository Implementierung gespeichert werden kann. Neben der Erzeugung des Identifiers wird im Konstruktor ein Event angelegt, dass in der zu implementierenden handle-Methode behandelt wird. Die Events, die in diesem Beispiel verwendet werden, erben alle vom DomainEvent, welches hier als Übergabe-Parameter dient.

Im CommandHandler wurde weiterhin die save-Methode aufgerufen, auch diese wird hier implementiert, wobei ein weiteres Event registriert wird. Hier nun der Beispiel-Code:

public class UserAggregate extends AbstractEventSourcedAggregateRoot {

public UserAggregate(User user) {
super(new UserAggregateIdentifier(user.getDsn()));
apply(new UserCreatedEvent(user));
}

@Override
protected void handle(DomainEvent event) {
// do what you have to do with the created user
}

public void save(User user) {
registerEvent(new UserSaveEvent(user));
}
}

Wie anfangs beschrieben, werden die Events erst ausgeführt, sobald das Aggregate im Repository gespeichert wird.

Folgendermaßen kann nun das UserRepositorys aussehen:

public class UserRepository extends AbstractRepository{

@Override
protected UserAggregate doLoad(AggregateIdentifier aggregateIdentifier, Long version) {
// load aggregate from a map for example
}

@Override
protected void doSave(UserAggregate aggregate) {

// save aggregate in a map for example
}
}

Im CommandHandler wird beim Aufruf der add-Methode des Repositorys die abstrakte doSave-Methode ausgeführt und das Aggregate gespeichert. Anschließend werden die registrierten Events ausgeführt. Hierbei wird das UserCreateEvent im Aggregate selber behandelt, dass UserSaveEvent aber im folgenden SaveEventListener, welcher lediglich zum Speichern des im Event befindlichen Users in der Datenbank zuständig ist:

@Component
public class SaveEventListener {

@Autowired
private HibernateTemplate hibernateTemplate;

@EventHandler
public void handle(UserSaveEvent event) {
hibernateTemplate.saveOrUpdate(event.getUser());
}
}

Für die Behandlung von Events sind folgende Spring-Konfigurationen vorzunehmen:

<bean class=“class=“org..AnnotationEventListenerBeanPostProcessor“ />
<bean id=“eventBus“ class=“org..SimpleEventBus“ />
<bean id=“updateListener“ class=“..SaveEventListener“ />

Interessant dürfte noch die Umsetzung eines UpdateUserHandler sein, da hierfür ein bestehendes Aggregate aus dem UserRepository geladen wird:

@Component
public class UpdateUserHandler {

  @Autowired
  private UserRepository userRepository;

  @CommandHandler
  public void handle(UpdateUserCommand command) {
    UserAggregate userAggregate = userRepository.load(new UserAggregateIdentifier(command.getIdentifier()), command.getVersion());
    userAggregate.save(command.getUser());
  }
}

Nachdem die save-Operation mit aktualisierten Benutzer-Daten aufgerufen wurde, muss das Aggregate nicht erneut im Repository gespeichert werden. Das Axon-Framework erkennt automatisch, dass es sich hierbei um eine neue Version des geladenen UserAggregates handelt und führt nach vollständigem Durchlauf des CommandHandlers alle registrierten Events aus.

In diesem Fall wird erneut das UserSaveEvent aufgerufen, der entsprechende Listener ausgeführt und damit die Änderung des Benutzers in der Datenbank gespeichert.

Dieses Beispiel soll einen Einblick in die Funktionsweise von CQRS und das Axon-Framework liefern, wobei einige Teilbereiche, wie z.B. der EventStore, ungenannt blieben. Ein Blick in den Reference-Guide sei allemal empfohlen.

Migration eines Eclipse RCP Projektes auf 4.x

Eingetragen bei: Eclipse, Java, RCP | 0

Zur Zeit liegt das Eclipse SDK in Version 4.1 zum Download unter http://www.eclipse.org/eclipse4/ bereit. Im Gegensatz zum Early Adopter Release (Version 4.0) verwendet die Version 4.1 die neuere Indigo Update Seite, die seit dem 22 Juni 2011 zur Verfügung steht. Der Vorgänger verwendete hier noch die Update Seite von Helios.

Für die Migration eines bestehenden RCP Projektes auf das Eclipse SDK 4.1 habe ich mich dazu entschieden, dass Produkt Plugin komplett zu ersetzten, um die Vorteile von Eclipse 4.x direkt verwenden zu können. Insgesamt besteht das Projekt aus 13 reinen Plugin Projekten und weiteren 3 Projekten, die sowohl dem Server als auch dem Client zur Verfügung stehen. Hierbei handelt es sich um Maven Projekte, das Manifest wird mit dem Maven Plugin Felix erstellt. Weiterhin wird in den Client Projekten Spring DM eingesetzt, die OSGI Services werden hierüber veröffentlicht.

Nachdem das Eclipse SDK installiert ist fällt einem neben dem veränderten Aussehen vor allem die Geschwindigkeit auf, mit der sich nun die Plugins installieren lassen. Selbst die Installation von größeren Plugins wie WTP gehen extrem schnell vonstatten. Nach der Installation der benötigten Plugins (Eclipse e4 Tools, Subclipse, WTP, Maven, Maven Extras) werden zunächst alle Projekte in den aktuellen Workspace ausgecheckt, mit Ausnahme von der Target Plattform und dem Produkt Plugin.

Schnell ist ein neues Projekt angelegt, welches als neues Target dienen soll. Hier werden nun die benötigten neuen Eclipse SDK 4.1 Plugins rein kopiert und natürlich alle anderen verwendeten OSGI Libraries. Nun fügt man dem Projekt noch die Target Definition hinzu und konfiguriert sie entsprechend. Nachdem nun die von Eclipse verwendete Target Plattform auf das neu angelegte Target gewechselt wurde, dürften die Compile-Fehler in den Projekten verschwunden sein.

Anschließend kann das neue Produkt Plugin erstellt werden, hierbei handelt es sich zunächst um ein ganz normales „Plug-in Project“, welches als OSGI Framework Equinox verwendet. Öffnet man nun das Manifest kann man mit einem Klick auf Extensions den entsprechenden Reiter sichtbar machen. Nun fügt man als Abhängigkeit org.eclipse.equinox.app hinzu und setzt das Häkchen bei „This plug-in is a singleton“. Anschließend fügt man die Extension org.eclipse.core.runtime.products
hinzu und trägt bei ID product ein. Dem Feld wird nun ein Product hinzugefügt mit der Application org.eclipse.e4.ui.workbench.swt.E4Application und einem beliebigen Namen. Dem Product wiederum wird die Property appName mit einem entsprechenden Namen hinzugefügt. Es wird noch eine zweite Property mit dem Namen applicationXMI benötigt, mit dem Wert <Projektname>/Application.e4xmi. Die entsprechende Datei existiert zum jetzigen Zeitpunkt noch nicht, wird aber im folgenden Schritt angelegt.

Dem Projekt wird nun ein neues Application Model hinzugefügt. Dies ist nur möglich, wenn vorher die Eclipse e4 Tools erfolgreich installiert worden sind. Als Container wird das Produkt Projekt angegeben, der Dateiname Application.e4xmi sollte beibehalten werden.

In dem sich nun öffnenden Editor fügt man unter Windows ein Trimmed Window hinzu, hier kann der Titel und die gewünschte Fenster-Größe definiert werden. Nun benötigt das Projekt noch eine Product-Configuration. Sobald sich der entsprechende Editor geöffnet hat, fügt man neben den eigenen Projekten noch folgende Abhängigkeiten hinzu:

  • org.eclipse.equinox.ds
  • org.eclipse.equinox.event
  • org.eclipse.e4.ui.workbench.renderers.swt

Nun kann das neu erstellte Produkt zum ersten Mal gestartet werden, es wird sich ein leeres Fenster in der gewünschten Größe mit dem angegebenen Titel öffnen.

Will man nun noch Spring DM verwenden, muss anschließend noch das Bundle org.springframework.osgi.extender gestartet werden. Werden jetzt die eigenen Bundle gestartet, erkennt Spring DM – sofern vorhanden – die XML-Konfigurationsdateien im META-INF Ordner und fährt für die jeweiligen Bundle den Spring Context hoch.

Jetzt erst wird die erste Java Klasse im Product Projekt erstellt, die bisher benötigten Standard Implementierungen von

  • ActionBarAdvisor
  • WorkbenchAdvisor
  • WorkbenchWindowAdvisor
  • IApplication
  • IPerspectiveFactory

sind endlich nicht mehr nötig.

Stattdessen kann direkt mit der Implementierung von Funktionalitäten begonnen werden, für ein simples Beispiel reicht zunächst einmal ein ExitHandler zum Beenden der Workbench aus.

public class ExitHandler {

@Execute
public void execute(IWorkbench workbench) {
workbench.close();
}

}

Die Workbench selber, wird dem ExitHandler hierbei von OSGI per Dependency Injection übergeben. Es können beliebige weitere Parameter verwendet werden, wie z.B. in Spring DM exportierte OSGI Services oder andere OSGI Komponenten. Kann OSGI die entsprechenden Klassen nicht injizieren kommt es zu einer RuntimeException.

Um den ExitHandler zu verwenden müssen nun noch ein paar Einstellungen in der Application.e4xmi vorgenommen werden.  Zunächst wird ein Command erstellt mit der Id app.exit und dem Namen Exit. Jetzt wird ein Handler erstellt, der das gerade erstellte Command verwendet und der auf den ExitHandler als Implementierung verweist.

In dem Trimmed Window kann anschließend ein Main Menu erzeugt werden, dieses wiederum erhält ein Menu und dieses schlussendlich ein HandledMenuItem, welches das Exit Command Aufruft.

Natürlich soll das Applikations-Fenster entsprechenden Inhalt bekommen. Hierzu wird, wie in der Vorgänger Version, eine View benötigt. Mittlerweile muss diese aber nicht von ViewPart Erben, ein ganz normales POJO reicht als View aus. Lediglich über dem Konstruktor wird eine @Inject Annotation benötigt, so dass das Parent Composite vom OSGI Framework übergeben werden kann.

  @Inject
public MyView(Composite parent) {
// do what you have to do – something with ui for example
}

Um die View zu verwenden muss auch diesmal eine Anpassung in der Application.e4xmi vorgenommen werden. Unterhalb des Trimmed Window befindet sich der Punkt Controls, hier kann nun ein Part Stack erstellt werden. Dieser bekommt wiederum einen Part der die soeben erstellte MyView Klasse zur Ausführung erhält.

Dieser Artikel soll einen kurzen Einblick in Eclipse SDK 4.x vermitteln und erläutern wie man einfach ein bestehendes RCP Projekt migrieren kann. Grade die endlich in OSGI integrierte Dependency Injection und das neue Application Model mit dem hilfreichen und einfach zu bedienenden e4xmi-Editor machen Freude.