Skip to content

Commit

Permalink
walkthrough documentation - Server-Sent Events (SSE)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohT committed Dec 27, 2021
1 parent f864bbb commit 9e0681b
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions showcase-quarkus-eventsourcing/WALKTHROUGH.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The following topics are meant to lead you through the code and highlight most i
* [ArchUnit in action](#ArchUnit-in-action)
* [Flyway in action](#Flyway-in-action)
* [Vanilla JavaScript UI](#Vanilla-JavaScript-UI)
* [Server-Sent Events (SSE) for Subscription Queries](#Server-Sent-Events-SSE-for-Subscription-Queries)

## Main Structure

Expand Down Expand Up @@ -109,11 +110,11 @@ Therefore, serialization libraries provide annotations for constructors. This in

As an example have a look at the constructor in [ChangeNicknameCommand.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/message/command/account/ChangeNicknameCommand.java).

### AxonFramework behind the boundary
## AxonFramework behind the boundary

Following "Entity Control Boundary" or "Ports'n'Adapters", an extreme but interesting experiment is to define interfaces between the core (domain) of the application and AxonFramework. This is not meant to be an advice to do so. It has a couple of pros and cons as listed below. But it is for sure an interesting experiment.

#### Services and Adapters
### Services and Adapters

As an example, AxonFramework's `CommandGateway` is represented inside the application by the boundary interface [CommandEmitterService.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/command/boundary/CommandEmitterService.java) and implemented by the [CommandEmitterAdapter.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/command/axon/CommandEmitterAdapter.java). The adapter is created in [AxonConfiguration.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/infrastructure/axon/AxonConfiguration.java) which also contains the CDI producer for the application.
[AccountResource.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/service/account/AccountResource.java) uses the [CommandEmitterService.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/command/boundary/CommandEmitterService.java) to send commands as it would be done with the `CommandGateway`.
Expand All @@ -122,42 +123,42 @@ The core of the application only uses the interface [CommandEmitterService.java]

The implementation and connection to AxonFramework all happens in the [messaging](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging) package in the two classes mentioned above. So if anything changes inside AxonFramework, these two classes are the only ones that would be affected in most cases.

#### Meta-Annotations
### Meta-Annotations

AxonFramework not only comes with interfaces like the `CommandGateway`, it also provides annotations to identify building blocks like a `CommandHandler`.
To take the approach of an abstraction between application core code and AxonFramework to an extreme level, the annotations can also be replaced by own ones. These own/custom meta-annotations are annotated in their definition with the original framework annotations.

Here is an example:
[CommandModelCommandHandler.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/command/boundary/CommandModelCommandHandler.java).

#### Pros
### Pros
* It could be easier to update to a newer version of AxonFramework, especially when there are breaking changes.
* It provides additional possibilities to extend/customize functionality provided by AxonFramework dedicated to the application.
* It defines and documents exactly those framework features that are used and hides the rest.
* The additional abstraction could be used for integration tests, that shouldn't produce side-effects via AxonFramework, e.g. by replacing those parts by mocks.

#### Cons
### Cons
* It introduces additional complexity.
* It is harder to compare the code to other applications, especially when different names are used.
* It is harder to discuss AxonFramework related topics because of the customization in-between.
* Before using a new feature of the framework, the abstraction/boundary needs to be extended first.
* Axon Aggregate tests are harder to setup because they also need to be aware of the customized parts.

#### Lessons learned
### Lessons learned

Updating minor versions of 4.x did not lead to changes in core domain code. But it is also very likely,
that this would also have been the case without the boundary.

The extreme approach to even define meta annotations lead to [a code change](https://github.com/JohT/showcase-quarkus-eventsourcing/commit/d0d3e623f3ef8aea8b75162416372f0b44be87d0#diff-38a240ea3c3ebfc9e839fa2220fa1935d5bcc49ba1bcb58c000e9d97cda2ccb3) in
[QueryModelResetHandler.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/messaging/query/boundary/QueryModelResetHandler.java) that wouldn't have been necessary otherwise. This special case doen't prove that the abstraction leads to more coupling, but is a good example on how Meta-Annotations tend to be more dependent on their originals (if they get changed) than it might be expected.

#### Summary
### Summary

To summarize, it could be beneficial to applications with a big or fast growing core domain to put some effort in designing a boundary (e.g. interfaces) to frameworks like Axon, to be able to adapt future updates fast, customize the structure and even the behavior to perfectly fit the application and to have code that documents which parts of the frameworks are used.

This is by no means at no cost. It introduces additional complexity, makes it harder to move code between different applications with different boundaries and also needs to be maintained. For small Microservices it is likely to be less effort to adapt framework changes in the whole application instead of maintaining an abstraction.

### ArchUnit in action
## ArchUnit in action

> [ArchUnit][ArchUnit] is a free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework.
Expand All @@ -170,7 +171,7 @@ This is by no means at no cost. It introduces additional complexity, makes it ha
[ArchUnit][ArchUnit] can also be used to test equals- and hashCode methods
as described in [Testing all equals and hashCode methods][TestingEqualsHashcode].

### Flyway in action
## Flyway in action

> Version control for your database
Expand All @@ -179,29 +180,29 @@ takes database script files inside the folder [resources/db](./src/main/resource

The configuration is specific to Quarkus and can be found in the [application.properties](./src/main/resources/application.properties).

#### Similarities to Event Sourcing
### Similarities to Event Sourcing

* Any database change ("event") is represented by a separate script file ("event payload").
* Existing script files won't be changed ("immutable").
* The order of the script files are represented by their version number ("sequence number").
* They are applied ("replayed") on the database ("projection") until it is up to date ("tracking token").

### Vanilla JavaScript UI
## Vanilla JavaScript UI

The user interface is made with plain/vanilla JavaScript, CSS and HTML.

#### UI Structure
### UI Structure

* [src/main/javascript](./src/main/javascript) contains JavaScript sources
* [src/test/javascript](./src/test/javascript) contains JavaScript Jasmine Unit-Tests
* [src/main/javascript/polyfills](./src/main/javascript/polyfills) contains JavaScript sources that provide functions, that are missing in older browsers.
* [META-INF/resources](./src/main/resources/META-INF/resources) contains static CSS and HTML sources
* [startup.js](./src/main/javascript/startup.js) registers JavaScript functions on page load
* [account.js](./src/main/javascript/account.js) contains the part of the application, that deals with the "account" (domain name)
* [event.js](./src/main/javascript/event.js) contains server sent events (SSE) client
* [event.js](./src/main/javascript/event.js) contains the Server-Sent Events (SSE) client
* [Maven POM](./pom.xml) also contains plugins to build the UI without Node.js.

#### UI Modules
### UI Modules

[account.js](./src/main/javascript/account.js) consists of these modules in the namespace `eventsourcing_showcase`:

Expand All @@ -210,9 +211,9 @@ The user interface is made with plain/vanilla JavaScript, CSS and HTML.
* **AccountController** - Coordinates the modules AccountUI and AccountRepository and provides the "load" function that is called by [startup.js](./src/main/javascript/startup.js).

[event.js](./src/main/javascript/event.js) consists of one module in the namespace `eventsourcing_showcase`:
* **EventController** - Contains the server sent events (SSE) client, which updates the UI without refresh in a reactive manner when a new nickname is created (backed by a axon subscription query).
* **EventController** - Contains the Server-Sent Events (SSE) client, which updates the UI without refresh in a reactive manner when a new nickname is created (backed by a axon subscription query).

#### UI Build
### UI Build

The whole application is build with [Maven][Maven], including the user interface. [Maven][Maven] is usually used for Java applications. [Node.js®][NodeJS] is popular for JavaScript applications. [Maven][Maven] might not be suitable for a large JavaScript applications. However in this case it simplifies the build, since all steps are done within one build tool.

Expand All @@ -222,13 +223,25 @@ These Maven-Plugins are used to build the user interface:
* [saga-maven-plugin][saga-maven-plugin] measures JavaScript unit test coverage
* [yuicompressor-maven-plugin][yuicompressor-maven-plugin] aggregates/copies all JavaScript sources into one `application.js` and compresses it to make it smaller and therefore faster to load.

## Server-Sent Events (SSE) for Subscription Queries

As shown in [Introducing Subscription Queries][AxonSubscriptionQueries], an Event Sourced System brings new possibilities when it comes to queries. Additionally to sending a request and displaying the results it is possible to "subscribe" to query result changes. This enables a reactive approach where it isn't necessary to refresh the browser or to repeatedly execute the query for new results.

After the initial response the query updates need to be pushed to the browser. Using [MicroProfile][MicroProfile] without any additional libraries this can be done as described in
[Server-Sent Events (SSE) in JAX-RS][ServerSentEvents].

### Structure

* [event.js](./src/main/javascript/event.js) contains the SSE client.
* [NicknameEventStreamResource.java](./src/main/java/io/github/joht/showcase/quarkuseventsourcing/service/nickname/NicknameEventStreamResource.java) contains the server-side SSE endpoint.

## References

* [ArchUnit][ArchUnit]
* [Axon Framework CDI Support][AxonFrameworkCDI]
* [AxonFramework Giftcard Example][AxonFrameworkGiftcardExample]
* [AxonFramework Parameter Resolver][AxonFrameworkParameterResolver]
* [Introducing Subscription Queries][AxonSubscriptionQueries]
* [Building a native executable][QuarkusNativeExecutable]
* [CDI Portable Extension][CDIExtension]
* [java.beans.ConstructorProperties Annotation][ConstructorProperties]
Expand All @@ -246,13 +259,15 @@ These Maven-Plugins are used to build the user interface:
* [PhantomJS][PhantomJS]
* [Quarkus Context and Dependency Injection (CDI)][QuarkusCDI]
* [Quarkus Application Initialization and Termination][QuarkusLivecycle]
* [Server-Sent Events (SSE) in JAX-RS][ServerSentEvents]
* [ServiceLoader][ServiceLoader]
* [Testing all equals and hashCode methods][TestingEqualsHashcode]

[ArchUnit]: https://www.archunit.org
[AxonFrameworkCDI]: https://github.com/AxonFramework/extension-cdi
[AxonFrameworkParameterResolver]: https://axoniq.io/blog-overview/parameter-resolvers-axon
[AxonFrameworkGiftcardExample]: https://github.com/AxonFramework/extension-springcloud-sample
[AxonSubscriptionQueries]: https://axoniq.io/blog-overview/introducing-subscription-queries
[CDIExtension]: https://docs.jboss.org/weld/reference/latest/en-US/html/extend.html
[ConstructorProperties]: https://docs.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.html
[Flyway]: https://flywaydb.org
Expand All @@ -267,7 +282,8 @@ These Maven-Plugins are used to build the user interface:
[QuarkusCDI]: https://quarkus.io/guides/cdi-reference
[QuarkusNativeExecutable]: https://quarkus.io/guides/building-native-image-guide
[QuarkusLivecycle]: https://quarkus.io/guides/lifecycle
[saga-maven-plugin]: https://timurstrekalov.github.io/saga/
[saga-maven-plugin]: https://timurstrekalov.github.io/saga
[ServerSentEvents]: https://www.baeldung.com/java-ee-jax-rs-sse
[ServiceLoader]: https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
[TestingEqualsHashcode]: https://joht.github.io/johtizen/testing/2020/03/08/test-all-equal-and-hashcode-methods.html
[yuicompressor-maven-plugin]: http://davidb.github.io/yuicompressor-maven-plugin/

0 comments on commit 9e0681b

Please sign in to comment.