04-20.eri-test 使用Java和Axon的CQRS-命令模块

Introduction

In this second part of this article series, we will implement the 命令 module, responsible for application state changes。Final code is in Github.

REST API Layer

我们将有效地从外部层开始对应用程序进行编码,并继续进行内部工作。 的产品控制器类将负责向端点公开以请求状态更改。

此类的唯一依赖项是Axon的命令Gateway负责派遣命令对象。 初始结构为:

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    public ProductController(final 命令Gateway commandGateway) {
        this.commandGateway = commandGateway;
    }

    private CommandGateway commandGateway;

    @PostMapping
    public CompletableFuture<String> create(@RequestBody 产品DTO dto) {
        return null;
    }

    @PutMapping
    public CompletableFuture<String> update(@RequestBody ProductDTO dto) {
        return null;
    }
}

哪里ProductDTO类只是一个POJO来映射json请求。

public class ProductDTO {

    private Long id;

    private String name;

    private int quantity;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

Command model

Command该应用程序上的实例将表示状态更改的意图。 应用程序状态的子集将由骨料宾语。 例如,将新产品添加到购物车的意图表示为AddProductCommand类:

public class AddProductCommand {

    public AddProductCommand(
            final Long id,
            final String name,
            final int quantity) {
        this.id = id;
        this.name = name;
        this.quantity = quantity;
    }

    @Target骨料Identifier
    private final Long id;
    private final String name;
    private final int quantity;

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getQuantity() {
        return quantity;
    }
}

哪里Target骨料Identifier批注用于标识哪个实例骨料类型应由此命令处理。

现在从我们的派遣此命令RestController我们只需要实例化它并作为参数传递CommandGateway 发送方法:

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    public ProductController(final CommandGateway commandGateway) {
        this.commandGateway = commandGateway;
    }

    private CommandGateway commandGateway;

    @PostMapping
    public CompletableFuture<String> create(@RequestBody ProductDTO dto) {
        AddProductCommand command = new AddProductCommand(
                dto.getId(),
                dto.getName(),
                dto.getQuantity());
        return commandGateway.send(command);
    }

    // ....
}

骨料

如前所述,骨料类将负责代表部分应用程序状态以及Command为此处理Aggregate

一个例子Aggregate可能:

{
  "id": 1,
  "name": "iPhone",
  "quantity": 2
}

翻译成代码,结果是:

@Aggregate
public class 产品汇总 {

    @AggregateIdentifier
    private Long id;
    private String name;
    private int quantity;

    @命令处理程序
    public ProductAggregate(AddProductCommand cmd) {
        // Verifies state consistency and applies events
    }
}

CommandHandler上的注释建设者意味着AddProductCommand命令用于创建一个新的Aggregate.

此时,您的应用程序状态尚未更改。 命令处理是执行业务逻辑的位置(即:检查数量是否为正值)并可能适用大事记这将导致状态改变。

Event model

首先,我们创建AddProductEvent具有触发我们期望的状态更改所需的属性的类。 在这种情况下,它将与各自的情况非常相似Command模型。

现在我们将改变ProductAggregate 建设者派遣AddProductEvent每当一个AddProductCommand已发送。 此类也可以充当AddProductEvent,执行应用程序状态的更改。

import static org.axonframework.modelling.command.AggregateLifecycle.apply;


@Aggregate
public class ProductAggregate {

    @AggregateIdentifier
    private Long id;
    private String name;
    private int quantity;

    @CommandHandler
    public ProductAggregate(AddProductCommand cmd) {
        apply(new AddProductEvent(cmd.getId(), cmd.getName(), cmd.getQuantity()));
    }

    @EventSourcingHandler
    public void on(AddProductEvent event) {
        this.id = event.getId();
        this.name = event.getName();
        this.quantity = event.getQuantity();
    }
}

到目前为止,我们已经建立了一个检查点,使我们可以测试应用程序并观察发生了什么。 按照终端命令运行指挥部应用:

docker-compose up -d
./gradlew clean assemble
java -jar commandside/build/libs/commandside.jar

现在,我们可以通过添加新产品来测试端点:

curl -X POST http://localhost:8080/products -H 'Content-Type: application/json' -d '{"id": 1, "name": "iPhone", "quantity": 7}'

我们可以在mongo数据库中验证域事件集合中存储了一个新事件:

{
   "_id":"5e0a2924b813b63783e1e092",
   "aggregateIdentifier":"1",
   "type":"ProductAggregate",
   "sequenceNumber":"0",
   "serializedPayload":"<com.example.project.command.addproduct.AddProductEvent><id>1</id><name>iPhone</name><quantity>7</quantity></com.example.project.command.addproduct.AddProductEvent>",
   "timestamp":"2019-12-30T16:43:16.851862731Z",
   "payloadType":"com.example.project.command.addproduct.AddProductEvent",
   "payloadRevision":null,
   "serializedMetaData":"<meta-data><entry><string>traceId</string><string>e62c8e0d-7505-4e99-ab7e-84b4619ee159</string></entry><entry><string>correlationId</string><string>e62c8e0d-7505-4e99-ab7e-84b4619ee159</string></entry></meta-data>",
   "eventIdentifier":"6eef19d8-b22a-4be6-9fd9-7681a31580b8"
}

对于通过POST请求添加的每个新产品,我们将在域事件从现在开始。 那就是我们应用程序发生的历史。

Aggregate persistence

除了单个事件的持久性,我们还希望在每次更改时都保持聚合(状态存储的聚合)。 为此,我们只需要添加JPA批注以将我们的聚合类变成一个实体:

@Aggregate
@Entity // This class can now be mapped to a table 
public class ProductAggregate {

    @AggregateIdentifier
    @Id // Defines the primary key
    private Long id;

    @Column // Map to a column with same name
    private String name;

    @Column // Map to a column with same name
    private int quantity;

    @CommandHandler
    public ProductAggregate(AddProductCommand cmd) {
        apply(new AddProductEvent(cmd.getId(), cmd.getName(), cmd.getQuantity()));
    }

    @EventSourcingHandler
    public void on(AddProductEvent event) {
        this.id = event.getId();
        this.name = event.getName();
        this.quantity = event.getQuantity();
    }
}

现在,使用上述端点在购物车中添加新产品,并检查product_table在您的Postgres数据库上,以验证存储的新条目是否与所需的汇总匹配。 您的Mongo数据库还应该包含新事件。

Conclusion

现在,在Mongo数据库上,我们具有所有事件的历史记录,可用来了解应用程序如何达到其当前状态。 另一方面,Postgres数据库具有我们可以用来显示给最终用户的数据,例如在结帐屏幕上。

我们可以一路回头,自己实现CQRS和事件源,但幸运的是,我们可以使用Axon的几个注释来达到相同的结果。

from: https://dev.to//fabiothiroki/cqrs-using-java-and-axon-command-module-57h5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值