Axon 参考指南
  • 介绍
  • 架构概览
    • DDD & CQRS 概念
    • 事件溯源
    • 事件驱动的微服务
  • Axon Server
  • 发行说明
    • Axon Framework
      • Major Releases
      • Minor Releases
    • Axon Server
      • Major Releases
      • Minor Releases Standard Edition
      • Minor Releases Enterprise Edition
    • Axon Framework Extensions
      • AMQP
        • Major Releases
      • CDI
        • Major Releases
      • JGroups
        • Major Releases
      • Kafka
        • Major Releases
        • Minor Releases
      • Kotlin
        • Experimental Releases
      • Mongo
        • Major Releases
        • Minor Releases
      • Reactor
        • Major Releases
        • Minor Releases
      • Spring Cloud
        • Major Releases
        • Minor Releases
      • Tracing
        • Major Releases
        • Minor Releases
  • Getting Started
    • 快速开始
  • Axon Framework
    • 介绍
    • 消息传递概念
      • 消息剖析
      • 消息关联
      • 消息拦截
      • 支持带注解的处理程序
      • 异常处理
      • 工作单元
    • 命令
      • 建模
        • 聚合
        • 多实体聚合
        • 聚合状态存储
        • 从另一个聚合创建聚合
        • 聚合多态性
        • 解决冲突
      • 命令调度器
      • 命令处理程序
      • 基础设施
      • 配置
    • 事件
      • 事件调度器
      • 事件处理程序
      • 事件处理器
        • 订阅事件处理器
        • 流式事件处理器
      • 事件总线和事件存储
      • 事件版本控制
    • 查询
      • 查询处理
      • 查询调度器
      • 查询处理程序
      • 实现
      • 配置
    • 长时处理过程(Sagas)
      • 实现
      • 关联
      • 基础设施
    • Deadlines
      • Deadline Managers
      • Event Schedulers
    • 测试
      • 命令 / 事件
      • 长时处理过程(Sagas)
    • 序列化
    • 调整
      • 事件快照
      • 事件处理
      • 命令处理
    • 监控和指标
    • Spring Boot 集成
    • 模块
  • Axon Server
    • 介绍
    • 安装
      • 本地安装
        • Axon Server SE
        • Axon Server EE
      • Docker / K8s
        • Axon Server SE
        • Axon Server EE
    • 管理
      • 配置
        • System Properties
        • Command Line Interface
        • REST API
        • GRPC API
      • Monitoring
        • Actuator Endpoints
        • gRPC Metrics
        • Heartbeat Monitoring
      • Clusters
      • Replication Groups
      • Multi-Context
      • Tagging
      • Backup and Messaging-only Nodes
      • Backups
      • Recovery
      • Plugins
      • Error Codes
    • 安全
      • SSL
      • 访问控制
      • 访问控制 - 标准版
      • 访问控制 - 企业版
      • 访问控制 - 客户端应用程序
      • 访问控制 - 命令行
      • 访问控制 - REST API
      • 访问控制 - LDAP
      • 访问控制 - OAuth 2.0
    • 性能
      • 事件段
      • 流量控制
    • 迁移
      • Standard to Enterprise Edition
      • Non-Axon Server to Axon Server
  • Extensions
    • Spring AMQP
    • JGroups
    • Kafka
    • Kotlin
    • Mongo
    • Reactor
      • Reactor Gateways
    • Spring Cloud
    • Tracing
  • Appendices
    • A. RDBMS Tuning
    • B. Message Handler Tuning
      • 参数解析器
      • 处理程序增强
    • C. 元数据注解
    • D. 标识符生成
    • E. Axon Server Query Language
由 GitBook 提供支持
在本页
  1. Axon Framework
  2. 命令
  3. 建模

聚合状态存储

State Stored Aggregates

上一页多实体聚合下一页从另一个聚合创建聚合

最后更新于2年前

