{"id":695,"date":"2026-06-19T23:31:46","date_gmt":"2026-06-19T22:31:46","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=695"},"modified":"2026-07-07T23:32:19","modified_gmt":"2026-07-07T22:32:19","slug":"event-sourcing-cqrs-mit-axon-framework-in-spring-boot","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=695","title":{"rendered":"Event Sourcing &amp; CQRS mit Axon Framework in Spring Boot"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Die meisten Spring-Boot-Anwendungen speichern nur den aktuellen Zustand ihrer Daten in einer relationalen Datenbank. Reicht das? Nicht immer. Wenn du nachvollziehen musst,&nbsp;<em>wie<\/em>&nbsp;ein Zustand entstanden ist \u2014 etwa bei Buchungen, Audit-Trails oder regulatorischen Anforderungen \u2014 brauchst du Event Sourcing. Und wenn deine Lese- und Schreibmuster stark voneinander abweichen, hilft CQRS (Command Query Responsibility Segregation).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Axon Framework vereint beide Patterns unter einem Dach und integriert sich nahtlos in Spring Boot. Dieser Artikel zeigt dir die praktische Umsetzung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was Axon 4.11 leistet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Axon (aktuell&nbsp;<strong>4.11.2<\/strong>, mit 5.1 als neueste Major Version) modelliert eine Anwendung rund um drei Kernkonzepte:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Aggregate<\/strong>\u00a0\u2014 verarbeitet Commands, trifft Entscheidungen und publiziert Events<\/li>\n\n\n\n<li><strong>Event Store<\/strong>\u00a0\u2014 speichert alle Events als unver\u00e4nderliche Quelle der Wahrheit<\/li>\n\n\n\n<li><strong>Projection<\/strong>\u00a0\u2014 h\u00f6rt auf Events und baut daraus optimierte Query-Modelle<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Statt&nbsp;<code>repository.save(order)<\/code>&nbsp;zu rufen, sendest du einen Command, und Axon k\u00fcmmert sich um Routing, Event-Persistierung und asynchrone Projektion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setup mit Spring Boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Maven-Dependencies:<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.axonframework<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>axon-spring-boot-starter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span><span class=\"hljs-comment\">&lt;!-- Optional: Axon Server f\u00fcr verteilte Setups --&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.axonframework<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>axon-server-connector<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Ohne Axon Server verwendet Axon einen In-Memory-Event-Store. F\u00fcr lokale Entwicklung perfekt \u2014 in Produktion wechselst du auf Axon Server oder eine JPA\/JDBC-Event-Store-Konfiguration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Domain: Ein Bestellprozess<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr unser Beispiel modellieren wir einen vereinfachten Bestellvorgang: Eine Bestellung wird erstellt, ein Betrag zugewiesen und schlie\u00dflich best\u00e4tigt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Commands und Events<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">import org.axonframework.modelling.command.TargetAggregateIdentifier;\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CreateOrderCommand<\/span> <\/span>{\n    @TargetAggregateIdentifier\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String orderId;\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String product;\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> int amount;\n\n    <span class=\"hljs-keyword\">public<\/span> CreateOrderCommand(String orderId, String product, int amount) {\n        this.orderId = orderId;\n        this.product = product;\n        this.amount = amount;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> String getOrderId() { <span class=\"hljs-keyword\">return<\/span> orderId; }\n    <span class=\"hljs-keyword\">public<\/span> String getProduct() { <span class=\"hljs-keyword\">return<\/span> product; }\n    <span class=\"hljs-keyword\">public<\/span> int getAmount() { <span class=\"hljs-keyword\">return<\/span> amount; }\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ConfirmOrderCommand<\/span> <\/span>{\n    @TargetAggregateIdentifier\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String orderId;\n\n    <span class=\"hljs-keyword\">public<\/span> ConfirmOrderCommand(String orderId) {\n        this.orderId = orderId;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> String getOrderId() { <span class=\"hljs-keyword\">return<\/span> orderId; }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">OrderCreatedEvent<\/span> <\/span>{\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String orderId;\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String product;\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> int amount;\n\n    <span class=\"hljs-keyword\">public<\/span> OrderCreatedEvent(String orderId, String product, int amount) {\n        this.orderId = orderId;\n        this.product = product;\n        this.amount = amount;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> String getOrderId() { <span class=\"hljs-keyword\">return<\/span> orderId; }\n    <span class=\"hljs-keyword\">public<\/span> String getProduct() { <span class=\"hljs-keyword\">return<\/span> product; }\n    <span class=\"hljs-keyword\">public<\/span> int getAmount() { <span class=\"hljs-keyword\">return<\/span> amount; }\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">OrderConfirmedEvent<\/span> <\/span>{\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String orderId;\n\n    <span class=\"hljs-keyword\">public<\/span> OrderConfirmedEvent(String orderId) {\n        this.orderId = orderId;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> String getOrderId() { <span class=\"hljs-keyword\">return<\/span> orderId; }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Der Aggregate<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> org.axonframework.commandhandling.CommandHandler;\n<span class=\"hljs-keyword\">import<\/span> org.axonframework.eventsourcing.EventSourcingHandler;\n<span class=\"hljs-keyword\">import<\/span> org.axonframework.modelling.command.AggregateIdentifier;\n<span class=\"hljs-keyword\">import<\/span> org.axonframework.spring.stereotype.Aggregate;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-keyword\">static<\/span> org.axonframework.modelling.command.AggregateLifecycle.apply;\n\n@Aggregate\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Order<\/span> <\/span>{\n\n    @AggregateIdentifier\n    private <span class=\"hljs-built_in\">String<\/span> orderId;\n    private <span class=\"hljs-built_in\">String<\/span> product;\n    private int amount;\n    private boolean confirmed;\n\n    @CommandHandler\n    public Order(CreateOrderCommand cmd) {\n        <span class=\"hljs-keyword\">if<\/span> (cmd.getAmount() &lt;= <span class=\"hljs-number\">0<\/span>) {\n            <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> IllegalArgumentException(<span class=\"hljs-string\">\"Betrag muss positiv sein\"<\/span>);\n        }\n        apply(<span class=\"hljs-keyword\">new<\/span> OrderCreatedEvent(\n            cmd.getOrderId(), cmd.getProduct(), cmd.getAmount()));\n    }\n\n    @EventSourcingHandler\n    public <span class=\"hljs-keyword\">void<\/span> on(OrderCreatedEvent event) {\n        <span class=\"hljs-keyword\">this<\/span>.orderId = event.getOrderId();\n        <span class=\"hljs-keyword\">this<\/span>.product = event.getProduct();\n        <span class=\"hljs-keyword\">this<\/span>.amount = event.getAmount();\n    }\n\n    @CommandHandler\n    public <span class=\"hljs-keyword\">void<\/span> handle(ConfirmOrderCommand cmd) {\n        <span class=\"hljs-keyword\">if<\/span> (confirmed) {\n            <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> IllegalStateException(<span class=\"hljs-string\">\"Bereits best\u00e4tigt\"<\/span>);\n        }\n        apply(<span class=\"hljs-keyword\">new<\/span> OrderConfirmedEvent(cmd.getOrderId()));\n    }\n\n    @EventSourcingHandler\n    public <span class=\"hljs-keyword\">void<\/span> on(OrderConfirmedEvent event) {\n        <span class=\"hljs-keyword\">this<\/span>.confirmed = <span class=\"hljs-literal\">true<\/span>;\n    }\n\n    protected Order() {}\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Wichtig:<\/strong>&nbsp;<code>@AggregateIdentifier<\/code>&nbsp;ist Pflicht \u2014 Axon braucht es, um Commands zum richtigen Aggregate zu routen. Der parameterlose Konstruktor wird von Axon beim Replay ben\u00f6tigt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Zustand eines Aggregates wird&nbsp;<strong>niemals direkt gesetzt<\/strong>.&nbsp;<code>@CommandHandler<\/code>-Methoden validieren und publizieren Events;&nbsp;<code>@EventSourcingHandler<\/code>-Methoden reagieren auf Events und setzen Felder. Beim Laden aus dem Event Store spielt Axon alle historischen Events erneut ab (Event Replay).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Die Projection (Query-Modell)<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">import org.axonframework.eventhandling.EventHandler;\nimport org.axonframework.queryhandling.QueryHandler;\nimport org.springframework.stereotype.Component;\nimport java.util.*;\n\n@Component\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">OrderProjection<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> Map&lt;String, OrderSummary&gt; orders = <span class=\"hljs-keyword\">new<\/span> ConcurrentHashMap&lt;&gt;();\n\n    @EventHandler\n    <span class=\"hljs-keyword\">public<\/span> void on(OrderCreatedEvent event) {\n        orders.put(event.getOrderId(), <span class=\"hljs-keyword\">new<\/span> OrderSummary(\n            event.getOrderId(), event.getProduct(),\n            event.getAmount(), <span class=\"hljs-keyword\">false<\/span>));\n    }\n\n    @EventHandler\n    <span class=\"hljs-keyword\">public<\/span> void on(OrderConfirmedEvent event) {\n        OrderSummary existing = orders.get(event.getOrderId());\n        <span class=\"hljs-keyword\">if<\/span> (existing != <span class=\"hljs-keyword\">null<\/span>) {\n            orders.put(event.getOrderId(), <span class=\"hljs-keyword\">new<\/span> OrderSummary(\n                existing.orderId(), existing.product(),\n                existing.amount(), <span class=\"hljs-keyword\">true<\/span>));\n        }\n    }\n\n    @QueryHandler\n    <span class=\"hljs-keyword\">public<\/span> OrderSummary handle(FindOrderQuery query) {\n        <span class=\"hljs-keyword\">return<\/span> orders.get(query.getOrderId());\n    }\n\n    @QueryHandler\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;OrderSummary&gt; handle(FindAllOrdersQuery query) {\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">List<\/span>.copyOf(orders.values());\n    }\n}\n\n<span class=\"hljs-keyword\">public<\/span> record OrderSummary(\n    String orderId, String product, int amount, boolean confirmed) {}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FindOrderQuery<\/span> <\/span>{\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> String orderId;\n    <span class=\"hljs-keyword\">public<\/span> FindOrderQuery(String orderId) { this.orderId = orderId; }\n    <span class=\"hljs-keyword\">public<\/span> String getOrderId() { <span class=\"hljs-keyword\">return<\/span> orderId; }\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FindAllOrdersQuery<\/span> <\/span>{}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Projections sind Spring-<code>@Component<\/code>s.&nbsp;<code>@EventHandler<\/code>-Methoden werden asynchron aufgerufen, nachdem der Aggregate seine Events committed hat \u2014 Query-Modelle sind also&nbsp;<em>eventually consistent<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">REST-Controller<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> org.axonframework.commandhandling.gateway.CommandGateway;\n<span class=\"hljs-keyword\">import<\/span> org.axonframework.queryhandling.QueryGateway;\n<span class=\"hljs-keyword\">import<\/span> org.springframework.web.bind.annotation.*;\n<span class=\"hljs-keyword\">import<\/span> java.util.*;\n<span class=\"hljs-keyword\">import<\/span> java.util.concurrent.CompletableFuture;\n\n@RestController\n@RequestMapping(<span class=\"hljs-string\">\"\/orders\"<\/span>)\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">OrderController<\/span> <\/span>{\n\n    private final CommandGateway commandGateway;\n    private final QueryGateway queryGateway;\n\n    public OrderController(CommandGateway commandGateway,\n                           QueryGateway queryGateway) {\n        <span class=\"hljs-keyword\">this<\/span>.commandGateway = commandGateway;\n        <span class=\"hljs-keyword\">this<\/span>.queryGateway = queryGateway;\n    }\n\n    @PostMapping\n    public CompletableFuture&lt;<span class=\"hljs-built_in\">String<\/span>&gt; create(@RequestBody <span class=\"hljs-built_in\">Map<\/span>&lt;<span class=\"hljs-built_in\">String<\/span>, <span class=\"hljs-built_in\">Object<\/span>&gt; body) {\n        <span class=\"hljs-built_in\">String<\/span> orderId = UUID.randomUUID().toString();\n        <span class=\"hljs-keyword\">return<\/span> commandGateway.send(<span class=\"hljs-keyword\">new<\/span> CreateOrderCommand(\n            orderId,\n            (<span class=\"hljs-built_in\">String<\/span>) body.get(<span class=\"hljs-string\">\"product\"<\/span>),\n            (int) body.get(<span class=\"hljs-string\">\"amount\"<\/span>)));\n    }\n\n    @PostMapping(<span class=\"hljs-string\">\"\/{orderId}\/confirm\"<\/span>)\n    public CompletableFuture&lt;Void&gt; confirm(@PathVariable <span class=\"hljs-built_in\">String<\/span> orderId) {\n        <span class=\"hljs-keyword\">return<\/span> commandGateway.send(<span class=\"hljs-keyword\">new<\/span> ConfirmOrderCommand(orderId));\n    }\n\n    @GetMapping(<span class=\"hljs-string\">\"\/{orderId}\"<\/span>)\n    public CompletableFuture&lt;OrderSummary&gt; <span class=\"hljs-keyword\">get<\/span>(@PathVariable String orderId) {\n        <span class=\"hljs-keyword\">return<\/span> queryGateway.query(\n            <span class=\"hljs-keyword\">new<\/span> FindOrderQuery(orderId), OrderSummary.class);\n    }\n\n    @GetMapping\n    public CompletableFuture&lt;List&lt;OrderSummary&gt;&gt; getAll() {\n        <span class=\"hljs-keyword\">return<\/span> queryGateway.query(\n            <span class=\"hljs-keyword\">new<\/span> FindAllOrdersQuery(),\n            <span class=\"hljs-keyword\">new<\/span> org.axonframework.messaging.responsetypes\n                .MultipleInstancesResponseType&lt;&gt;(OrderSummary.class));\n    }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>CommandGateway.send()<\/code>&nbsp;und&nbsp;<code>QueryGateway.query()<\/code>&nbsp;geben&nbsp;<code>CompletableFuture<\/code>&nbsp;zur\u00fcck \u2014 der Aufrufer entscheidet selbst \u00fcber synchrones oder asynchrones Verhalten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Axon Server vs. Embedded Event Store<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Axon Server (Docker-Image&nbsp;<code>axoniq\/axonserver:latest<\/code>&nbsp;oder eine spezifische Version wie&nbsp;<code>axoniq\/axonserver:2024.1<\/code>) bietet den vollen Funktionsumfang: Event Store, Command\/Query Routing \u00fcber mehrere Instanzen hinweg und ein Management-Dashboard. F\u00fcr den Einstieg gen\u00fcgt der embedded Store \u2014 die Anwendung l\u00e4uft direkt nach dem Booten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wann lohnt sich Axon?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Event Sourcing mit Axon ist kein Allheilmittel. Es gl\u00e4nzt in Szenarien mit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Audit-Anforderungen<\/strong>\u00a0(Finanzen, Buchhaltung) \u2014 jeder Zustands\u00fcbergang ist ein Event<\/li>\n\n\n\n<li><strong>Komplexen Gesch\u00e4ftslogiken<\/strong>\u00a0(Sagas, Workflows) \u2014 Axon&#8217;s Saga-Support orchestriert langlaufende Prozesse<\/li>\n\n\n\n<li><strong>CQRS<\/strong>\u00a0(Lese-\/Schreib-Trennung) \u2014 wenn deine Queries andere Datenstrukturen brauchen als Commands<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr einfache CRUD-Apps ist klassisches Spring Data JPA die bessere Wahl.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Axon Framework 4.11 macht Event Sourcing und CQRS in Spring Boot zug\u00e4nglich. Mit klaren Annotations (<code>@Aggregate<\/code>,&nbsp;<code>@CommandHandler<\/code>,&nbsp;<code>@EventHandler<\/code>,&nbsp;<code>@QueryHandler<\/code>) entsteht eine nat\u00fcrliche Trennung von Commands, Events und Queries \u2014 wartbar, testbar und auditierbar. Der Wechsel vom In-Memory-Store zu Axon Server ist flie\u00dfend und erfordert keine Code-\u00c4nderungen. Ab Version 4.13 ist JDK 17 erforderlich; Axon Framework 5.x setzt JDK 21 voraus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wer mehr \u00fcber die Historie seiner Daten wissen will: Axon ist daf\u00fcr das richtige Werkzeug.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Die meisten Spring-Boot-Anwendungen speichern nur den aktuellen Zustand ihrer Daten in einer relationalen Datenbank. Reicht das? Nicht immer. Wenn du nachvollziehen musst,&nbsp;wie&nbsp;ein Zustand entstanden ist \u2014 etwa bei Buchungen, Audit-Trails oder regulatorischen Anforderungen \u2014 brauchst du Event Sourcing. Und wenn deine Lese- und Schreibmuster stark voneinander abweichen, hilft CQRS (Command Query Responsibility Segregation). Das Axon [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,5],"tags":[],"class_list":["post-695","post","type-post","status-publish","format-standard","hentry","category-plain_java","category-spring"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=695"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions"}],"predecessor-version":[{"id":696,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/695\/revisions\/696"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}