Skip to content

Commit b018154

Browse files
authored
Merge pull request #456 from AxonIQ/af5-migration
Migrate to Axon Framework 5
2 parents 7fdc52e + 6e3fa39 commit b018154

198 files changed

Lines changed: 603 additions & 4357 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Code Samples
22

3-
This repository contains several Axon-based sample projects to explain specific topics.
4-
Axon usage extends itself from the repositories under the [Axon Framework project](https://github.com/AxonFramework/),
5-
as well as the tools provided by AxonIQ like [Axon Server](https://www.axoniq.io/products/axon-server).
3+
This repository contains several Axon Framework 5-based sample projects to explain specific topics.
4+
Axon usage extends itself from the repositories under the [Axon Framework project](https://github.com/AxonIQ/AxonFramework),
5+
as well as the tools provided by AxoniQ like [Axon Server](https://www.axoniq.io/products/axon-server).
66

77
Each Maven module within this project represents a different sample you can use, each with its own `README.md`
88
explaining the intent and usage.
@@ -17,23 +17,31 @@ Down below is an exhaustive list of all the sample:
1717
and Spring-based application.
1818
2. [Distributed Exceptions](distributed-exceptions/README.md) - Sample showing how to deal with exceptions in a
1919
distributed application.
20-
3. [Multitenancy](multitenancy/README.md) - Sample showing 'a' approach to multitenancy.
21-
4. [Reset Handler](reset-handler/README.md) - Sample showing how to reset a `StreamingEventProcessor`.
22-
5. [Saga - No TOAST in PostgreSQL](saga/README.md) - Sample showing a basis Saga, that's stored in a PostgreSQL
23-
without [TOAST](https://wiki.postgresql.org/wiki/TOAST).
24-
6. [Serialization Avro](serialization-avro/README.md) - Sample showing usage of Apache Avro Commands/Events/Queries.
25-
7. [Sequencing Policy](sequencing-policy/README.md) - Sample showing how to set up a custom `SequencingPolicy` to adjust
26-
the event sequence for a `StreamingEventProcessor`.
27-
8. [Set-Based Validation](set-based-validation/README.md) - Sample showing several approaches to implement set-based
28-
validation.
29-
9. [Set-Based Validation - Actor Model](set-based-validation-actor-model/README.md) - Sample showing how to implement
30-
set-based validation through a dedicated aggregate instance.
31-
10. [Snapshots](snapshots/README.md) - Sample showing how to configure aggregate snapshotting.
32-
11. [Stateful Event Handler](stateful-event-handler/README.md) - Sample showing a stateful event handler that can be
33-
used as a replacement for sagas.
34-
12. [Subscription Query - REST](subscription-query-rest/README.md) - Sample showing how to use Axon's subscription query
35-
cleanly in a REST-based controller.
36-
13. [Subscription Query - Streaming](subscription-query-streaming/README.md) - Sample showing how to use Axon's
20+
3. [Order Fulfillment Workflow](order-fulfillment-workflow/README.md) - Sample showing how to model an Order
21+
Fulfillment process with the AxoniQ Workflow Engine.
22+
4. [Reset Handler](reset-handler/README.md) - Sample showing how to reset a `PooledStreamingEventProcessor`.
23+
5. [Serialization Avro](serialization-avro/README.md) - Sample showing usage of Apache Avro Commands/Events/Queries.
24+
6. [Sequencing Policy](sequencing-policy/README.md) - Sample showing how to set up a custom `SequencingPolicy` to adjust
25+
the event sequence for a `PooledStreamingEventProcessor`.
26+
7. [Snapshots](snapshots/README.md) - Sample showing how to configure event-sourced entity snapshotting.
27+
8. [Stateful Event Handler](stateful-event-handler/README.md) - Sample showing a stateful event handler that can be
28+
used as a replacement for sagas.
29+
9. [Subscription Query - REST](subscription-query-rest/README.md) - Sample showing how to use Axon's subscription query
30+
cleanly in a REST-based controller.
31+
10. [Subscription Query - Streaming](subscription-query-streaming/README.md) - Sample showing how to use Axon's
3732
subscription query cleanly in a streaming-based controller.
38-
14. [Upcasters](upcaster/README.md) - Sample showing several implementations of upcasters.
33+
11. [Workflow Saga](workflow-saga/README.md) - Sample showing a Saga-like process modelled with the AxoniQ Workflow
34+
Engine.
35+
36+
## Topics now covered by the Axon Framework project itself
37+
38+
A few topics that used to live here as standalone samples now have more thorough, actively maintained examples in the
39+
[AxonIQ/AxonFramework](https://github.com/AxonIQ/AxonFramework) repository's `examples/` directory. Rather than
40+
maintaining a second, thinner copy, we point to those instead:
41+
42+
- **Sagas** - see the [`extension-workflow`](https://github.com/AxonIQ/extension-workflow) project and the automation
43+
patterns in [`examples/university-demo`](https://github.com/AxonIQ/AxonFramework/tree/main/examples/university-demo).
44+
- **Multitenancy** - see [`examples/university-multi-tenancy-examples`](https://github.com/AxonIQ/AxonFramework/tree/main/examples/university-multi-tenancy-examples).
45+
- **Set-based validation** - see `CourseUniqueNameSetValidation` in [`examples/university-demo`](https://github.com/AxonIQ/AxonFramework/tree/main/examples/university-demo/src/main/java/org/axonframework/examples/demo/university/faculty/write/createcourse).
46+
- **Upcasting / event transformation** - see [`examples/university-message-transformation`](https://github.com/AxonIQ/AxonFramework/tree/main/examples/university-message-transformation).
3947

‎axon-spring-template/pom.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<dependencies>
1515
<!-- Axon -->
1616
<dependency>
17-
<groupId>org.axonframework</groupId>
17+
<groupId>org.axonframework.extensions.spring</groupId>
1818
<artifactId>axon-spring-boot-starter</artifactId>
1919
</dependency>
2020
<dependency>

‎distributed-exceptions/pom.xml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<artifactId>axon-eventsourcing</artifactId>
3030
</dependency>
3131
<dependency>
32-
<groupId>org.axonframework</groupId>
32+
<groupId>org.axonframework.extensions.spring</groupId>
3333
<artifactId>axon-spring-boot-starter</artifactId>
3434
</dependency>
3535
<!-- Spring -->
@@ -56,7 +56,7 @@
5656
<!-- Testing -->
5757
<dependency>
5858
<groupId>org.testcontainers</groupId>
59-
<artifactId>junit-jupiter</artifactId>
59+
<artifactId>testcontainers-junit-jupiter</artifactId>
6060
</dependency>
6161
<dependency>
6262
<groupId>org.junit.jupiter</groupId>
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
package io.axoniq.distributedexceptions;
22

3-
import io.axoniq.distributedexceptions.command.ExceptionWrappingHandlerInterceptor;
43
import org.springframework.boot.SpringApplication;
54
import org.springframework.boot.autoconfigure.SpringBootApplication;
6-
import org.springframework.context.annotation.Bean;
7-
import org.springframework.context.annotation.Profile;
85

96
@SpringBootApplication
107
public class DistributedExceptionsApplication {
118

129
public static void main(String[] args) {
1310
SpringApplication.run(DistributedExceptionsApplication.class, args);
1411
}
15-
16-
@Bean
17-
@Profile("command")
18-
public ExceptionWrappingHandlerInterceptor exceptionWrappingHandlerInterceptor() {
19-
return new ExceptionWrappingHandlerInterceptor();
20-
}
2112
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.axoniq.distributedexceptions.api;
22

3-
import javax.annotation.Nonnull;
3+
import org.axonframework.eventsourcing.annotation.EventTag;
4+
import org.axonframework.messaging.eventhandling.annotation.Event;
45

5-
public record CardIssuedEvent(@Nonnull String id, int amount) {
6+
@Event
7+
public record CardIssuedEvent(@EventTag(key = "GiftCard") String id, int amount) {
68

79
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.axoniq.distributedexceptions.api;
22

3-
import javax.annotation.Nonnull;
3+
import org.axonframework.eventsourcing.annotation.EventTag;
4+
import org.axonframework.messaging.eventhandling.annotation.Event;
45

5-
public record CardRedeemedEvent(@Nonnull String id, int amount) {
6+
@Event
7+
public record CardRedeemedEvent(@EventTag(key = "GiftCard") String id, int amount) {
68

79
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package io.axoniq.distributedexceptions.api;
22

3-
import org.axonframework.modelling.command.TargetAggregateIdentifier;
3+
import org.axonframework.messaging.commandhandling.annotation.Command;
4+
import org.axonframework.modelling.annotation.TargetEntityId;
45

5-
import javax.annotation.Nonnull;
6-
7-
public record IssueCardCommand(@TargetAggregateIdentifier @Nonnull String id, int amount) {
6+
@Command(routingKey = "id")
7+
public record IssueCardCommand(@TargetEntityId String id, int amount) {
88

99
}
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package io.axoniq.distributedexceptions.api;
22

3-
import org.axonframework.modelling.command.TargetAggregateIdentifier;
3+
import org.axonframework.messaging.commandhandling.annotation.Command;
4+
import org.axonframework.modelling.annotation.TargetEntityId;
45

5-
import javax.annotation.Nonnull;
6-
7-
public record RedeemCardCommand(@TargetAggregateIdentifier @Nonnull String id, int amount) {
6+
@Command(routingKey = "id")
7+
public record RedeemCardCommand(@TargetEntityId String id, int amount) {
88

99
}

‎distributed-exceptions/src/main/java/io/axoniq/distributedexceptions/command/ExceptionWrappingHandlerInterceptor.java‎

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,37 @@
22

33
import io.axoniq.distributedexceptions.api.GiftCardBusinessError;
44
import io.axoniq.distributedexceptions.api.GiftCardBusinessErrorCode;
5-
import org.axonframework.commandhandling.CommandExecutionException;
6-
import org.axonframework.commandhandling.CommandMessage;
7-
import org.axonframework.messaging.InterceptorChain;
8-
import org.axonframework.messaging.MessageHandlerInterceptor;
9-
import org.axonframework.messaging.unitofwork.UnitOfWork;
5+
import org.axonframework.messaging.commandhandling.CommandExecutionException;
6+
import org.axonframework.messaging.commandhandling.CommandMessage;
7+
import org.axonframework.messaging.core.MessageHandlerInterceptor;
8+
import org.axonframework.messaging.core.MessageHandlerInterceptorChain;
9+
import org.axonframework.messaging.core.MessageStream;
10+
import org.axonframework.messaging.core.unitofwork.ProcessingContext;
1011
import org.slf4j.Logger;
1112
import org.slf4j.LoggerFactory;
13+
import org.springframework.context.annotation.Profile;
14+
import org.springframework.stereotype.Component;
1215

1316
import java.lang.invoke.MethodHandles;
14-
import javax.annotation.Nonnull;
1517

16-
public class ExceptionWrappingHandlerInterceptor implements MessageHandlerInterceptor<CommandMessage<?>> {
18+
@Component
19+
@Profile("command")
20+
public class ExceptionWrappingHandlerInterceptor implements MessageHandlerInterceptor<CommandMessage> {
1721

1822
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
1923

2024
@Override
21-
public Object handle(@Nonnull UnitOfWork<? extends CommandMessage<?>> unitOfWork,
22-
@Nonnull InterceptorChain interceptorChain) {
23-
try {
24-
return interceptorChain.proceed();
25-
} catch (Throwable e) {
26-
throw new CommandExecutionException(
27-
"An exception has occurred during command execution", e, exceptionDetails(e)
28-
);
29-
}
25+
public MessageStream<?> interceptOnHandle(CommandMessage message,
26+
ProcessingContext context,
27+
MessageHandlerInterceptorChain<CommandMessage> chain) {
28+
return chain.proceed(message, context)
29+
.onErrorContinue(throwable -> MessageStream.failed(
30+
new CommandExecutionException(
31+
"An exception has occurred during command execution",
32+
throwable,
33+
exceptionDetails(throwable)
34+
)
35+
));
3036
}
3137

3238
// Domain specific details can be returned in a couple of forms.

‎distributed-exceptions/src/main/java/io/axoniq/distributedexceptions/command/GiftCard.java‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,45 @@
44
import io.axoniq.distributedexceptions.api.CardIssuedEvent;
55
import io.axoniq.distributedexceptions.api.RedeemCardCommand;
66
import io.axoniq.distributedexceptions.api.CardRedeemedEvent;
7-
import org.axonframework.commandhandling.CommandHandler;
8-
import org.axonframework.eventsourcing.EventSourcingHandler;
9-
import org.axonframework.modelling.command.AggregateIdentifier;
10-
import org.axonframework.spring.stereotype.Aggregate;
7+
import org.axonframework.eventsourcing.annotation.EventSourcingHandler;
8+
import org.axonframework.eventsourcing.annotation.reflection.EntityCreator;
9+
import org.axonframework.extension.spring.stereotype.EventSourced;
10+
import org.axonframework.messaging.commandhandling.annotation.CommandHandler;
11+
import org.axonframework.messaging.eventhandling.gateway.EventAppender;
1112
import org.slf4j.Logger;
1213
import org.slf4j.LoggerFactory;
1314
import org.springframework.context.annotation.Profile;
1415

1516
import java.lang.invoke.MethodHandles;
1617

17-
import static org.axonframework.modelling.command.AggregateLifecycle.apply;
18-
19-
@Aggregate
18+
@EventSourced(tagKey = "GiftCard")
2019
@Profile("command")
2120
class GiftCard {
2221

2322
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
2423

25-
@AggregateIdentifier
2624
private String giftCardId;
2725
private int remainingValue;
2826

2927
@CommandHandler
30-
public GiftCard(IssueCardCommand command) {
28+
public static void handle(IssueCardCommand command, EventAppender eventAppender) {
3129
logger.debug("handling {}", command);
3230
if (command.amount() <= 0) {
3331
throw new NegativeOrZeroAmount(command.amount(), "amount <= 0");
3432
}
35-
apply(new CardIssuedEvent(command.id(), command.amount()));
33+
eventAppender.append(new CardIssuedEvent(command.id(), command.amount()));
3634
}
3735

3836
@CommandHandler
39-
public void handle(RedeemCardCommand command) {
37+
public void handle(RedeemCardCommand command, EventAppender eventAppender) {
4038
logger.debug("handling {}", command);
4139
if (command.amount() <= 0) {
4240
throw new NegativeOrZeroAmount(command.amount(), "amount <= 0");
4341
}
4442
if (command.amount() > remainingValue) {
4543
throw new InsufficientFunds("amount > remaining value");
4644
}
47-
apply(new CardRedeemedEvent(giftCardId, command.amount()));
45+
eventAppender.append(new CardRedeemedEvent(giftCardId, command.amount()));
4846
}
4947

5048
@EventSourcingHandler
@@ -62,6 +60,7 @@ public void on(CardRedeemedEvent event) {
6260
logger.debug("new remaining value: {}", remainingValue);
6361
}
6462

63+
@EntityCreator
6564
public GiftCard() {
6665
// Required by Axon
6766
logger.debug("Empty constructor invoked");

0 commit comments

Comments
 (0)