In the main page we have seen how to create an Aggregate backed by Event Sourcing. In other words, the storage method for an Event Sourced Aggregate is by replaying the events which constitute the changes on the Aggregate.

An Aggregate can however be stored as-is too. When doing so, the Repository used to save and load the Aggregate, is the GenericJpaRepository. The structure of a state-stored Aggregate is a little different from an Event Sourced Aggregate:

import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventhandling.EventHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.modelling.command.AggregateMember;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

@Entity // 1.
public class GiftCard {

    @Id // 2.
    @AggregateIdentifier
    private String id;

    // 3.
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "giftCardId")
    @AggregateMember
    private List<GiftCardTransaction> transactions = new ArrayList<>();

    private int remainingValue;

    @CommandHandler  // 4.
    public GiftCard(IssueCardCommand cmd) {
        if (cmd.getAmount() <= 0) {
            throw new IllegalArgumentException("amount <= 0");
        }
        id = cmd.getCardId();
        remainingValue = cmd.getAmount();

         // 5.
        apply(new CardIssuedEvent(cmd.getCardId(), cmd.getAmount()));
    }

    @CommandHandler
    public void handle(RedeemCardCommand cmd) {
         // 6.
        if (cmd.getAmount() <= 0) {
            throw new IllegalArgumentException("amount <= 0");
        }
        if (cmd.getAmount() > remainingValue) {
            throw new IllegalStateException("amount > remaining value");
        }
        if (transactions.stream().map(GiftCardTransaction::getTransactionId).anyMatch(cmd.getTransactionId()::equals)) {
            throw new IllegalStateException("TransactionId must be unique");
        }

         // 7.
        remainingValue -= cmd.getAmount();
        transactions.add(new GiftCardTransaction(id, cmd.getTransactionId(), cmd.getAmount()));

        apply(new CardRedeemedEvent(id, cmd.getTransactionId(), cmd.getAmount()));
    }

    @EventHandler  // 8.
    protected void on(CardReimbursedEvent event) {
        this.remainingValue += event.getAmount();
    }

    protected GiftCard() { }  // 9.
}

The above exert shows an state stored Aggregate from a 'Gift Card Service'. The numbered comments in the snippet point out Axon specifics which are explained here:

  1. As the Aggregate is stored in a JPA repository, it is required to annotated the class with @Entity.

  2. An Aggregate Root must declare a field that contains the Aggregate Identifier.

    This identifier must be initialized at the latest when the first event is published.

    This identifier field must be annotated by the @AggregateIdentifier annotation.

    When using JPA to store the Aggregate, Axon knows to use the @Id annotation provided by JPA.

    Since the Aggregate is an entity, the @Id annotation is a hard requirement.

  3. Since the Aggregate is stored as is, correct mapping of the entities should be taking into account.

  4. A @CommandHandler annotated constructor, or differently put the 'command handling constructor'.

    This annotation tells the framework that the given constructor is capable of handling the IssueCardCommand.

  5. The static AggregateLifecycle#apply(Object...) may be used to publish an Event Message.

    Upon calling this function the provided Objects will be published as EventMessages within the scope of the Aggregate they are applied in.

  6. The Command Handling method will first decide whether the incoming Command is valid to handle at this point.

  7. After the business logic has been validated, the state of the Aggregate may be adjusted

  8. Entities within an Aggregate can listen to the events the Aggregate publishes, by defining an @EventHandler annotated method.

    These methods will be invoked when an Event Message is published prior to being handled by any external handlers.

  9. A no-arg constructor, which is required by JPA.

    Failure to provide this constructor will result in an exception when loading the Aggregate.

Adjusting state in Command Handlers

This Aggregate has several ''.

Differently from , State-Stored Aggregates can pair the decision making logic and state changes in a Command Handler. There are no consequences for State-Stored Aggregates in following this paradigm as there are no Event Sourcing Handlers which drive it's state.

Aggregate
Aggregate Members
Event Sourced Aggregates